Single-Stage Solvex Development Cortix Tech 30Sep2025
1. Use-Case 04: TBP-Diluent-H\(_2\)O-HNO\(_3\)-UO\(_2^{2+}\)-Air Mixing#
Developer: Valmor F. de Almeida, PhD
Cortix Tech, Lowell, MA 01854, USA
Revision date: 02Dec25
1.1. Objectives#
Develop a usecase scenario for water, nitric acid, and uranyl extraction by TBP with a vapor phase.
Test implementation and present results.
Use AI assistants (limited) to help with information and reporting.
AI requests below may need to be executed multiple times if the result is not satisfactory or incorrect.
'''AI assistance options'''
# Set all to False if you do not have access to OpenAI API and/or AI codes below
cortix_ai = True
stage_ai = True
'''Generate proprietary knowledge database?'''
db_save = False # set this to false if going public (online) with this notebook
'''Other helpers'''
fig_count = 0
tbl_count = 0
markdown_display = True # if False code cell output is type: stream, else: markdown. Use True for in-house conversion to .md
1.2. System#
Single stage mixing of TBP with inert diluent, HNO3 solution with uranyl, and vapor.
'''Setup the base system'''
from cortix import Cortix
from cortix import Network
from cortix import Units as unit
from cortix import ReactionMechanism
from cortix import Quantity
system = Cortix(use_mpi=False, splash=True) # System top level
system_net = system.network = Network() # Network
if cortix_ai:
from cortix import CortixAI
cortix_ai = CortixAI(llm_model='gpt-5-mini', llm_cleverness=0.8)
cortix_ai.markdown_header_level = '<h3>'
cortix_ai.n_chunks = 8
if cortix_ai:
cortix_ai.explain(markdown_display=markdown_display, save_supporting_info=db_save)
[14410] 2025-12-02 13:54:50,255 - cortix - INFO - Created Cortix object
_____________________________________________________________________________
L A U N C H I N G
_____________________________________________________________________________
... s . (TAAG Fraktur)
xH88"`~ .x8X :8 @88>
:8888 .f"8888Hf u. .u . .88 %8P uL ..
:8888> X8L ^""` ...ue888b .d88B :@8c :888ooo . .@88b @88R
X8888 X888h 888R Y888r ="8888f8888r -*8888888 .@88u ""Y888k/"*P
88888 !88888. 888R I888> 4888>"88" 8888 888E` Y888L
88888 %88888 888R I888> 4888> " 8888 888E 8888
88888 `> `8888> 888R I888> 4888> 8888 888E `888N
`8888L % ?888 ! u8888cJ888 .d888L .+ .8888Lu= 888E .u./"888&
`8888 `-*"" / "*888*P" ^"8888*" ^%888* 888& d888" Y888*"
"888. :" "Y" "Y" "Y" R888" ` "Y Y"
`""***~"` ""
https://cortix.org
_____________________________________________________________________________
Cortix AI assistant: working on explanation...
Overview
This snippet initializes a Cortix simulation “system” (no MPI, with a splash banner), creates an empty network, imports several Cortix classes, and conditionally configures and invokes a CortixAI object when a truthy cortix_ai is present.
Key intent: prepare core runtime objects (Cortix system and Network) and optionally set up an AI helper object with a couple of attributes, then call its explain method.
Imports
from cortix import Cortix, Network, Units as unit, ReactionMechanism, Quantity: bring core Cortix classes into the namespace; Units is aliased to unit.
The CortixAI class is imported inside a conditional block only if cortix_ai evaluates to True at runtime.
Line-by-line explanation
The top comment ‘’’Setup the base system’’’ is a module-level string (used here as a comment).
system = Cortix(use_mpi=False, splash=True): instantiate the top-level Cortix object with MPI disabled and the splash/banner enabled.
system_net = system.network = Network(): create a new Network instance, assign it to system.network, and also assign the same object to the local name system_net (two references to the same Network).
if cortix_ai:: check the truthiness of the name cortix_ai; if True, execute the indented block.
Inside that block: from cortix import CortixAI imports the AI helper class on demand.
cortix_ai = CortixAI(llm_model=’gpt-5-mini’, llm_cleverness=0.8): construct a CortixAI instance with the given model and cleverness and assign it to the name cortix_ai (overwriting its prior value).
cortix_ai.markdown_header_level = ‘
’: set an attribute on the CortixAI instance to the string ‘
’.
cortix_ai.n_chunks = 8: set the instance attribute n_chunks to 8.
if cortix_ai:: a second conditional that checks cortix_ai again; if truthy it executes the call below.
cortix_ai.explain(markdown_display=markdown_display, save_supporting_info=db_save): invoke the explain method on the CortixAI instance, passing two keyword arguments (markdown_display and db_save are names resolved at runtime).
Objects and names created or referenced
system: Cortix instance (configured with use_mpi=False, splash=True).
system.network and system_net: references to the same newly created Network instance.
unit: alias for Units (imported but not used in the shown snippet).
ReactionMechanism, Quantity: imported class names present but unused in the snippet.
cortix_ai: tested for truthiness; if truthy, redefined as a CortixAI instance with attributes markdown_header_level and n_chunks set; later its explain method is called.
markdown_display, db_save: passed into cortix_ai.explain; they must be defined in the runtime environment for the call to succeed.
Control-flow and runtime considerations
The conditional blocks depend on the value of cortix_ai at runtime: the first if both checks and then reassigns cortix_ai to a CortixAI instance if entered; the second if invokes explain only when cortix_ai is truthy (either because it was truthy before or because it was set in the prior block).
Several names used in the explain call (markdown_display, db_save) are not defined in the snippet and are resolved at runtime.
Summary
The code sets up a Cortix system and an associated empty Network, imports utility classes, and—if a truthy cortix_ai is present—creates/configures a CortixAI instance and calls its explain method with two runtime-provided arguments.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4120
'''FYI LLM models info'''
if cortix_ai:
print(cortix_ai.llm_names_info)
{'gpt-5': 'Full reasoning-intensive tasks', 'gpt-5-mini': 'Balance of speed and capability', 'gpt-5-nano': 'Speed and cost efficiency', 'gpt-4o-mini': 'Fastest at advanced reasoning', 'gpt-4o': 'Great for most tasks', 'gpt-4.1': 'Great for quick coding and analysis', 'gpt-4.1-mini': 'Faster than 4.1 for everyday tasks'}
1.2.1. Stage#
Instantiate a single stage to model and simulate the reactive mixing process.
'''Import Stage'''
from solvex.stage import Stage
'''Create and configure a StageAI object'''
if stage_ai:
from stage_ai import StageAI
stage_ai = StageAI(llm_model='gpt-5-mini', llm_cleverness=0.8)
stage_ai.markdown_header_level = '<h4>'
if stage_ai:
stage_ai.help('stage', markdown_display=markdown_display, save_supporting_info=db_save)
Stage AI assistant: working on help...
Stage Description
Overview
This module implements a single solvent-extraction contactor (a stage) that couples three co-existing domains: aqueous phase, organic phase, and a vapor (cover) phase. Yes — phases are involved: aqueous phase, organic phase, and vapor phase.
Information about a stage module is needed: stage
The class Stage inherits from cortix.Module and uses a ReactionMechanism to define chemistry. It integrates phasic mass-concentration state variables in time using an ODE integrator (scipy.integrate.odeint) and enforces mass balances, handles inflow/outflow streams and maintains time histories of phase compositions.
Original Stage schematics
Aqueous Organic
External Product
Feed
| ^
| |
| |
V |
|---------------------|
Vapor/Org inter-stage| | Vapor/Aqu inter-stage
outflow <------------| |-----------> outflow
| |
Organic inter-stage | S O L V E N T | Aqueous inter-stage
outflow <------------| E X T R A C T I O N |-----------> outflow
| |
| |
Vapor/Aqu inter-stage| S T A G E | Vapor/Org inter-stage
inflow ------------>| |<----------- inflow
| |
Aqueous inter-stage | | Organic inter-stage
inflow ------------->| |<----------- inflow
|---------------------|
| ^
| |
| |
V |
Aqueous Organic
Product External
Feed
High-level code structure
Key imports: numpy, scipy.integrate.odeint, pandas, cortix.Module, cortix.common.phase_new.Phase, cortix.Quantity, ReactionMechanism and Units utilities.
Primary class: Stage(Module)
Core data members (representative):
port_names_expected: a list of expected port names (inter-stage and feed/product ports).
mix_vol, vol_flowrate_org, vol_flowrate_aqu, vol_flowrate_vap_org, vol_flowrate_vap_aqu
temperature, flow_residence_time_* and average
rxn_mech (ReactionMechanism instance)
phase containers: aqueous_phase, organic_phase, vapor_phase (Phase instances)
inflow parameter containers: inflow_aqueous_phase, inflow_organic_phase, inflow_vapor_aqueous_phase, inflow_vapor_organic_phase
numerical controls: initial_time, end_time, time_step, __ode_params, monitoring flags, tolerances
The module distinguishes state containers (phases) from inflow parameter containers (inflow_*). Phase containers store time-stamped concentration histories; inflow containers serve as parameters for mass inflow rates.
Important method signatures (no full source code)
Constructor:
def init(self, mix_vol, vol_flowrates, temperature)
Reaction mechanism handling:
def add_reaction_mechanism(self, rxn_mech=None)
Simulation and stepping:
def run(self, *args)
def __call_ports(self, time)
def __step(self, time=0.0)
State assembly and conversion:
def __get_state_vector(self, time=None, cc_name=’mass_cc’)
def __convert_mass_cc_to_molar_cc(self, u_vec)
Mass balance RHS (ODE rhs):
def __mbal_rhs_func(self, u_vec, time, params)
This is the ODE right-hand-side function requested by odeint. See the signature above.
Outflow/inflow bookkeeping:
def __update_mass_inflow_rates(self)
def __update_outflow_mass_rates(self, u_vec)
State update and diagnostics:
def __update_state_variables(self, u_vec, time)
def __check_mass_conservation(self, u_vec, write=False)
def __total_mass_generation_rate_density_residual(self, u_vec)
Utilities for users (time histories and instantaneous vectors):
def r_vec(self, time=None)
def g_vec(self, time=None)
def rxn_efficiencies(self, time=None)
Several history-returning functions (r_vec_history, g_vec_history, mass_density_history, etc.)
Ports: the module expects inter-stage and feed/product ports (existence of ports). Example one-line python list of port names (as used in the Stage source):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
What the step method does (description + snippet)
Purpose: __step advances the Stage state by exactly one time_step. It:
Assembles the current state vector u_vec_0 from phase containers.
Integrates the mass-balance ODE system in the interval [time, time+time_step] using odeint, with __mbal_rhs_func as the RHS function.
Validates integration success and performs mass balance checks.
Optionally prints monitored mass flow information.
Updates phase containers with the new state at the advanced time.
Returns the updated time.
Representative source snippet of __step (copied as-is from the Stage module):
def __step(self, time=0.0):
“””Stepping Stage in time.
“””
u_vec_0 = self.__get_state_vector(time)
t_interval = np.linspace(time, time+self.time_step, num=2)
(u_vec_hist, info_dict) = odeint(self.__mbal_rhs_func,
u_vec_0, t_interval,
args=(self.__ode_params,),
#rtol=1e-6, atol=1e-6, mxstep=1000,
full_output=True )
assert info_dict['message'] == 'Integration successful.','%r <= time <= %r; %r'%\
(time,time+self.time_step,info_dict['message'])
u_vec = u_vec_hist[1,:] # solution vector at final time step
time += self.time_step
# Verify global mass conservation; use u_vec directly not the phase containers
if self.monitor_mass_conservation_residual or time >= self.end_time:
self.__check_mass_conservation(u_vec, write=True)
else:
self.__check_mass_conservation(u_vec)
# Monitor total mass flowrates ; use u_vec directly not the phase containers
if self.monitor_mass_flowrates or time >= self.end_time:
(total_mass_inflow_rate, total_mass_outflow_rate) = \
self.__compute_total_mass_flowrates(u_vec)
print('total mass inflow rate [g/min] = %2.3e'%\
(total_mass_inflow_rate/(unit.gram/unit.min))
)
print('total mass outflow rate [g/min] = %2.3e'%\
(total_mass_outflow_rate/(unit.gram/unit.min))
)
print('\t net total mass flow rate [g/min] = %2.3e'%\
((total_mass_outflow_rate - total_mass_inflow_rate)/(unit.gram/unit.min))
)
# Now update the phase containers
self.__update_state_variables(u_vec, time)
return time
The mass-balance RHS function (what it is and its signature)
Signature (as implemented):
def __mbal_rhs_func(self, u_vec, time, params)
Purpose and behavior:
This method computes the time derivative dt_u for the ordered state vector u_vec (mass concentrations per phasic volume, ordered by species global IDs).
Major tasks inside the RHS function:
Enforce non-negativity by clamping negative concentrations to zero.
Compute state-derived outflow mass rates from u_vec via __update_outflow_mass_rates(u_vec).
Convert phasic mass concentrations to molar concentrations (per phasic volume) via __convert_mass_cc_to_molar_cc(u_vec).
Call the reaction mechanism to get g_vec (species mole generation rates per mixing-volume), using rxn_mech.g_vec(…).
For each phase (aqueous, organic, vapor) compute dt for each species using the local phase volume fraction, mixing volume, (inflow - outflow)/mixing_vol and the chemistry term g_vec * molar_mass, consistent with the phase formation. Vapor handling blends vapor fractions from both carrier phases.
The returned dt_u array matches the ordering of u_vec and is used by odeint to integrate forwards.
Ports and their interaction with time stepping
Ports exist to connect this Stage to other stages/modules (inter-stage streams, feed and product). The Stage maintains port_names_expected and uses send/recv to communicate during the run.
__call_ports(time) is invoked after each step and manages:
Receiving inflow messages (e.g., aquatic inflow) from connected ports and assigning the provided mass inflow rates into local mass inflow arrays.
Sending or responding to outflow/product requests when connected.
It relies on self.get_port(name).connected_port to decide if a port is active and uses self.send / self.recv for data exchange.
Typical interaction order in run():
__update_mass_inflow_rates() called before time loop (to initialize inflow rates).
For each time loop iteration:
__perturb_mass_inflow_rates() optionally modifies inflows.
__step(time) advances the state.
__call_ports(time) performs port communications (recv inflows, send outflow/product messages).
Example (single-line) list of port names referenced by the Stage (see code):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
Brief example usage snippet
Short illustrative example (how a user would set up and run a Stage):
from cortix import ReactionMechanism
create a stage (mixing volume, vol_flowrates [org, aqu, (vap_org, vap_aqu)], temperature)
stg = Stage(200unit.mL, [240unit.mL/unit.min, 360unit.mL/unit.min, (5unit.mL/unit.min, 0.0)], 313.15)
define and add reaction mechanism
mechanism = [‘H2O(a) <-> H2O(v) ; tau=2e-2 ; info=H2O vaporization’]
rxn_mech = ReactionMechanism(mechanism=mechanism, order_species=True)
stg.add_reaction_mechanism(rxn_mech)
run() expects arguments for logger/connection context; typically called by a simulation driver
stg.run(driver_args)
Note: connecting ports to other modules (to provide inflow mass rates and receive outflow queries) is done through Module port connection APIs (send/recv). The Stage expects to receive mass rates at inflow ports and will respond at outflow/product ports when connected.
Summary
Stage provides a time-integrated, phase-resolved solvent-extraction model with three phases: aqueous, organic, and vapor.
State variables are species mass concentrations per phasic volume; inflows are treated as parameters and converted to mass inflow rate vectors before running.
Time advancement uses odeint with an explicit __mbal_rhs_func that accounts for inflow – outflow terms and chemistry (via rxn_mech.g_vec).
Interaction with other modules is via ports; __call_ports manages port communications after each step.
Key developer-facing hooks: add_reaction_mechanism(), run(), __step(), __mbal_rhs_func(…), r_vec(), g_vec(), and phase history readers.
This module implements a single solvent-extraction contactor (a stage) that couples three co-existing domains: aqueous phase, organic phase, and a vapor (cover) phase. Yes — phases are involved: aqueous phase, organic phase, and vapor phase.
Information about a stage module is needed: stage
The class Stage inherits from cortix.Module and uses a ReactionMechanism to define chemistry. It integrates phasic mass-concentration state variables in time using an ODE integrator (scipy.integrate.odeint) and enforces mass balances, handles inflow/outflow streams and maintains time histories of phase compositions.
Original Stage schematics
Aqueous Organic
External Product
Feed
| ^
| |
| |
V |
|---------------------|
Vapor/Org inter-stage| | Vapor/Aqu inter-stage
outflow <------------| |-----------> outflow
| |
Organic inter-stage | S O L V E N T | Aqueous inter-stage
outflow <------------| E X T R A C T I O N |-----------> outflow
| |
| |
Vapor/Aqu inter-stage| S T A G E | Vapor/Org inter-stage
inflow ------------>| |<----------- inflow
| |
Aqueous inter-stage | | Organic inter-stage
inflow ------------->| |<----------- inflow
|---------------------|
| ^
| |
| |
V |
Aqueous Organic
Product External
Feed
High-level code structure
Key imports: numpy, scipy.integrate.odeint, pandas, cortix.Module, cortix.common.phase_new.Phase, cortix.Quantity, ReactionMechanism and Units utilities.
Primary class: Stage(Module)
Core data members (representative):
port_names_expected: a list of expected port names (inter-stage and feed/product ports).
mix_vol, vol_flowrate_org, vol_flowrate_aqu, vol_flowrate_vap_org, vol_flowrate_vap_aqu
temperature, flow_residence_time_* and average
rxn_mech (ReactionMechanism instance)
phase containers: aqueous_phase, organic_phase, vapor_phase (Phase instances)
inflow parameter containers: inflow_aqueous_phase, inflow_organic_phase, inflow_vapor_aqueous_phase, inflow_vapor_organic_phase
numerical controls: initial_time, end_time, time_step, __ode_params, monitoring flags, tolerances
The module distinguishes state containers (phases) from inflow parameter containers (inflow_*). Phase containers store time-stamped concentration histories; inflow containers serve as parameters for mass inflow rates.
Important method signatures (no full source code)
Constructor:
def init(self, mix_vol, vol_flowrates, temperature)
Reaction mechanism handling:
def add_reaction_mechanism(self, rxn_mech=None)
Simulation and stepping:
def run(self, *args)
def __call_ports(self, time)
def __step(self, time=0.0)
State assembly and conversion:
def __get_state_vector(self, time=None, cc_name=’mass_cc’)
def __convert_mass_cc_to_molar_cc(self, u_vec)
Mass balance RHS (ODE rhs):
def __mbal_rhs_func(self, u_vec, time, params)
This is the ODE right-hand-side function requested by odeint. See the signature above.
Outflow/inflow bookkeeping:
def __update_mass_inflow_rates(self)
def __update_outflow_mass_rates(self, u_vec)
State update and diagnostics:
def __update_state_variables(self, u_vec, time)
def __check_mass_conservation(self, u_vec, write=False)
def __total_mass_generation_rate_density_residual(self, u_vec)
Utilities for users (time histories and instantaneous vectors):
def r_vec(self, time=None)
def g_vec(self, time=None)
def rxn_efficiencies(self, time=None)
Several history-returning functions (r_vec_history, g_vec_history, mass_density_history, etc.)
Ports: the module expects inter-stage and feed/product ports (existence of ports). Example one-line python list of port names (as used in the Stage source):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
What the step method does (description + snippet)
Purpose: __step advances the Stage state by exactly one time_step. It:
Assembles the current state vector u_vec_0 from phase containers.
Integrates the mass-balance ODE system in the interval [time, time+time_step] using odeint, with __mbal_rhs_func as the RHS function.
Validates integration success and performs mass balance checks.
Optionally prints monitored mass flow information.
Updates phase containers with the new state at the advanced time.
Returns the updated time.
Representative source snippet of __step (copied as-is from the Stage module):
def __step(self, time=0.0):
“””Stepping Stage in time.
“””
u_vec_0 = self.__get_state_vector(time)
t_interval = np.linspace(time, time+self.time_step, num=2)
(u_vec_hist, info_dict) = odeint(self.__mbal_rhs_func,
u_vec_0, t_interval,
args=(self.__ode_params,),
#rtol=1e-6, atol=1e-6, mxstep=1000,
full_output=True )
assert info_dict['message'] == 'Integration successful.','%r <= time <= %r; %r'%\
(time,time+self.time_step,info_dict['message'])
u_vec = u_vec_hist[1,:] # solution vector at final time step
time += self.time_step
# Verify global mass conservation; use u_vec directly not the phase containers
if self.monitor_mass_conservation_residual or time >= self.end_time:
self.__check_mass_conservation(u_vec, write=True)
else:
self.__check_mass_conservation(u_vec)
# Monitor total mass flowrates ; use u_vec directly not the phase containers
if self.monitor_mass_flowrates or time >= self.end_time:
(total_mass_inflow_rate, total_mass_outflow_rate) = \
self.__compute_total_mass_flowrates(u_vec)
print('total mass inflow rate [g/min] = %2.3e'%\
(total_mass_inflow_rate/(unit.gram/unit.min))
)
print('total mass outflow rate [g/min] = %2.3e'%\
(total_mass_outflow_rate/(unit.gram/unit.min))
)
print('\t net total mass flow rate [g/min] = %2.3e'%\
((total_mass_outflow_rate - total_mass_inflow_rate)/(unit.gram/unit.min))
)
# Now update the phase containers
self.__update_state_variables(u_vec, time)
return time
The mass-balance RHS function (what it is and its signature)
Signature (as implemented):
def __mbal_rhs_func(self, u_vec, time, params)
Purpose and behavior:
This method computes the time derivative dt_u for the ordered state vector u_vec (mass concentrations per phasic volume, ordered by species global IDs).
Major tasks inside the RHS function:
Enforce non-negativity by clamping negative concentrations to zero.
Compute state-derived outflow mass rates from u_vec via __update_outflow_mass_rates(u_vec).
Convert phasic mass concentrations to molar concentrations (per phasic volume) via __convert_mass_cc_to_molar_cc(u_vec).
Call the reaction mechanism to get g_vec (species mole generation rates per mixing-volume), using rxn_mech.g_vec(…).
For each phase (aqueous, organic, vapor) compute dt for each species using the local phase volume fraction, mixing volume, (inflow - outflow)/mixing_vol and the chemistry term g_vec * molar_mass, consistent with the phase formation. Vapor handling blends vapor fractions from both carrier phases.
The returned dt_u array matches the ordering of u_vec and is used by odeint to integrate forwards.
Ports and their interaction with time stepping
Ports exist to connect this Stage to other stages/modules (inter-stage streams, feed and product). The Stage maintains port_names_expected and uses send/recv to communicate during the run.
__call_ports(time) is invoked after each step and manages:
Receiving inflow messages (e.g., aquatic inflow) from connected ports and assigning the provided mass inflow rates into local mass inflow arrays.
Sending or responding to outflow/product requests when connected.
It relies on self.get_port(name).connected_port to decide if a port is active and uses self.send / self.recv for data exchange.
Typical interaction order in run():
__update_mass_inflow_rates() called before time loop (to initialize inflow rates).
For each time loop iteration:
__perturb_mass_inflow_rates() optionally modifies inflows.
__step(time) advances the state.
__call_ports(time) performs port communications (recv inflows, send outflow/product messages).
Example (single-line) list of port names referenced by the Stage (see code):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
Brief example usage snippet
Short illustrative example (how a user would set up and run a Stage):
from cortix import ReactionMechanism
create a stage (mixing volume, vol_flowrates [org, aqu, (vap_org, vap_aqu)], temperature)
stg = Stage(200unit.mL, [240unit.mL/unit.min, 360unit.mL/unit.min, (5unit.mL/unit.min, 0.0)], 313.15)
define and add reaction mechanism
mechanism = [‘H2O(a) <-> H2O(v) ; tau=2e-2 ; info=H2O vaporization’]
rxn_mech = ReactionMechanism(mechanism=mechanism, order_species=True)
stg.add_reaction_mechanism(rxn_mech)
run() expects arguments for logger/connection context; typically called by a simulation driver
stg.run(driver_args)
Note: connecting ports to other modules (to provide inflow mass rates and receive outflow queries) is done through Module port connection APIs (send/recv). The Stage expects to receive mass rates at inflow ports and will respond at outflow/product ports when connected.
Summary
Stage provides a time-integrated, phase-resolved solvent-extraction model with three phases: aqueous, organic, and vapor.
State variables are species mass concentrations per phasic volume; inflows are treated as parameters and converted to mass inflow rate vectors before running.
Time advancement uses odeint with an explicit __mbal_rhs_func that accounts for inflow – outflow terms and chemistry (via rxn_mech.g_vec).
Interaction with other modules is via ports; __call_ports manages port communications after each step.
Key developer-facing hooks: add_reaction_mechanism(), run(), __step(), __mbal_rhs_func(…), r_vec(), g_vec(), and phase history readers.
Aqueous Organic
External Product
Feed
| ^
| |
| |
V |
|---------------------|
Vapor/Org inter-stage| | Vapor/Aqu inter-stage
outflow <------------| |-----------> outflow
| |
Organic inter-stage | S O L V E N T | Aqueous inter-stage
outflow <------------| E X T R A C T I O N |-----------> outflow
| |
| |
Vapor/Aqu inter-stage| S T A G E | Vapor/Org inter-stage
inflow ------------>| |<----------- inflow
| |
Aqueous inter-stage | | Organic inter-stage
inflow ------------->| |<----------- inflow
|---------------------|
| ^
| |
| |
V |
Aqueous Organic
Product External
Feed
Key imports: numpy, scipy.integrate.odeint, pandas, cortix.Module, cortix.common.phase_new.Phase, cortix.Quantity, ReactionMechanism and Units utilities.
Primary class: Stage(Module)
Core data members (representative):
port_names_expected: a list of expected port names (inter-stage and feed/product ports).
mix_vol, vol_flowrate_org, vol_flowrate_aqu, vol_flowrate_vap_org, vol_flowrate_vap_aqu
temperature, flow_residence_time_* and average
rxn_mech (ReactionMechanism instance)
phase containers: aqueous_phase, organic_phase, vapor_phase (Phase instances)
inflow parameter containers: inflow_aqueous_phase, inflow_organic_phase, inflow_vapor_aqueous_phase, inflow_vapor_organic_phase
numerical controls: initial_time, end_time, time_step, __ode_params, monitoring flags, tolerances
The module distinguishes state containers (phases) from inflow parameter containers (inflow_*). Phase containers store time-stamped concentration histories; inflow containers serve as parameters for mass inflow rates.
Important method signatures (no full source code)
Constructor:
def init(self, mix_vol, vol_flowrates, temperature)
Reaction mechanism handling:
def add_reaction_mechanism(self, rxn_mech=None)
Simulation and stepping:
def run(self, *args)
def __call_ports(self, time)
def __step(self, time=0.0)
State assembly and conversion:
def __get_state_vector(self, time=None, cc_name=’mass_cc’)
def __convert_mass_cc_to_molar_cc(self, u_vec)
Mass balance RHS (ODE rhs):
def __mbal_rhs_func(self, u_vec, time, params)
This is the ODE right-hand-side function requested by odeint. See the signature above.
Outflow/inflow bookkeeping:
def __update_mass_inflow_rates(self)
def __update_outflow_mass_rates(self, u_vec)
State update and diagnostics:
def __update_state_variables(self, u_vec, time)
def __check_mass_conservation(self, u_vec, write=False)
def __total_mass_generation_rate_density_residual(self, u_vec)
Utilities for users (time histories and instantaneous vectors):
def r_vec(self, time=None)
def g_vec(self, time=None)
def rxn_efficiencies(self, time=None)
Several history-returning functions (r_vec_history, g_vec_history, mass_density_history, etc.)
Ports: the module expects inter-stage and feed/product ports (existence of ports). Example one-line python list of port names (as used in the Stage source):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
What the step method does (description + snippet)
Purpose: __step advances the Stage state by exactly one time_step. It:
Assembles the current state vector u_vec_0 from phase containers.
Integrates the mass-balance ODE system in the interval [time, time+time_step] using odeint, with __mbal_rhs_func as the RHS function.
Validates integration success and performs mass balance checks.
Optionally prints monitored mass flow information.
Updates phase containers with the new state at the advanced time.
Returns the updated time.
Representative source snippet of __step (copied as-is from the Stage module):
def __step(self, time=0.0):
“””Stepping Stage in time.
“””
u_vec_0 = self.__get_state_vector(time)
t_interval = np.linspace(time, time+self.time_step, num=2)
(u_vec_hist, info_dict) = odeint(self.__mbal_rhs_func,
u_vec_0, t_interval,
args=(self.__ode_params,),
#rtol=1e-6, atol=1e-6, mxstep=1000,
full_output=True )
assert info_dict['message'] == 'Integration successful.','%r <= time <= %r; %r'%\
(time,time+self.time_step,info_dict['message'])
u_vec = u_vec_hist[1,:] # solution vector at final time step
time += self.time_step
# Verify global mass conservation; use u_vec directly not the phase containers
if self.monitor_mass_conservation_residual or time >= self.end_time:
self.__check_mass_conservation(u_vec, write=True)
else:
self.__check_mass_conservation(u_vec)
# Monitor total mass flowrates ; use u_vec directly not the phase containers
if self.monitor_mass_flowrates or time >= self.end_time:
(total_mass_inflow_rate, total_mass_outflow_rate) = \
self.__compute_total_mass_flowrates(u_vec)
print('total mass inflow rate [g/min] = %2.3e'%\
(total_mass_inflow_rate/(unit.gram/unit.min))
)
print('total mass outflow rate [g/min] = %2.3e'%\
(total_mass_outflow_rate/(unit.gram/unit.min))
)
print('\t net total mass flow rate [g/min] = %2.3e'%\
((total_mass_outflow_rate - total_mass_inflow_rate)/(unit.gram/unit.min))
)
# Now update the phase containers
self.__update_state_variables(u_vec, time)
return time
The mass-balance RHS function (what it is and its signature)
Signature (as implemented):
def __mbal_rhs_func(self, u_vec, time, params)
Purpose and behavior:
This method computes the time derivative dt_u for the ordered state vector u_vec (mass concentrations per phasic volume, ordered by species global IDs).
Major tasks inside the RHS function:
Enforce non-negativity by clamping negative concentrations to zero.
Compute state-derived outflow mass rates from u_vec via __update_outflow_mass_rates(u_vec).
Convert phasic mass concentrations to molar concentrations (per phasic volume) via __convert_mass_cc_to_molar_cc(u_vec).
Call the reaction mechanism to get g_vec (species mole generation rates per mixing-volume), using rxn_mech.g_vec(…).
For each phase (aqueous, organic, vapor) compute dt for each species using the local phase volume fraction, mixing volume, (inflow - outflow)/mixing_vol and the chemistry term g_vec * molar_mass, consistent with the phase formation. Vapor handling blends vapor fractions from both carrier phases.
The returned dt_u array matches the ordering of u_vec and is used by odeint to integrate forwards.
Ports and their interaction with time stepping
Ports exist to connect this Stage to other stages/modules (inter-stage streams, feed and product). The Stage maintains port_names_expected and uses send/recv to communicate during the run.
__call_ports(time) is invoked after each step and manages:
Receiving inflow messages (e.g., aquatic inflow) from connected ports and assigning the provided mass inflow rates into local mass inflow arrays.
Sending or responding to outflow/product requests when connected.
It relies on self.get_port(name).connected_port to decide if a port is active and uses self.send / self.recv for data exchange.
Typical interaction order in run():
__update_mass_inflow_rates() called before time loop (to initialize inflow rates).
For each time loop iteration:
__perturb_mass_inflow_rates() optionally modifies inflows.
__step(time) advances the state.
__call_ports(time) performs port communications (recv inflows, send outflow/product messages).
Example (single-line) list of port names referenced by the Stage (see code):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
Brief example usage snippet
Short illustrative example (how a user would set up and run a Stage):
from cortix import ReactionMechanism
create a stage (mixing volume, vol_flowrates [org, aqu, (vap_org, vap_aqu)], temperature)
stg = Stage(200unit.mL, [240unit.mL/unit.min, 360unit.mL/unit.min, (5unit.mL/unit.min, 0.0)], 313.15)
define and add reaction mechanism
mechanism = [‘H2O(a) <-> H2O(v) ; tau=2e-2 ; info=H2O vaporization’]
rxn_mech = ReactionMechanism(mechanism=mechanism, order_species=True)
stg.add_reaction_mechanism(rxn_mech)
run() expects arguments for logger/connection context; typically called by a simulation driver
stg.run(driver_args)
Note: connecting ports to other modules (to provide inflow mass rates and receive outflow queries) is done through Module port connection APIs (send/recv). The Stage expects to receive mass rates at inflow ports and will respond at outflow/product ports when connected.
Summary
Stage provides a time-integrated, phase-resolved solvent-extraction model with three phases: aqueous, organic, and vapor.
State variables are species mass concentrations per phasic volume; inflows are treated as parameters and converted to mass inflow rate vectors before running.
Time advancement uses odeint with an explicit __mbal_rhs_func that accounts for inflow – outflow terms and chemistry (via rxn_mech.g_vec).
Interaction with other modules is via ports; __call_ports manages port communications after each step.
Key developer-facing hooks: add_reaction_mechanism(), run(), __step(), __mbal_rhs_func(…), r_vec(), g_vec(), and phase history readers.
Constructor:
def init(self, mix_vol, vol_flowrates, temperature)
Reaction mechanism handling:
def add_reaction_mechanism(self, rxn_mech=None)
Simulation and stepping:
def run(self, *args)
def __call_ports(self, time)
def __step(self, time=0.0)
State assembly and conversion:
def __get_state_vector(self, time=None, cc_name=’mass_cc’)
def __convert_mass_cc_to_molar_cc(self, u_vec)
Mass balance RHS (ODE rhs):
def __mbal_rhs_func(self, u_vec, time, params)
This is the ODE right-hand-side function requested by odeint. See the signature above.
Outflow/inflow bookkeeping:
def __update_mass_inflow_rates(self)
def __update_outflow_mass_rates(self, u_vec)
State update and diagnostics:
def __update_state_variables(self, u_vec, time)
def __check_mass_conservation(self, u_vec, write=False)
def __total_mass_generation_rate_density_residual(self, u_vec)
Utilities for users (time histories and instantaneous vectors):
def r_vec(self, time=None)
def g_vec(self, time=None)
def rxn_efficiencies(self, time=None)
Several history-returning functions (r_vec_history, g_vec_history, mass_density_history, etc.)
Ports: the module expects inter-stage and feed/product ports (existence of ports). Example one-line python list of port names (as used in the Stage source):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
Purpose: __step advances the Stage state by exactly one time_step. It:
Assembles the current state vector u_vec_0 from phase containers.
Integrates the mass-balance ODE system in the interval [time, time+time_step] using odeint, with __mbal_rhs_func as the RHS function.
Validates integration success and performs mass balance checks.
Optionally prints monitored mass flow information.
Updates phase containers with the new state at the advanced time.
Returns the updated time.
Representative source snippet of __step (copied as-is from the Stage module):
def __step(self, time=0.0): “””Stepping Stage in time. “”” u_vec_0 = self.__get_state_vector(time)
t_interval = np.linspace(time, time+self.time_step, num=2) (u_vec_hist, info_dict) = odeint(self.__mbal_rhs_func, u_vec_0, t_interval, args=(self.__ode_params,), #rtol=1e-6, atol=1e-6, mxstep=1000, full_output=True ) assert info_dict['message'] == 'Integration successful.','%r <= time <= %r; %r'%\ (time,time+self.time_step,info_dict['message']) u_vec = u_vec_hist[1,:] # solution vector at final time step time += self.time_step # Verify global mass conservation; use u_vec directly not the phase containers if self.monitor_mass_conservation_residual or time >= self.end_time: self.__check_mass_conservation(u_vec, write=True) else: self.__check_mass_conservation(u_vec) # Monitor total mass flowrates ; use u_vec directly not the phase containers if self.monitor_mass_flowrates or time >= self.end_time: (total_mass_inflow_rate, total_mass_outflow_rate) = \ self.__compute_total_mass_flowrates(u_vec) print('total mass inflow rate [g/min] = %2.3e'%\ (total_mass_inflow_rate/(unit.gram/unit.min)) ) print('total mass outflow rate [g/min] = %2.3e'%\ (total_mass_outflow_rate/(unit.gram/unit.min)) ) print('\t net total mass flow rate [g/min] = %2.3e'%\ ((total_mass_outflow_rate - total_mass_inflow_rate)/(unit.gram/unit.min)) ) # Now update the phase containers self.__update_state_variables(u_vec, time) return time
The mass-balance RHS function (what it is and its signature)
Signature (as implemented):
def __mbal_rhs_func(self, u_vec, time, params)
Purpose and behavior:
This method computes the time derivative dt_u for the ordered state vector u_vec (mass concentrations per phasic volume, ordered by species global IDs).
Major tasks inside the RHS function:
Enforce non-negativity by clamping negative concentrations to zero.
Compute state-derived outflow mass rates from u_vec via __update_outflow_mass_rates(u_vec).
Convert phasic mass concentrations to molar concentrations (per phasic volume) via __convert_mass_cc_to_molar_cc(u_vec).
Call the reaction mechanism to get g_vec (species mole generation rates per mixing-volume), using rxn_mech.g_vec(…).
For each phase (aqueous, organic, vapor) compute dt for each species using the local phase volume fraction, mixing volume, (inflow - outflow)/mixing_vol and the chemistry term g_vec * molar_mass, consistent with the phase formation. Vapor handling blends vapor fractions from both carrier phases.
The returned dt_u array matches the ordering of u_vec and is used by odeint to integrate forwards.
Ports and their interaction with time stepping
Ports exist to connect this Stage to other stages/modules (inter-stage streams, feed and product). The Stage maintains port_names_expected and uses send/recv to communicate during the run.
__call_ports(time) is invoked after each step and manages:
Receiving inflow messages (e.g., aquatic inflow) from connected ports and assigning the provided mass inflow rates into local mass inflow arrays.
Sending or responding to outflow/product requests when connected.
It relies on self.get_port(name).connected_port to decide if a port is active and uses self.send / self.recv for data exchange.
Typical interaction order in run():
__update_mass_inflow_rates() called before time loop (to initialize inflow rates).
For each time loop iteration:
__perturb_mass_inflow_rates() optionally modifies inflows.
__step(time) advances the state.
__call_ports(time) performs port communications (recv inflows, send outflow/product messages).
Example (single-line) list of port names referenced by the Stage (see code):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
Brief example usage snippet
Short illustrative example (how a user would set up and run a Stage):
from cortix import ReactionMechanism
create a stage (mixing volume, vol_flowrates [org, aqu, (vap_org, vap_aqu)], temperature)
stg = Stage(200unit.mL, [240unit.mL/unit.min, 360unit.mL/unit.min, (5unit.mL/unit.min, 0.0)], 313.15)
define and add reaction mechanism
mechanism = [‘H2O(a) <-> H2O(v) ; tau=2e-2 ; info=H2O vaporization’]
rxn_mech = ReactionMechanism(mechanism=mechanism, order_species=True)
stg.add_reaction_mechanism(rxn_mech)
run() expects arguments for logger/connection context; typically called by a simulation driver
stg.run(driver_args)
Note: connecting ports to other modules (to provide inflow mass rates and receive outflow queries) is done through Module port connection APIs (send/recv). The Stage expects to receive mass rates at inflow ports and will respond at outflow/product ports when connected.
Summary
Stage provides a time-integrated, phase-resolved solvent-extraction model with three phases: aqueous, organic, and vapor.
State variables are species mass concentrations per phasic volume; inflows are treated as parameters and converted to mass inflow rate vectors before running.
Time advancement uses odeint with an explicit __mbal_rhs_func that accounts for inflow – outflow terms and chemistry (via rxn_mech.g_vec).
Interaction with other modules is via ports; __call_ports manages port communications after each step.
Key developer-facing hooks: add_reaction_mechanism(), run(), __step(), __mbal_rhs_func(…), r_vec(), g_vec(), and phase history readers.
Signature (as implemented):
def __mbal_rhs_func(self, u_vec, time, params)
Purpose and behavior:
This method computes the time derivative dt_u for the ordered state vector u_vec (mass concentrations per phasic volume, ordered by species global IDs).
Major tasks inside the RHS function:
Enforce non-negativity by clamping negative concentrations to zero.
Compute state-derived outflow mass rates from u_vec via __update_outflow_mass_rates(u_vec).
Convert phasic mass concentrations to molar concentrations (per phasic volume) via __convert_mass_cc_to_molar_cc(u_vec).
Call the reaction mechanism to get g_vec (species mole generation rates per mixing-volume), using rxn_mech.g_vec(…).
For each phase (aqueous, organic, vapor) compute dt for each species using the local phase volume fraction, mixing volume, (inflow - outflow)/mixing_vol and the chemistry term g_vec * molar_mass, consistent with the phase formation. Vapor handling blends vapor fractions from both carrier phases.
The returned dt_u array matches the ordering of u_vec and is used by odeint to integrate forwards.
Ports exist to connect this Stage to other stages/modules (inter-stage streams, feed and product). The Stage maintains port_names_expected and uses send/recv to communicate during the run.
__call_ports(time) is invoked after each step and manages:
Receiving inflow messages (e.g., aquatic inflow) from connected ports and assigning the provided mass inflow rates into local mass inflow arrays.
Sending or responding to outflow/product requests when connected.
It relies on self.get_port(name).connected_port to decide if a port is active and uses self.send / self.recv for data exchange.
Typical interaction order in run():
__update_mass_inflow_rates() called before time loop (to initialize inflow rates).
For each time loop iteration:
__perturb_mass_inflow_rates() optionally modifies inflows.
__step(time) advances the state.
__call_ports(time) performs port communications (recv inflows, send outflow/product messages).
Example (single-line) list of port names referenced by the Stage (see code):
ports = [‘aqu-feed’, ‘org-feed’, ‘aqu-product’, ‘org-product’, ‘aqu-inflow’, ‘org-inflow’, ‘aqu-outflow’, ‘org-outflow’, ‘vap-aqu-inflow’, ‘vap-org-inflow’,’vap-aqu-outflow’,’vap-org-outflow’]
Brief example usage snippet
Short illustrative example (how a user would set up and run a Stage):
from cortix import ReactionMechanism
create a stage (mixing volume, vol_flowrates [org, aqu, (vap_org, vap_aqu)], temperature)
stg = Stage(200unit.mL, [240unit.mL/unit.min, 360unit.mL/unit.min, (5unit.mL/unit.min, 0.0)], 313.15)
define and add reaction mechanism
mechanism = [‘H2O(a) <-> H2O(v) ; tau=2e-2 ; info=H2O vaporization’]
rxn_mech = ReactionMechanism(mechanism=mechanism, order_species=True)
stg.add_reaction_mechanism(rxn_mech)
run() expects arguments for logger/connection context; typically called by a simulation driver
stg.run(driver_args)
Note: connecting ports to other modules (to provide inflow mass rates and receive outflow queries) is done through Module port connection APIs (send/recv). The Stage expects to receive mass rates at inflow ports and will respond at outflow/product ports when connected.
Summary
Stage provides a time-integrated, phase-resolved solvent-extraction model with three phases: aqueous, organic, and vapor.
State variables are species mass concentrations per phasic volume; inflows are treated as parameters and converted to mass inflow rate vectors before running.
Time advancement uses odeint with an explicit __mbal_rhs_func that accounts for inflow – outflow terms and chemistry (via rxn_mech.g_vec).
Interaction with other modules is via ports; __call_ports manages port communications after each step.
Key developer-facing hooks: add_reaction_mechanism(), run(), __step(), __mbal_rhs_func(…), r_vec(), g_vec(), and phase history readers.
Short illustrative example (how a user would set up and run a Stage):
from cortix import ReactionMechanism
create a stage (mixing volume, vol_flowrates [org, aqu, (vap_org, vap_aqu)], temperature)
stg = Stage(200unit.mL, [240unit.mL/unit.min, 360unit.mL/unit.min, (5unit.mL/unit.min, 0.0)], 313.15)
define and add reaction mechanism
mechanism = [‘H2O(a) <-> H2O(v) ; tau=2e-2 ; info=H2O vaporization’] rxn_mech = ReactionMechanism(mechanism=mechanism, order_species=True) stg.add_reaction_mechanism(rxn_mech)
run() expects arguments for logger/connection context; typically called by a simulation driver
stg.run(driver_args)
Note: connecting ports to other modules (to provide inflow mass rates and receive outflow queries) is done through Module port connection APIs (send/recv). The Stage expects to receive mass rates at inflow ports and will respond at outflow/product ports when connected.
Stage provides a time-integrated, phase-resolved solvent-extraction model with three phases: aqueous, organic, and vapor.
State variables are species mass concentrations per phasic volume; inflows are treated as parameters and converted to mass inflow rate vectors before running.
Time advancement uses odeint with an explicit __mbal_rhs_func that accounts for inflow – outflow terms and chemistry (via rxn_mech.g_vec).
Interaction with other modules is via ports; __call_ports manages port communications after each step.
Key developer-facing hooks: add_reaction_mechanism(), run(), __step(), __mbal_rhs_func(…), r_vec(), g_vec(), and phase history readers.
Source: stage.py
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ RAG = stage.py
+ Total # of tokens = 18921
Done with help...
1.2.1.1. Configuration#
'''Create Stage and add to base system'''
# Initialization
mix_volume = 1*unit.L
# Aqueous phase
vol_flowrate_aqu = 500*unit.mL/unit.min
# Organic phase
vol_flowrate_org = 600*unit.mL/unit.min
# Vapor phase
vol_flowrate_vap = (3.7*vol_flowrate_org/100, 8.1*vol_flowrate_aqu/100) # percentage of (org, aqu)
vol_flowrates = [vol_flowrate_org, vol_flowrate_aqu, vol_flowrate_vap]
stg_temperature = unit.convert_temperature(40, 'C', 'K')
stg = Stage(mix_volume, vol_flowrates, stg_temperature) # Create solvent extraction module
system_net.module(stg) # Add stage module to network
if cortix_ai:
cortix_ai.explain(markdown_header_level='<h5>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview
This snippet builds and registers a solvent-extraction “Stage” object into a process network. It defines volumes, phase flowrates (with units), a stage temperature (converted to Kelvin), constructs the Stage, and attaches it to system_net. A conditional call to cortix_ai.explain occurs at the end (see last section).
Top-line triple-quoted string serves as a short module-level description: “Create Stage and add to base system”.
Units and basic quantities
mix_volume is set to 1 * unit.L, i.e., a liquid volume quantity of 1 liter using the codebase’s unit system.
vol_flowrate_aqu is 500 * unit.mL / unit.min, representing the aqueous phase volumetric flow in milliliters per minute.
vol_flowrate_org is 600 * unit.mL / unit.min, representing the organic phase volumetric flow in milliliters per minute.
Vapor-phase flow specification
vol_flowrate_vap is defined as a 2-tuple:
First element: 3.7% of vol_flowrate_org computed as 3.7 * vol_flowrate_org / 100.
Second element: 8.1% of vol_flowrate_aqu computed as 8.1 * vol_flowrate_aqu / 100.
The intent is to represent vapor-phase contributions as percentages of the organic and aqueous flows.
vol_flowrates is a list [vol_flowrate_org, vol_flowrate_aqu, vol_flowrate_vap], ordering phases as (organic, aqueous, vapor).
Temperature handling
stg_temperature = unit.convert_temperature(40, ‘C’, ‘K’) converts 40 degrees Celsius to Kelvin via the unit helper, producing a temperature quantity suitable for the Stage constructor.
Stage construction and registration
stg = Stage(mix_volume, vol_flowrates, stg_temperature) constructs a Stage object (named “stg”) with:
mixing volume,
the list of phase flowrates,
the stage temperature.
The comment indicates this Stage is a “solvent extraction module”.
system_net.module(stg) registers or adds that Stage instance to the larger process network object system_net.
Conditional AI/metadata call
The final if-block checks the truthiness of cortix_ai.
If cortix_ai evaluates as true, a method is invoked with keyword arguments markdown_header_level=’
’, markdown_display=markdown_display, and save_supporting_info=db_save.
The code does not modify prior variables; this is an auxiliary call executed only when cortix_ai is present.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 3670
print('Flow residence time [s]: average = %5.3e'%stg.flow_residence_time_avg)
print('Aqueous volume fraction = %5.3e'%stg.volume_frac_aqu)
print('Organic volume fraction = %5.3e'%stg.volume_frac_org)
print('Vapor volume fraction = %5.3e'%stg.volume_frac_vap)
Flow residence time [s]: average = 5.160e+01
Aqueous volume fraction = 4.300e-01
Organic volume fraction = 5.160e-01
Vapor volume fraction = 5.393e-02
'''Draw the Cortix network system'''
system_net.draw(engine='circo', node_shape='folder', ports=True)
'''For help purposes'''
import solvex.stage
Documentation options:
Live in this notebook run on code cell:
help(solvex_ustc.stage)On the web: source
# Poor's man help
#help(solvex_ustc.stage)
1.2.2. Reaction Mechanism#
1.2.2.1. Water, nitric acid, and uranyl extraction with vapor model#
args_dict = {'water_activity': 1.0}
file_name='tbp-h2o-hno3-uo22+-air.txt'
rxn_mech = ReactionMechanism(file_name=file_name, order_species=True, args_dict=args_dict)
WARNING: ReactionMechanism: user must implement a H2O*[C4H9O]3PO(o) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for [C4H9O]3PO(o) + H2O(a) <-> H2O*[C4H9O]3PO(o)
WARNING: ReactionMechanism: user must implement a [H2O]2*[[C4H9O]3PO]2(o) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for 2 [C4H9O]3PO(o) + 2 H2O(a) <-> [H2O]2*[[C4H9O]3PO]2(o)
WARNING: ReactionMechanism: user must implement a [H2O]6*[[C4H9O]3PO]3(o) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for 3 [C4H9O]3PO(o) + 6 H2O(a) <-> [H2O]6*[[C4H9O]3PO]3(o)
WARNING: ReactionMechanism: user must implement a HNO3*[C4H9O]3PO(o) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for H^+(a) + NO3^-(a) + [C4H9O]3PO(o) <-> HNO3*[C4H9O]3PO(o)
WARNING: ReactionMechanism: user must implement a HNO3*[[C4H9O]3PO]2(o) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for H^+(a) + NO3^-(a) + 2 [C4H9O]3PO(o) <-> HNO3*[[C4H9O]3PO]2(o)
WARNING: ReactionMechanism: user must implement a UO2[NO3]2*[[C4H9O]3PO]2(o) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for UO2^2+(a) + 2 NO3^-(a) + 2 [C4H9O]3PO(o) <-> UO2[NO3]2*[[C4H9O]3PO]2(o)
WARNING: ReactionMechanism: user must implement a H2O(v) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for H2O(a) <-> H2O(v)
WARNING: ReactionMechanism: user must implement a O2(a) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for O2(v) <-> O2(a)
WARNING: ReactionMechanism: user must implement a N2(a) product partition function with signature <product>(rxn_mech, temperature, spc_molar_cc, arg_dict) function for N2(v) <-> N2(a)
#'''User input'''
#rxn_mech.cat_input()
#'''Show mechanism'''
# Jupyter Book does not render LaTeX through IPython.display(Markdown)
#rxn_mech.md_print()
#'''Species and reactions manual output (copy output and paste into mardown cell)'''
#print(len(rxn_mech.species_names), ' **Species**\n', rxn_mech.latex_species)
#print(len(rxn_mech.reactions), ' **Reactions**\n', rxn_mech.latex_rxn)
16 Species
9 Reactions
1.2.2.2. Sanity Check#
'''Data check'''
print('Is mass conserved?', rxn_mech.is_mass_conserved())
rxn_mech.rank_analysis(verbose=True, tol=1e-8)
print('S=\n', rxn_mech.stoic_mtrx)
Is mass conserved? True
# reactions = 9
# species = 16
rank of S = 9
S is full rank.
S=
[[-1. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. -1. 0. 0.]
[-2. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. -2. 1. 0.]
[-6. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. -3. 0. 1.]
[ 0. 0. 0. 1. 0. -1. 0. 0. -1. 0. 0. 0. 0. -1. 0. 0.]
[ 0. 0. 0. 0. 1. -1. 0. 0. -1. 0. 0. 0. 0. -2. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. -2. 0. 0. 1. -1. -2. 0. 0.]
[-1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. -1. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 1. -1. 0. 0. 0. 0. 0. 0. 0. 0.]]
1.2.2.3. User-Provided Partition Functions#
'''Partition functions involved in the reaction mechanism'''
from solvex.partition_func_local import partition_h2o_tbp_org
from solvex.partition_func_local import partition_2h2o_2tbp_org
from solvex.partition_func_local import partition_6h2o_3tbp_org
# Partition function for H2O*TBP complexation
rxn_mech.data[0]['tau-partition-function'] = partition_h2o_tbp_org
# Partition function for 2H2O*2TBP complexation
rxn_mech.data[1]['tau-partition-function'] = partition_2h2o_2tbp_org
# Partition function for 6H2O*3TBP complexation
rxn_mech.data[2]['tau-partition-function'] = partition_6h2o_3tbp_org
from solvex.partition_func_local import partition_hno3_tbp_org
from solvex.partition_func_local import partition_hno3_2tbp_org
# Partition function for HNO3*TBP complexation
rxn_mech.data[3]['tau-partition-function'] = partition_hno3_tbp_org
# Partition function for HNO3*2TBP complexation
rxn_mech.data[4]['tau-partition-function'] = partition_hno3_2tbp_org
from solvex.partition_func_local import partition_uo2no32_2tbp_org
# Partition function for UO2(NO3)2*2TBP complexation
rxn_mech.data[5]['tau-partition-function'] = partition_uo2no32_2tbp_org
from solvex import partition_h2o_vap
from solvex import partition_o2_aqu
from solvex import partition_n2_aqu
# Partition function for h2o vaporization
rxn_mech.data[6]['tau-partition-function'] = partition_h2o_vap
# Partition function for O2 absorption
rxn_mech.data[7]['tau-partition-function'] = partition_o2_aqu
# Partition function for N2 absorption
rxn_mech.data[8]['tau-partition-function'] = partition_n2_aqu
if cortix_ai:
cortix_ai.explain(markdown_header_level='<h5>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview
The snippet assigns partition-function callables to entries in a reaction-mechanism data structure (rxn_mech.data).
Each assignment links a named reaction or physical process (complexation or phase transfer) to a corresponding partition-function function imported from solvex or solvex.partition_func_local.
Imports
Several partition-function functions are imported from solvex.partition_func_local:
partition_h2o_tbp_org
partition_2h2o_2tbp_org
partition_6h2o_3tbp_org
partition_hno3_tbp_org
partition_hno3_2tbp_org
partition_uo2no32_2tbp_org
Three partition-function functions are imported directly from the solvex package:
partition_h2o_vap
partition_o2_aqu
partition_n2_aqu
Assignments to rxn_mech.data
The code sets the ‘tau-partition-function’ key for specific entries of rxn_mech.data by index:
Index 0: partition_h2o_tbp_org — H2O·TBP complexation
Index 1: partition_2h2o_2tbp_org — 2 H2O·2 TBP complexation
Index 2: partition_6h2o_3tbp_org — 6 H2O·3 TBP complexation
Index 3: partition_hno3_tbp_org — HNO3·TBP complexation
Index 4: partition_hno3_2tbp_org — HNO3·2 TBP complexation
Index 5: partition_uo2no32_2tbp_org — UO2(NO3)2·2 TBP complexation
Index 6: partition_h2o_vap — H2O vaporization (phase transfer)
Index 7: partition_o2_aqu — O2 absorption into aqueous phase
Index 8: partition_n2_aqu — N2 absorption into aqueous phase
Each assignment stores a function object (callable) under the ‘tau-partition-function’ key for the given reaction/process entry; rxn_mech.data is treated as an indexable sequence of mapping objects.
Conditional call
The code ends with a conditional that, if the name cortix_ai is truthy, invokes cortix_ai.explain with specific keyword arguments.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 3615
1.2.2.4. Add Reaction Mechanism to Stage#
stg.add_reaction_mechanism(rxn_mech)
1.2.2.5. Verify Species Groups#
#'''Aqueous phase'''
# Jupyter Book does not render LaTeX through IPython.display(Markdown)
#str = stg.rxn_mech.md_print('(a)')
#'''Show aqueous phase'''
# Jupyter Book does not render LaTeX through IPython.display(Markdown)
#print(str)
#'''Organic phase'''
# Jupyter Book does not render LaTeX through IPython.display(Markdown)
#str = stg.rxn_mech.md_print('(o)', n_species_line=5)
#'''Show organic phase'''
# Jupyter Book does not render LaTeX through IPython.display(Markdown)
#print(str)
#'''Vapor phase'''
# Jupyter Book does not render LaTeX through IPython.display(Markdown)
#str = stg.rxn_mech.md_print('(v)')
#'''Show vapor phase'''
# Jupyter Book does not render LaTeX through IPython.display(Markdown)
#print(str)
1.2.2.6. Mass Transfer Data#
'''Adjust relaxation times for mass transfer'''
rxn_mech.data[0]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[1]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[2]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[3]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[4]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[5]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[6]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[7]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[8]['tau'] = 1.0e-0 * stg.flow_residence_time_avg
if cortix_ai:
cortix_ai.explain(markdown_header_level='<h5>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview
The snippet sets the ‘tau’ value for multiple reaction entries in rxn_mech.data and conditionally invokes a method on cortix_ai.
Detailed explanation
The first line is a module-level comment string: “Adjust relaxation times for mass transfer”.
rxn_mech.data[0][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[1][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[2][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[3][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[4][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[5][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[6][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[7][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
rxn_mech.data[8][‘tau’] = 1.0e-0 * stg.flow_residence_time_avg
Each assignment targets an element of rxn_mech.data by index (0 through 8) and sets the dictionary key ‘tau’.
The right-hand side multiplies stg.flow_residence_time_avg by 1.0e-0. Since 1.0e-0 is numerically 1.0, each ‘tau’ is set equal to stg.flow_residence_time_avg.
The grouping/blank lines in the code do not affect behavior; they only separate assignments visually.
if cortix_ai:
When cortix_ai evaluates truthy, the code calls cortix_ai.explain(markdown_header_level=’
’, markdown_display=markdown_display, save_supporting_info=db_save).
The conditional checks cortix_ai’s truthiness and performs that method call with three keyword arguments.
State changes and side effects
rxn_mech.data is mutated in-place: entries at indices 0..8 gain/overwrite the ‘tau’ key with the same numeric value stg.flow_residence_time_avg.
No values are returned by the shown code; its effect is limited to in-memory assignment and the conditional method call.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 3801
1.2.2.7. Meta Data#
'''Names and info of interest for species'''
tbp_org_name = '[C4H9O]3PO(o)'
tbp_org = stg.organic_phase.get_species(tbp_org_name)
tbp_org.info = 'Free TBP'
tbp_monomer_org_name = 'H2O*[C4H9O]3PO(o)'
tbp_monomer_org = stg.organic_phase.get_species(tbp_monomer_org_name)
tbp_monomer_org.info = 'TBP Monomer Hydrate'
tbp_dimer_org_name = '[H2O]2*[[C4H9O]3PO]2(o)'
tbp_dimer_org = stg.organic_phase.get_species(tbp_dimer_org_name)
tbp_dimer_org.info = 'TBP Dimer Hydrate'
tbp_trimer_hexahydrate_org_name = '[H2O]6*[[C4H9O]3PO]3(o)'
tbp_trimer_hexahydrate_org = stg.organic_phase.get_species(tbp_trimer_hexahydrate_org_name)
tbp_trimer_hexahydrate_org.info = 'TBP Trimer Hexahydrate'
uo2no3_2_2tbp_org_name = 'UO2[NO3]2*['+tbp_org_name[:-3]+']2(o)'
uo2no3_2_2tbp_org = stg.organic_phase.get_species(uo2no3_2_2tbp_org_name)
uo2no3_2_2tbp_org.info = r'UO$_2$(NO$_3$)$_2$ * 2TBP'
1.3. Initial Conditions of Mixer#
1.3.1. Organic Phase#
'''Organic phase in the mixer (diluent is inert)'''
vol_frac_tbp_org = 30/100 # free tbp
#TODO: look this up at 40 C # W: TODO: look this up at 40 C
rho_tbp = 972.5 * unit.gram/unit.L # pure liquid TBP
stg.rxn_mech.args_dict['rho-tbp'] = rho_tbp
tbp_mass_cc_org = rho_tbp * vol_frac_tbp_org # per volume of organic phase in the mixture
stg.organic_phase.set_value(tbp_org_name, tbp_mass_cc_org)
print('mass_cc_tbp_org [g/L] =', tbp_mass_cc_org)
print('molar_cc_tbp_org [M] = %1.5e'%(tbp_mass_cc_org/tbp_org.molar_mass/unit.molar))
mass_cc_tbp_org [g/L] = 291.75
molar_cc_tbp_org [M] = 1.09551e+00
1.3.2. Aqueous Phase#
'''Aqueous phase in the mixer'''
h2o_aqu = stg.aqueous_phase.get_species('H2O(a)')
h2o_aqu.info = 'Water'
#TODO look this up at 40 C # W: TODO look this up at 40 C
rho_h2o_aqu = 992 * unit.gram/unit.L # per volume of aqueous phase in the mixture
stg.aqueous_phase.set_value('H2O(a)', rho_h2o_aqu)
c_hno3_aqu = 1e-3 * unit.molar # residual
h_plus_aqu = stg.aqueous_phase.get_species('H^+(a)')
rho_h_plus_aqu = c_hno3_aqu * h_plus_aqu.molar_mass
stg.aqueous_phase.set_value('H^+(a)', rho_h_plus_aqu)
no3_minus_aqu = stg.aqueous_phase.get_species('NO3^-(a)')
rho_no3_minus_aqu = c_hno3_aqu * no3_minus_aqu.molar_mass
stg.aqueous_phase.set_value('NO3^-(a)', rho_no3_minus_aqu)
rho_uo2_2plus_aqu = 0 * unit.gram/unit.L
stg.aqueous_phase.set_value('UO2^2+(a)', rho_uo2_2plus_aqu)
if cortix_ai:
cortix_ai.explain(markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Purpose
This code configures an aqueous phase in a chemistry/thermophysical staging object (stg): it registers water, sets mass concentrations (mass per L of aqueous phase) for a small residual nitric acid and selected ions, and ensures uranium(vi) ion is absent.
It operates on
stg.aqueous_phasespecies objects, reading attributes and writing mass-concentration values viaset_value.Units used are molarity (
unit.molar) for concentration and mass per volume (unit.gram/unit.L) for the values stored.
Species setup and metadata
The code obtains a species object for water, annotates it, and assigns a mass concentration for the aqueous phase.
h2o_aqu = stg.aqueous_phase.get_species('H2O(a)')retrieves the H₂O(a) species object from the aqueous phase.h2o_aqu.info = 'Water'sets a human-readable description on the species object.rho_h2o_aqu = 992 * unit.gram/unit.Ldefines the mass concentration assigned to water (comment notes this corresponds to ≈40 °C and is TODO-checked).stg.aqueous_phase.set_value('H2O(a)', rho_h2o_aqu)writes that water mass-per-volume into the aqueous phase representation.
Residual nitric acid and ion mass concentrations
A residual nitric acid concentration is specified in molar units, then converted to mass-per-volume for the dissociated ions using each species’ molar mass.
c_hno3_aqu = 1e-3 * unit.molarsets a residual HNO₃ concentration of 1×10⁻³ mol·L⁻¹.For the proton:
h_plus_aqu = stg.aqueous_phase.get_species('H^+(a)')retrieves the H⁺(a) species object.rho_h_plus_aqu = c_hno3_aqu * h_plus_aqu.molar_masscomputes mass concentration = molar concentration × molar mass (units: mass/volume).stg.aqueous_phase.set_value('H^+(a)', rho_h_plus_aqu)stores that mass-per-volume for H⁺(a).
For the nitrate anion:
no3_minus_aqu = stg.aqueous_phase.get_species('NO3^-(a)')retrieves NO₃⁻(a).rho_no3_minus_aqu = c_hno3_aqu * no3_minus_aqu.molar_masscomputes its mass concentration from the same 1e-3 M.stg.aqueous_phase.set_value('NO3^-(a)', rho_no3_minus_aqu)stores the computed value.
Explicit zeroing and conditional call
The code explicitly sets the uranium(vi) dioxo ion to zero and conditionally invokes a high-level explain call.
rho_uo2_2plus_aqu = 0 * unit.gram/unit.Ldefines a zero mass concentration for UO₂²⁺(a).stg.aqueous_phase.set_value('UO2^2+(a)', rho_uo2_2plus_aqu)records that UO₂²⁺(a) is absent in the aqueous phase.The final
if cortix_ai:block callscortix_ai.explain(markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)whencortix_aiis truthy; the call is passed the specified keyword arguments.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 3816
1.3.3. Vapor Phase#
'''Vapor phase in the mixer'''
from solvex import air_vapor_content
stg_pressure = 1.0 * unit.bar
stg_relative_humidity = 35.0 # percent
(n2_mass_cc_vap, o2_mass_cc_vap, h2o_mass_cc_vap) = air_vapor_content(stg_pressure, stg_temperature,
stg_relative_humidity)
stg.vapor_phase.set_value('H2O(v)', h2o_mass_cc_vap) # per volume of the vapor phase in the mixture
stg.vapor_phase.set_value('N2(v)', n2_mass_cc_vap) # per volume of the vapor phase in the mixture
stg.vapor_phase.set_value('O2(v)', o2_mass_cc_vap) # per volume of the vapor phase in the mixture
if cortix_ai:
cortix_ai.explain(markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview
This snippet sets up a vapor-phase composition for a process stage (stg) based on air vapor content computed from pressure, temperature and relative humidity, then assigns per-volume mass values for N₂, O₂ and H₂O into the stage’s vapor-phase state.
Line-by-line explanation
The top comment labels the code block as handling the vapor phase in a mixer.
from solvex import air_vapor_content
Imports the function air_vapor_content from the solvex module; that function is used below to compute species mass concentrations in the vapor.
stg_pressure = 1.0 * unit.bar
Defines stg_pressure as 1.0 bar, using a units-enabled quantity (unit.bar indicates a unit object multiplied by the numeric value).
stg_relative_humidity = 35.0 # percent
Sets the relative humidity to 35.0 (interpreted as percent).
(n2_mass_cc_vap, o2_mass_cc_vap, h2o_mass_cc_vap) = air_vapor_content(stg_pressure, stg_temperature, stg_relative_humidity)
Calls air_vapor_content with three arguments: pressure, temperature, and relative humidity.
The returned tuple is unpacked into three variables named to indicate mass per volume in the vapor phase:
n2_mass_cc_vap — mass concentration for N₂ in the vapor phase (per unit volume of that phase).
o2_mass_cc_vap — mass concentration for O₂ in the vapor phase.
h2o_mass_cc_vap — mass concentration for H₂O (water vapor) in the vapor phase.
Note: stg_temperature is referenced here and therefore must have been defined earlier in the surrounding code.
stg.vapor_phase.set_value(‘H2O(v)’, h2o_mass_cc_vap) # per volume of the vapor phase in the mixture
Stores the computed water-vapor mass-per-volume into the stage’s vapor-phase state under the species name “H2O(v)”. The comment reiterates that the value is per volume of the vapor phase.
stg.vapor_phase.set_value(‘N2(v)’, n2_mass_cc_vap) # per volume of the vapor phase in the mixture
Stores the N₂ vapor mass-per-volume into the vapor-phase state under “N2(v)”.
stg.vapor_phase.set_value(‘O2(v)’, o2_mass_cc_vap) # per volume of the vapor phase in the mixture
Stores the O₂ vapor mass-per-volume into the vapor-phase state under “O2(v)”.
Variables, units and naming conventions
The variable names use the suffix _mass_cc_vap to indicate mass per volume of the vapor phase (cc suggesting volume unit, e.g., cm³ or other volumetric unit depending on context).
Species are labeled with “(v)” to denote the vapor phase (e.g., H2O(v), N2(v), O2(v)).
Units are provided via a unit system (unit.bar); compatibility between the unit system, air_vapor_content inputs/outputs, and stg.vapor_phase.set_value is implied by the code.
Runtime/context notes
stg, unit and stg_temperature (and the solvex.air_vapor_content implementation) must exist in the surrounding context for this snippet to run without NameError.
The code computes vapor-phase mass concentrations from thermodynamic inputs and writes them into the stage object’s vapor_phase state so downstream code can use those per-volume species values.
Final call
The snippet ends with a conditional call that invokes cortix_ai.explain(…) when cortix_ai is truthy; the call passes markdown_header_level, a markdown_display object, and a save flag.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4111
1.3.4. Wrap-up#
'''Returning to the aqueous phase to populate O2 and N2'''
o2_molar_mass = stg.aqueous_phase.get_species('O2(a)').molar_mass
o2_molar_cc_vap = o2_mass_cc_vap / o2_molar_mass
equilibrium_fraction = 0.5
partition_coeff = partition_o2_aqu(rxn_mech, stg_temperature, None)
o2_molar_cc_aqu = partition_coeff * o2_molar_cc_vap
o2_mass_cc_aqu = equilibrium_fraction * o2_molar_cc_aqu * o2_molar_mass
stg.aqueous_phase.set_value('O2(a)', o2_mass_cc_aqu)
n2_molar_mass = stg.aqueous_phase.get_species('N2(a)').molar_mass
n2_molar_cc_vap = n2_mass_cc_vap / n2_molar_mass
equilibrium_fraction = 0.5
partition_coeff = partition_n2_aqu(rxn_mech, stg_temperature, None)
n2_molar_cc_aqu = partition_coeff * n2_molar_cc_vap
n2_mass_cc_aqu = equilibrium_fraction * n2_molar_cc_aqu * n2_molar_mass
stg.aqueous_phase.set_value('N2(a)', n2_mass_cc_aqu)
if cortix_ai:
cortix_ai.explain(markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Summary
This snippet moves dissolved O₂ and N₂ amounts from a vapor-phase basis into the aqueous phase for a stage object (stg). It computes molar concentrations from given vapor masses, applies phase-partition coefficients, applies an equilibrium fraction, converts back to mass, and writes the results into stg.aqueous_phase.
Variables and units
o2_molar_mass: molar mass of aqueous O₂ species obtained from stg.aqueous_phase.get_species(‘O2(a)’).molar_mass.
o2_mass_cc_vap, n2_mass_cc_vap: input mass-per-volume values for O₂ and N₂ in the vapor (assumed defined earlier); unit implied as mass per cc by the name.
*_molar_cc_vap: molar concentration in the vapor phase = mass_cc_vap / molar_mass (mol per cc).
partition_coeff: dimensionless partition coefficient returned by partition_o2_aqu(…) or partition_n2_aqu(…); used to convert vapor molar concentration to aqueous molar concentration.
*_molar_cc_aqu: molar concentration in the aqueous phase (mol per cc).
equilibrium_fraction: fraction of the computed aqueous molar concentration to keep as mass in the aqueous phase (set to 0.5 in both blocks).
*_mass_cc_aqu: final aqueous mass-per-volume (same units as input mass_cc_vap) written into stg.aqueous_phase.
stg.aqueous_phase.set_value(species, value): stores computed mass-per-volume for that aqueous species.
Step-by-step execution
Retrieve O₂ molar mass from the aqueous-phase species object.
Convert the provided vapor O₂ mass-per-volume (o2_mass_cc_vap) to molar concentration: o2_molar_cc_vap = o2_mass_cc_vap / o2_molar_mass.
Set equilibrium_fraction to 0.5.
Call partition_o2_aqu(rxn_mech, stg_temperature, None) to obtain a partition coefficient for O₂.
Compute aqueous molar concentration: o2_molar_cc_aqu = partition_coeff * o2_molar_cc_vap.
Convert a fraction of that aqueous molar concentration to mass-per-volume: o2_mass_cc_aqu = equilibrium_fraction * o2_molar_cc_aqu * o2_molar_mass.
Store o2_mass_cc_aqu into the aqueous phase under ‘O2(a)’ via stg.aqueous_phase.set_value.
Repeat the analogous sequence for N₂:
Read N₂ molar mass from stg.aqueous_phase.get_species(‘N2(a)’).molar_mass.
Convert vapor mass to vapor molar concentration: n2_molar_cc_vap = n2_mass_cc_vap / n2_molar_mass.
Reset equilibrium_fraction to 0.5 (same value).
Obtain partition coefficient via partition_n2_aqu(rxn_mech, stg_temperature, None).
Compute aqueous molar concentration and convert the equilibrium_fraction to mass-per-volume: n2_mass_cc_aqu = equilibrium_fraction * (partition_coeff * n2_molar_cc_vap) * n2_molar_mass.
Store n2_mass_cc_aqu into stg.aqueous_phase under ‘N2(a)’.
Finally, if cortix_ai is truthy, the code invokes cortix_ai.explain with given keyword arguments.
Notes and assumptions
The code assumes o2_mass_cc_vap and n2_mass_cc_vap are defined earlier and expressed in the same mass-per-volume units expected by stg.aqueous_phase.set_value.
equilibrium_fraction is hard-coded to 0.5 for both species; the second assignment overwrites the first but with the same value.
partition_o2_aqu and partition_n2_aqu are expected to return dimensionless conversion factors that convert vapor molar concentration to aqueous molar concentration.
All numeric multiplications follow: mass_per_volume = fraction * (partition_coeff * (mass_per_volume_vap / molar_mass)) * molar_mass, which algebraically reduces to mass_per_volume = fraction * partition_coeff * mass_per_volume_vap (molar_mass cancels), so the code preserves consistent units when inputs are consistent.
State changes and side effects
stg.aqueous_phase is modified twice: values for ‘O2(a)’ and ‘N2(a)’ are updated to the computed aqueous mass-per-volume.
The code relies on external functions/objects (partition_o2_aqu, partition_n2_aqu, stg, rxn_mech, stg_temperature, cortix_ai) that are not defined within the snippet.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4228
1.4. Inflow Conditions#
1.4.1. Aqueous Phase#
'''Aqueous phase in the inflow'''
#TODO here the concentration must be larger than in the initial condition in the mixer for lower temp # W: Line too long (105/100)
# look this up later, 1.01 factor may be incorrect
stg.inflow_aqueous_phase.set_value('H2O(a)', 1.0 * rho_h2o_aqu)
c_hno3_aqu = 0.5 * unit.molar # low acid feed
h_plus_aqu = stg.inflow_aqueous_phase.get_species('H^+(a)')
rho_plus_aqu = c_hno3_aqu * h_plus_aqu.molar_mass
stg.inflow_aqueous_phase.set_value('H^+(a)', rho_plus_aqu)
no3_minus_aqu = stg.inflow_aqueous_phase.get_species('NO3^-(a)')
rho_no3_minus_aqu = c_hno3_aqu * no3_minus_aqu.molar_mass
stg.inflow_aqueous_phase.set_value('NO3^-(a)', rho_no3_minus_aqu)
rho_uo2_2plus_aqu = 110 * unit.gram/unit.L
stg.inflow_aqueous_phase.set_value('UO2^2+(a)', rho_uo2_2plus_aqu)
if cortix_ai:
cortix_ai.explain(markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview
This snippet configures species concentrations in an inflow aqueous phase (sets water, acid ions, and a uranyl ion) using a stage-like API (stg.inflow_aqueous_phase).
Top-line comment and TODO note document intent and a pending check about concentration vs. mixer initial condition.
stg.inflow_aqueous_phase.set_value(‘H2O(a)’, 1.0 * rho_h2o_aqu) sets the aqueous water quantity for the inflow species named ‘H2O(a)’ to 1.0 times the value held in rho_h2o_aqu (presumably a mass concentration or density for H₂O(a)).
c_hno3_aqu = 0.5 * unit.molar defines the nitric acid feed concentration as 0.5 molar (0.5 mol·L⁻¹) and is labeled “low acid feed”.
h_plus_aqu = stg.inflow_aqueous_phase.get_species(‘H^+(a)’) retrieves the species object for H⁺(a); rho_plus_aqu = c_hno3_aqu * h_plus_aqu.molar_mass multiplies the molar concentration by the species molar mass to produce a mass concentration; stg.inflow_aqueous_phase.set_value(‘H^+(a)’, rho_plus_aqu) stores that mass-based value in the inflow.
no3_minus_aqu = stg.inflow_aqueous_phase.get_species(‘NO3^-(a)’) retrieves the NO₃⁻(a) species; rho_no3_minus_aqu = c_hno3_aqu * no3_minus_aqu.molar_mass converts the same 0.5 mol·L⁻¹ to a mass concentration for NO₃⁻(a); stg.inflow_aqueous_phase.set_value(‘NO3^-(a)’, rho_no3_minus_aqu) stores it.
rho_uo2_2plus_aqu = 110 * unit.gram/unit.L defines a mass concentration of 110 g·L⁻¹ for UO₂²⁺(a); stg.inflow_aqueous_phase.set_value(‘UO2^2+(a)’, rho_uo2_2plus_aqu) stores that value.
if cortix_ai: cortix_ai.explain(markdown_header_level=’
’, markdown_display=markdown_display, save_supporting_info=db_save) conditionally invokes the cortix_ai object’s explain method when cortix_ai is truthy (the call is supplied with a header-level argument and display/save flags).
Units and conversion principle
Concentration variable c_hno3_aqu is in molar (mol·L⁻¹); multiplying by a species’ molar_mass (g·mol⁻¹) yields a mass concentration (g·L⁻¹).
rho_h2o_aqu and rho_uo2_2plus_aqu are used/presented as mass concentrations or densities (units implied by multiplication with unit objects).
API and data roles
stg.inflow_aqueous_phase appears to be an inflow-phase object exposing set_value(name, value) to assign species amounts and get_species(name) to retrieve a species descriptor with attributes such as molar_mass.
Species identifiers in the code use ASCII tokens (e.g., ‘H2O(a)’, ‘H^+(a)’, ‘NO3^-(a)’, ‘UO2^2+(a)’) corresponding to chemical species H₂O(a), H⁺(a), NO₃⁻(a), and UO₂²⁺(a).
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4750
1.4.2. Organic Phase#
'''Organic phase in the inflow'''
# Set the same mass concentration as the initial condition in the mixer
stg.inflow_organic_phase.set_value(tbp_org_name, tbp_mass_cc_org)
1.4.3. Vapor Phase#
'''Vapor phase in the inflow'''
inflow_temperature = unit.convert_temperature(20, 'C', 'K')
inflow_pressure = 1.1*unit.bar
inflow_relative_humidity = 25.0
(n2_mass_cc_vap, o2_mass_cc_vap, h2o_mass_cc_vap) = air_vapor_content(inflow_pressure, inflow_temperature,
inflow_relative_humidity)
stg.inflow_vapor_aqueous_phase.set_value('N2(v)', n2_mass_cc_vap)
stg.inflow_vapor_organic_phase.set_value('N2(v)', n2_mass_cc_vap)
stg.inflow_vapor_aqueous_phase.set_value('O2(v)', o2_mass_cc_vap)
stg.inflow_vapor_organic_phase.set_value('O2(v)', o2_mass_cc_vap)
stg.inflow_vapor_aqueous_phase.set_value('H2O(v)', h2o_mass_cc_vap)
stg.inflow_vapor_organic_phase.set_value('H2O(v)', h2o_mass_cc_vap)
if cortix_ai:
cortix_ai.explain(markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview
This snippet prepares vapor-phase inflow properties (temperature, pressure, relative humidity), computes vapor-phase mass concentrations for N₂, O₂, and H₂O, assigns those concentrations into two inflow phase objects (aqueous and organic), and conditionally invokes a Cortix AI helper.
Line-by-line explanation
‘’’Vapor phase in the inflow’’’ — a standalone triple-quoted string used as a short descriptive note in the code.
inflow_temperature = unit.convert_temperature(20, ‘C’, ‘K’) — converts 20 °C to Kelvin and stores the result in inflow_temperature.
inflow_pressure = 1.1*unit.bar — sets inflow_pressure to 1.1 bar (using the provided unit object).
inflow_relative_humidity = 25.0 — sets relative humidity to 25.0 (percent).
(n2_mass_cc_vap, o2_mass_cc_vap, h2o_mass_cc_vap) = air_vapor_content(inflow_pressure, inflow_temperature, inflow_relative_humidity) — calls air_vapor_content with pressure, temperature, and relative humidity; the function returns three values which are assigned to the variables named for nitrogen, oxygen, and water vapor mass concentrations (variable names imply mass per cubic centimeter of vapor).
stg.inflow_vapor_aqueous_phase.set_value(‘N2(v)’, n2_mass_cc_vap) and stg.inflow_vapor_organic_phase.set_value(‘N2(v)’, n2_mass_cc_vap) — set the N₂(v) vapor mass concentration in both the inflow aqueous-phase and inflow organic-phase objects to the computed n2_mass_cc_vap.
stg.inflow_vapor_aqueous_phase.set_value(‘O2(v)’, o2_mass_cc_vap) and stg.inflow_vapor_organic_phase.set_value(‘O2(v)’, o2_mass_cc_vap) — set O₂(v) similarly.
stg.inflow_vapor_aqueous_phase.set_value(‘H2O(v)’, h2o_mass_cc_vap) and stg.inflow_vapor_organic_phase.set_value(‘H2O(v)’, h2o_mass_cc_vap) — set H₂O(v) similarly.
if cortix_ai: cortix_ai.explain(markdown_header_level=’
’, markdown_display=markdown_display, save_supporting_info=db_save) — if the cortix_ai object is truthy, its explain method is invoked with the given keyword arguments (markdown_header_level set to ‘
’, and two other parameters passed through).
Notes on names and conventions
Species strings like N₂(v), O₂(v), H₂O(v) include “(v)” to denote vapor-phase species.
Variable names with _mass_cc_vap indicate they hold mass-based concentrations for the vapor (the code’s naming convention suggests mass per cubic centimeter, though the exact unit depends on the implementation of air_vapor_content).
unit.convert_temperature and unit.bar rely on a units utility/object available in the environment; their exact types depend on that units implementation.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4143
1.4.4. Wrap-up#
#'''Returning to aqueous phase in the inflow'''
# Leave aqueous phase as is; no dissolved gas
#'''Returning to organic phase in the inflow'''
# Leave organic phase as is; no dissolved gas
1.5. Start-up Simulation#
Define the start-up simulation period as one flow residence time.
'''Getting ready to run'''
end_time = 1 * stg.flow_residence_time_avg
import numpy as np
ave_tau = np.mean([data['tau'] for data in stg.rxn_mech.data])
time_step = ave_tau / 15
show_time = (True, 10*time_step)
stg.name = 'Stg-1'
stg.save = True
stg.verbose = True
stg.perturb_flowrates = False
stg.time_step = time_step
stg.end_time = end_time
stg.show_time = show_time
'''Run system in parallel'''
stg.monitor_mass_flowrates = False
stg.monitor_mass_conservation_residual = False
stg.mass_bal_rate_dens_res_tol = 1.e-8 * unit.micro*unit.gram/unit.L/unit.second
system.run()
system.close() # Shutdown Cortix
[14410] 2025-12-02 14:00:33,011 - cortix - INFO - Launching Module <solvex.stage.Stage object at 0x7fba103e6510>
[15284] 2025-12-02 14:00:34,119 - cortix - INFO - Stg-1::run():time[m]=0.0
[15284] 2025-12-02 14:00:34,582 - cortix - INFO - Stg-1::run():time[m]=0.6
Total mass rate density (mixture volume) residual [g/L-s]= -1.66533e-16
total mass inflow rate [g/min] = 7.419e+02
total mass outflow rate [g/min] = 7.161e+02
net total mass flow rate [g/min] = -2.580e+01
[15284] 2025-12-02 14:00:34,730 - cortix - INFO - Stg-1::run():time[m]=0.9 (et[s]=0.6)
[14410] 2025-12-02 14:00:34,904 - cortix - INFO - run()::Elapsed wall clock time [s]: 344.65
[14410] 2025-12-02 14:00:34,905 - cortix - INFO - Closed Cortix object.
_____________________________________________________________________________
T E R M I N A T I N G
_____________________________________________________________________________
... s . (TAAG Fraktur)
xH88"`~ .x8X :8 @88>
:8888 .f"8888Hf u. .u . .88 %8P uL ..
:8888> X8L ^""` ...ue888b .d88B :@8c :888ooo . .@88b @88R
X8888 X888h 888R Y888r ="8888f8888r -*8888888 .@88u ""Y888k/"*P
88888 !88888. 888R I888> 4888>"88" 8888 888E` Y888L
88888 %88888 888R I888> 4888> " 8888 888E 8888
88888 `> `8888> 888R I888> 4888> 8888 888E `888N
`8888L % ?888 ! u8888cJ888 .d888L .+ .8888Lu= 888E .u./"888&
`8888 `-*"" / "*888*P" ^"8888*" ^%888* 888& d888" Y888*"
"888. :" "Y" "Y" "Y" R888" ` "Y Y"
`""***~"` ""
https://cortix.org
_____________________________________________________________________________
[14410] 2025-12-02 14:00:34,906 - cortix - INFO - close()::Elapsed wall clock time [s]: 344.65
'''Recover stage'''
stg = system_net.modules[0]
n_startup = len(stg.organic_phase.time_stamps)
if cortix_ai:
cortix_ai.explain(markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Summary
The snippet selects the first module from system_net, counts how many timestamps exist on that module’s organic_phase, stores that count in n_startup, and — if cortix_ai is truthy — invokes cortix_ai.explain with two keyword arguments.
Line-by-line explanation
‘’’Recover stage’’’
A triple-quoted string literal at the top of the snippet (serves as an inline, module-level note/comment).
stg = system_net.modules[0]
Reads the first element (index 0) of system_net.modules and assigns it to the variable stg. system_net.modules is treated as an indexable sequence.
n_startup = len(stg.organic_phase.time_stamps)
Accesses stg.organic_phase.time_stamps and applies len(…) to obtain its length; assigns that integer to n_startup. This assumes organic_phase has an attribute time_stamps that supports len() (e.g., a list or other sized sequence).
if cortix_ai:
A truthiness check: the block runs only if cortix_ai evaluates to True (not None/False/empty).
cortix_ai.explain(markdown_display=markdown_display, save_supporting_info=db_save)
When the if condition is met, calls the explain method on the cortix_ai object, passing two keyword arguments: markdown_display and save_supporting_info set to the value of db_save.
Variables and expected types/roles
system_net: an object exposing a modules sequence (indexable).
modules: a sequence (list/tuple-like) of module objects; modules[0] yields the target module.
stg: the selected module object.
stg.organic_phase: an attribute on the module that groups organic-phase data.
stg.organic_phase.time_stamps: an iterable/sequence of timestamps (supports len()).
n_startup: integer equal to the number of entries in time_stamps.
cortix_ai: an object checked for truthiness; if truthy, its explain method is invoked.
markdown_display, db_save: variables passed as keyword arguments to cortix_ai.explain.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 3342
1.5.1. Organic Phase Results#
'''Plot organic phase'''
# TODO: time axis normalized by phase flow residence time.
stg.organic_phase.plot(title='Organic Phase Start-Up', legend='Organic Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Organic phase species history dashboard at start-up.')
Figure 1: Organic phase species history dashboard at start-up.
Note depletion of free TBP in the organic phase.
Note corresponding complexation of TBP with H2O, HNO3 and uranyl.
Note orders of magnitude of mass concentration in the mixer.
Experimental measurements would be instrumental to help calibrate and validate the model.
if cortix_ai:
issues = '+ Title your reponse as: Overview of the Organic Phase Data at Start-Up.'
cortix_ai.explain(phase=stg.organic_phase, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of the Organic Phase Data at Start-Up.
Summary
Dataset: time series from 0 to 51.604 s (regular ~3.44 s spacing) of organic-phase concentrations (g/L) for an extractant [C₄H₉O]₃PO(o) and several adducts/complexes formed by uptake of H₂O, HNO₃, and UO₂(NO₃)₂.
Global behaviour: [C₄H₉O]₃PO(o) decreases monotonically from 291.75 to 215.493 g/L while all listed adducts/complexes increase monotonically from zero to positive values, indicating progressive solute uptake and extractant association over the 51.6 s interval.
Sampling note: first nonzero concentrations are recorded at t = 3.44027 s.
Trends by species (initial → final; net change; average rate)
[C₄H₉O]₃PO(o): 291.75 → 215.493; Δ = −76.257 g/L; average rate ≈ −1.478 g·L⁻¹·s⁻¹.
H₂O·[C₄H₉O]₃PO(o): 0 → 10.8402; Δ = +10.8402 g/L; average rate ≈ +0.2100 g·L⁻¹·s⁻¹.
HNO₃·[C₄H₉O]₃PO(o): 0 → 0.902605; Δ = +0.902605 g/L; average rate ≈ +0.0175 g·L⁻¹·s⁻¹.
HNO₃·[[C₄H₉O]₃PO]₂(o): 0 → 1.23869; Δ = +1.23869 g/L; average rate ≈ +0.0240 g·L⁻¹·s⁻¹.
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): 0 → 25.5828; Δ = +25.5828 g/L; average rate ≈ +0.4958 g·L⁻¹·s⁻¹.
[H₂O]₂·[[C₄H₉O]₃PO]₂(o): 0 → 43.3519; Δ = +43.3519 g/L; average rate ≈ +0.8400 g·L⁻¹·s⁻¹.
[H₂O]₆·[[C₄H₉O]₃PO]₃(o): 0 → 10.1912; Δ = +10.1912 g/L; average rate ≈ +0.1976 g·L⁻¹·s⁻¹.
Comparative observations and composition at 51.6 s
Total organic-phase concentration (sum of all reported species) at t = 51.604 s ≈ 307.60 g/L (initial total at t = 0 was 291.75 g/L).
Final composition (approx share of total 307.60 g/L):
[C₄H₉O]₃PO(o): 215.493 g/L ≈ 70.1% of the reported total.
[H₂O]₂·[[C₄H₉O]₃PO]₂(o): 43.352 g/L ≈ 14.1%.
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): 25.583 g/L ≈ 8.3%.
H₂O·[C₄H₉O]₃PO(o): 10.840 g/L ≈ 3.5%.
[H₂O]₆·[[C₄H₉O]₃PO]₃(o): 10.191 g/L ≈ 3.3%.
HNO₃-related adducts combined: ≈ 2.141 g/L ≈ 0.7% (HNO₃·[C₄H₉O]₃PO(o) + HNO₃·[[C₄H₉O]₃PO]₂(o)).
Interpretation of mass changes: although [C₄H₉O]₃PO(o) is depleted by ~76.3 g/L, the reported total organic-phase concentration increases by ~15.85 g/L, consistent with solvent binding of water, nitrate, and uranyl species (formation of several adducts) and net uptake of inorganic components into the organic phase.
Kinetic notes and timing
Formation is fastest (largest average growth rate) for [H₂O]₂·[[C₄H₉O]₃PO]₂(o) (~0.84 g·L⁻¹·s⁻¹) and UO₂ complex (~0.496 g·L⁻¹·s⁻¹), indicating rapid association of water and UO₂ species with the extractant during start-up.
Nitrate-bearing adducts appear in much smaller concentrations (combined ~2.14 g/L at 51.6 s) and grow an order of magnitude slower than the dominant water/uranyl-associated species.
All species show monotonic trends over the sampled interval; no reversals or oscillations are present in the provided data.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 6916
'''Organic phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('organic')
quant.plot(title='Organic Phase Mass Density @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+r'$-\rho_\text{diluent}$'
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Organic phase mass density history at start-up.')
Figure 2: Organic phase mass density history at start-up.
tbl_count += 1
print(f'Table {tbl_count}: Organic phase mass density history at start-up.')
print('Time [s] Organic Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 1: Organic phase mass density history at start-up.
Time [s] Organic Phase Mass Density [g/L]
0.000000 291.75
17.201342 296.10
34.402683 301.65
51.604025 307.60
Name: Organic Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.5.2. Aqueous Phase Results#
'''Plot aqueous phase'''
# TODO: time axis normalized by phase flow residence time.
stg.aqueous_phase.plot(title='Aqueous Phase Start-Up', legend='Aqueous Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Aqueous phase species history dashboard at start-up.')
Figure 3: Aqueous phase species history dashboard at start-up.
The inflow feed increases the concentration of all species in the aqueous phase of the mixer.
if cortix_ai:
issues = '+ Title your reponse as: Overview of the Aqueous Phase Data at Start-Up.\n'
cortix_ai.explain(phase=stg.aqueous_phase, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of the Aqueous Phase Data at Start-Up.
Summary (endpoints, absolute change, mean rate, relative change)
Time sampling: 0 → 51.604 s (16 points, uniform increment ≈ 3.44027 s).
H₂O(a): start 992 g/L → end 986.42 g/L; Δ = −5.58 g/L; mean rate ≈ −0.108 g·L⁻¹·s⁻¹; change ≈ −0.56%.
H⁺(a): start 0.00100739 g/L → end 0.31294 g/L; Δ = +0.31193 g/L; mean rate ≈ +0.00604 g·L⁻¹·s⁻¹; ≈3.1×10²-fold increase (≈3.1×10⁴%).
N₂(a): start 0.00815443 g/L → end 0.00853078 g/L; Δ = +0.000376 g/L; mean rate ≈ +7.29×10⁻⁶ g·L⁻¹·s⁻¹; ≈+4.6%.
NO₃⁻(a): start 0.0620055 g/L → end 15.1589 g/L; Δ = +15.0969 g/L; mean rate ≈ +0.2926 g·L⁻¹·s⁻¹; ≈2.4×10²-fold increase (≈2.4×10⁴%).
O₂(a): start 0.0053437 g/L → end 0.00540316 g/L; Δ = +5.95×10⁻⁵ g/L; mean rate ≈ +1.15×10⁻⁶ g·L⁻¹·s⁻¹; ≈+1.1%.
UO₂²⁺(a): start 0 g/L → end 60.5577 g/L; Δ = +60.5577 g/L; mean rate ≈ +1.1735 g·L⁻¹·s⁻¹; (no fold-change defined from zero).
Comparative observations
Largest absolute and fastest accumulation: UO₂²⁺(a) (≈ +1.17 g·L⁻¹·s⁻¹), closely followed by NO₃⁻(a) (≈ +0.293 g·L⁻¹·s⁻¹).
Moderate accumulation: H⁺(a) increases substantially in relative terms but is small in absolute mass (mean rate ≈ +0.00604 g·L⁻¹·s⁻¹).
Nearly invariant species: N₂(a) and O₂(a) change only slightly (rates ≲ 10⁻⁵ g·L⁻¹·s⁻¹), effectively constant on this timescale.
Solvent behavior: bulk H₂O(a) shows a small, steady decrease (−0.56%), consistent with transfer/consumption at the measured rates.
Time-series character and relationships
Linear trend: UO₂²⁺(a) and NO₃⁻(a) display approximately linear, monotonic increases over the recorded interval; H⁺(a) also rises steadily.
Magnitude hierarchy at t = 51.6 s: UO₂²⁺ ≫ NO₃⁻ ≫ H₂O ≫ (N₂, O₂, H⁺ in absolute mass except H₂O).
Sampling: regular spacing (3.44027 s) supports simple mean-rate estimates above; finer curvature or short-term transients (if present) are not evident from these points.
Notable numeric highlights
UO₂²⁺(a) final concentration 60.56 g/L (dominant solute by far).
NO₃⁻(a) rises from trace to 15.16 g/L — large production/ingress relative to starting level.
H₂O(a) loss is small in percent but corresponds to a multi-gram-per-litre decrease over 52 s.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 6220
'''Aqueous phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('aqueous')
quant.plot(title='Aqueous Phase Mass Density @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Aqueous phase mass density history at start-up.')
Figure 4: Aqueous phase mass density history at start-up.
tbl_count += 1
print(f'Table {tbl_count}: Aqueous phase mass density history at start-up.')
print('Time [s] Aqueous Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 2: Aqueous phase mass density history at start-up.
Time [s] Aqueous Phase Mass Density [g/L]
0.000000 992.08
17.201342 1026.95
34.402683 1049.01
51.604025 1062.46
Name: Aqueous Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.5.3. Vapor Phase Results#
'''Plot vapor phase'''
# TODO: time axis normalized by phase flow residence time.
stg.vapor_phase.plot(title='Vapor Phase Start-Up', legend='Vapor Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Vapor phase species history dashboard start-up.')
Figure 5: Vapor phase species history dashboard start-up.
if cortix_ai:
issues = '+ Title your reponse as: Overview of the Vapor Phase Data at Start-Up.'
cortix_ai.explain(phase=stg.vapor_phase, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of the Vapor Phase Data at Start-Up.
Summary of trends
All three vapor species (H₂O(v), N₂(v), O₂(v)) increase monotonically over the 0–51.604 s interval; H₂O(v) shows the largest relative growth while remaining the smallest in absolute concentration.
The total vapor concentration rises from 1.086834 g/L to 1.158577 g/L (≈ +6.60%), indicating a net increase in vapour-phase mass during start-up.
Quantitative comparisons (initial → final)
H₂O(v): 0.0178756 g/L → 0.0261323 g/L; Δ = +0.0082567 g/L (≈ +46.2%).
N₂(v): 0.817538 g/L → 0.876683 g/L; Δ = +0.059145 g/L (≈ +7.24%).
O₂(v): 0.251420 g/L → 0.255762 g/L; Δ = +0.004342 g/L (≈ +1.73%).
Total vapor: 1.086834 g/L → 1.158577 g/L; Δ = +0.071744 g/L (≈ +6.60%).
Rates and temporal behaviour
Average rate over 0–51.604 s:
H₂O(v): ≈ 1.60 × 10⁻⁴ g·L⁻¹·s⁻¹.
N₂(v): ≈ 1.15 × 10⁻³ g·L⁻¹·s⁻¹.
O₂(v): ≈ 8.41 × 10⁻⁵ g·L⁻¹·s⁻¹.
Total vapor: ≈ 1.39 × 10⁻³ g·L⁻¹·s⁻¹.
Instantaneous increments (per ~3.44 s time steps) show higher early growth rates that decrease with time for each species, consistent with a decelerating approach toward a new steady state.
Composition and relative contributions
Initial mass fractions (≈):
H₂O(v): 1.65%.
N₂(v): 75.25%.
O₂(v): 23.10%.
Final mass fractions (≈):
H₂O(v): 2.25%.
N₂(v): 75.65%.
O₂(v): 22.09%.
Interpretation: H₂O(v) increases its share noticeably (absolute concentration still small); N₂(v) remains the dominant component and increases slightly in both absolute and fractional terms; O₂(v) rises slightly in absolute concentration but decreases modestly as a fraction of the total.
Concise interpretation
The dataset shows a clear, monotonic rise in vapor-phase concentrations during start-up with decelerating rates, suggesting the system is moving toward a higher but asymptotic vapor concentration (approaching a new equilibrium). H₂O(v) exhibits the largest relative change, while N₂(v) provides the largest absolute contribution to the total vapor.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 5630
'''Vapor phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('vapor')
quant.plot(title='Vapor Phase Mass Density @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Vapor phase mass density history at start-up.')
Figure 6: Vapor phase mass density history at start-up.
tbl_count += 1
print(f'Table {tbl_count}: Vapor phase mass density history at start-up.')
print('Time [s] Vapor Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 3: Vapor phase mass density history at start-up.
Time [s] Vapor Phase Mass Density [g/L]
0.000000 1.09
17.201342 1.12
34.402683 1.14
51.604025 1.16
Name: Vapor Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.5.3.1. Relative Humidity#
'''Compute relative humidity in the vapor phase'''
import matplotlib.pyplot as plt
from solvex import air_relative_humidity
from copy import deepcopy
h2o_vap_pd_series = deepcopy(stg.vapor_phase.df['H2O(v)'])
for idx, rho_h2o in enumerate(h2o_vap_pd_series):
h2o_vap_pd_series.iloc[idx] = air_relative_humidity(stg_temperature, rho_h2o)
rh_pd_series = h2o_vap_pd_series
time_unit = stg.vapor_phase.time_unit
rh_pd_series.name = f'Relative Humidity History [{time_unit}]'
quant = Quantity(name=rh_pd_series.name, latex_name='RH', unit='%', info=f'Relative Humidity History (Vapor Phase) [{time_unit}]')
quant.value = h2o_vap_pd_series
quant.plot(title='Stage Rel. Humidity @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature, 'K','C'),
x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+' ['+quant.unit+']', show=True,
figsize=[10,3])
fig_count += 1
print(f'Figure {fig_count}: Stage relative humidity history at start-up.')
Figure 7: Stage relative humidity history at start-up.
tbl_count += 1
print(f'Table {tbl_count}: Stage relative humidity history at start-up.')
print('Time [s] Rel. Humidity [%]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 4: Stage relative humidity history at start-up.
Time [s] Rel. Humidity [%]
0.000000 35.00
17.201342 44.26
34.402683 48.88
51.604025 51.17
Name: Relative Humidity History [s], dtype: float64
if cortix_ai:
issues = '+ Title your reponse as: Overview of the Relative Humidity Data at Start-Up.'
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h5>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of the Relative Humidity Data at Start-Up
Key facts
The table has three columns: an integer index, time in seconds (time [s]), and the measured Relative Humidity (RH). Although the RH column header shows “[s]”, the dependent quantity unit is given as % and the numeric values (35 → 50.83) are percentages (RH [%]).
Time values are evenly spaced at ≈ 6.88054 s increments (0, 6.88054, 13.7611, …, 48.1638 s).
RH increases monotonically from 35.00% at t = 0 s to 50.8277% at t = 48.1638 s.
Summary statistics (RH, %)
Minimum: 35.0000% at t = 0 s.
Maximum: 50.8277% at t = 48.1638 s.
Range: 15.8277 percentage points.
Mean (arithmetic): 44.9883%.
Standard deviation: population σ ≈ 5.18%; sample s ≈ 5.54%.
Time evolution and interval rates
RH change is largest at the start and decelerates over time, consistent with a system approaching an asymptote.
Interval-by-interval RH increase and instantaneous rate (ΔRH / Δt):
0 → 6.88054 s: ΔRH = 4.4811 → 0.651 %/s
6.88054 → 13.7611 s: ΔRH = 3.3981 → 0.494 %/s
13.7611 → 20.6416 s: ΔRH = 2.5742 → 0.374 %/s
20.6416 → 27.5221 s: ΔRH = 1.9484 → 0.283 %/s
27.5221 → 34.4027 s: ΔRH = 1.4734 → 0.214 %/s
34.4027 → 41.2832 s: ΔRH = 1.1129 → 0.162 %/s
41.2832 → 48.1638 s: ΔRH = 0.8396 → 0.122 %/s
Overall average rate from t = 0 to t = 48.1638 s: (50.8277 − 35.0000) / 48.1638 ≈ 0.329 %/s.
Interpretation
The monotonic rise with decreasing incremental rate indicates the vapor-phase RH is approaching a steady value near ≈ 51%.
The rapid initial increase and slower later changes are characteristic of a first-order approach toward equilibrium; using the initial slope as a rough guide yields a characteristic timescale on the order of 20–30 s.
All table columns have been accounted for: index (record), time (s), and RH (interpreted as % despite the “[s]” label on that column).
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 5801
1.5.4. Overall Stage Efficiency#
Stage efficiency measures how close to chemical equilibrium the system is as a whole. This is a direct result of the reaction relaxation time which is dependent on the mass transfer coefficients of the system. Much more needs to be investigated in this project with various degrees of theory but these results represent the beginning of a solid development.
'''Stage overall efficiency'''
quant = stg.efficiency_history(mean=True)
quant.plot(title='Stage Efficiency @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=True)
fig_count += 1
print(f'Figure {fig_count}: Stage efficiency history at start-up.')
Figure 8: Stage efficiency history at start-up.
tbl_count += 1
print(f'Table {tbl_count}: Stage efficiency history at start-up.')
print('Time [s] (Stage. Eff., +-std) [%]')
time_name = ''
import pandas as pd
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(['',''], axis=1).rename_axis(time_name)
.round(3))
print(df.to_string(max_rows=20, min_rows=20))
Table 5: Stage efficiency history at start-up.
Time [s] (Stage. Eff., +-std) [%]
0.00 15.000 21.603
0.06 18.148 19.824
0.11 21.110 18.304
0.17 23.868 17.030
0.23 26.420 15.966
0.29 28.764 15.069
0.34 30.907 14.289
0.40 32.862 13.584
0.46 34.642 12.925
0.52 36.260 12.291
0.57 37.726 11.672
0.63 39.053 11.066
0.69 40.249 10.470
0.75 41.325 9.889
0.80 42.291 9.324
0.86 43.155 8.779
if cortix_ai:
issues = '+ Title your reponse as: Overview of the Stage Efficiency Data at Start-Up.'
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of the Stage Efficiency Data at Start-Up
Dataset summary
The table contains 8 time samples (0 to 48.1638 s) taken at uniform intervals of 6.88054 s; the dependent quantity is Stage Efficiency (mean, ± std) in percent [%].
Two stage columns are present: column “0” and column “1”. Column “0” increases over time; column “1” decreases over time.
Descriptive statistics (per stage, sample std)
Stage 0 (values: 15.0002, 21.1099, 26.4205, 30.9071, 34.6422, 37.7262, 40.2492, 42.291)
Mean = 31.043 %; sample standard deviation = 9.600 %.
Min = 15.0002 %; Max = 42.291 %; Range = 27.291 percentage points.
Stage 1 (values: 21.6026, 18.3038, 15.9664, 14.2891, 12.9249, 11.6725, 10.4702, 9.32378)
Mean = 14.319 %; sample standard deviation = 4.148 %.
Min = 9.32378 %; Max = 21.6026 %; Range = 12.279 percentage points.
Trends, rates and relative changes
Stage 0 shows a monotonic increase from 15.0002 % at t = 0 s to 42.291 % at t = 48.1638 s.
Average linear increase ≈ +0.567 % per second (slope ≈ 0.5666 %/s).
Relative change = (42.291 − 15.0002)/15.0002 ≈ +181.9 % (increase).
Stage 1 shows a monotonic decrease from 21.6026 % at t = 0 s to 9.32378 % at t = 48.1638 s.
Average linear decrease ≈ −0.255 % per second (slope ≈ −0.2549 %/s).
Relative change = (9.32378 − 21.6026)/21.6026 ≈ −56.9 % (decrease).
Interpretive notes
Stage 0 exhibits larger absolute variability (sample std ≈ 9.6 %) than Stage 1 (sample std ≈ 4.15 %), consistent with a strong upward trend and wider range.
Stage 1 exhibits a steady downward trend with smaller spread around its mean.
Time sampling is uniform (Δt = 6.88054 s) and yields clear, monotonic opposing behaviors for the two stages across the 48.16 s start-up window.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4974
'''Individual reaction efficiency'''
quant = stg.efficiency_history()
quant.plot(title='Reaction Efficiency @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', legend=stg.rxn_mech.reactions, show=True, figsize=[10,3])
fig_count += 1
print(f'Figure {fig_count}: Reaction efficiency history at start-up.')
Figure 9: Reaction efficiency history at start-up.
'''Individual reaction efficiency'''
quant = stg.efficiency_history()
tbl_count += 1
print(f'Table {tbl_count}: Reaction efficiency history at start-up.')
print('Time [min] Rxn Eff. [%]')
col_names = [f'r{i}' for i in range(len(stg.rxn_mech.reactions))]
time_name = ''
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(col_names, axis=1).rename_axis(time_name)
.round(3))
print(df.to_string(max_rows=20, min_rows=20))
Table 6: Reaction efficiency history at start-up.
Time [min] Rxn Eff. [%]
r0 r1 r2 r3 r4 r5 r6 r7 r8
0.00 0.000 0.000 0.000 0.000 0.000 0.000 35.002 50.000 50.000
0.06 6.372 6.507 6.645 2.418 2.445 1.934 37.458 49.910 49.642
0.11 12.135 12.589 13.066 4.736 4.827 3.830 39.608 49.839 49.359
0.17 17.277 18.105 19.026 7.031 7.209 5.751 41.489 49.783 49.140
0.23 21.835 23.048 24.451 9.311 9.590 7.704 43.133 49.741 48.972
0.29 25.843 27.393 29.272 11.579 11.969 9.692 44.570 49.710 48.847
0.34 29.350 31.169 33.491 13.833 14.340 11.711 45.825 49.688 48.758
0.40 32.408 34.418 37.137 16.062 16.691 13.752 46.920 49.674 48.698
0.46 35.066 37.193 40.254 18.256 19.007 15.801 47.876 49.665 48.661
0.52 37.369 39.548 42.895 20.397 21.271 17.840 48.710 49.661 48.644
0.57 39.360 41.537 45.112 22.472 23.464 19.849 49.437 49.662 48.643
0.63 41.076 43.209 46.955 24.464 25.568 21.810 50.072 49.665 48.654
0.69 42.551 44.609 48.473 26.363 27.571 23.705 50.625 49.671 48.675
0.75 43.816 45.774 49.708 28.160 29.459 25.522 51.107 49.678 48.704
0.80 44.898 46.741 50.700 29.848 31.227 27.250 51.528 49.688 48.739
0.86 45.819 47.540 51.486 31.426 32.871 28.882 51.894 49.698 48.779
if cortix_ai:
issues = '+ Title your reponse as: Overview of Individual Reaction Efficiency at Start-Up.'
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of Individual Reaction Efficiency at Start-Up
Dataset and time base
Times are given in seconds: 0, 6.88054, 13.7611, 20.6416, 27.5221, 34.4027, 41.2832, 48.1638. Column headers 0–8 correspond to the ordered reactions (listed below in chemically formatted form). The dependent quantity is reaction efficiency in % measured at each time.
Per-reaction analysis
[C₄H₉O]₃PO(o) + H₂O(a) <-> H₂O·[C₄H₉O]₃PO(o): starts 0%, rises monotonically to 44.90% at 48.16 s (steady increase across all time points).
2 [C₄H₉O]₃PO(o) + 2 H₂O(a) <-> [H₂O]₂·[[C₄H₉O]₃PO]₂(o): starts 0%, rises monotonically to 46.74% at 48.16 s.
3 [C₄H₉O]₃PO(o) + 6 H₂O(a) <-> [H₂O]₆·[[C₄H₉O]₃PO]₃(o): starts 0%, grows to 50.70% at 48.16 s (crosses 50% by the final time point).
H⁺(a) + NO₃⁻(a) + [C₄H₉O]₃PO(o) <-> HNO₃·[C₄H₉O]₃PO(o): starts 0%, increases monotonically to 29.85% at 48.16 s.
H⁺(a) + NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <-> HNO₃·[[C₄H₉O]₃PO]₂(o): starts 0%, increases monotonically to 31.23% at 48.16 s.
UO₂²⁺(a) + 2 NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <-> UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): starts at 35.00%, increases monotonically to 51.53% at 48.16 s (crosses 50% between 34.40 s and 41.28 s).
H₂O(a) <-> H₂O(v): starts 50.00%, shows a slight decrease then stabilizes near 49.66–49.84% (final 49.69%), effectively near-constant.
O₂(v) <-> O₂(a): starts 50.00%, small decline to ~48.64–48.97% and ends at 48.74% (minor net drop ~1.26 percentage points).
N₂(v) <-> N₂(a): starts 50.00%, small decline to ~48.64–48.76% and ends at 48.74% (very slight decrease and minor recovery toward the end).
Summary observations
All reactions show monotonic trends (no reversals or oscillations) over the 0–48 s window: seven reactions increase from their start values, two (gaseous exchange O₂, N₂ and H₂O gas–liquid exchange) remain near 50% with minor decreases.
Reactions with highest final efficiencies: reaction 6 (UO₂ complexation) 51.53% and reaction 2 (trimer hydration complex) 50.70%; both exceed 50% by the last sample.
Reactions 0–2 (simple to higher-order PO species hydration) exhibit the largest absolute increases from 0% to ~45–51% over the measurement period.
Time scale: significant changes occur continuously across the eight measurements (~6.88 s spacing); major crossovers past 50% occur by ~41 s for reaction 6 and only at the final timestamp for reaction 2.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 6907
1.5.5. Reaction Rate Density#
This quantity only makes sense per volume of the mixture.
'''Reaction rate density'''
import matplotlib.pyplot as plt
quant = stg.r_vec_history() # mole/m^3-s
quant.plot(title='Reaction Rate Density @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', legend=stg.rxn_mech.reactions, show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Reaction rate density history at start-up.')
Figure 10: Reaction rate density history at start-up.
tbl_count += 1
print(f'Table {tbl_count}: Reaction rate density history at start-up.')
print('Time [min] r [mM/s]')
import pandas as pd
col_names = [f'r{i}' for i in range(len(stg.rxn_mech.reactions))]
time_name = 't [min]'
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(col_names, axis=1).rename_axis(time_name)
.round(4))
print(df.to_string(max_rows=20, min_rows=20))
Table 7: Reaction rate density history at start-up.
Time [min] r [mM/s]
r0 r1 r2 r3 r4 r5 r6 r7 r8
t [min]
0.00 1.0955 2.6403 0.4865 0.0000 0.0000 0.0000 0.0019 0.0014 0.0024
0.06 0.9819 2.2624 0.3985 0.0029 0.0028 0.0082 0.0018 0.0014 0.0025
0.11 0.8885 1.9663 0.3326 0.0088 0.0080 0.0392 0.0018 0.0014 0.0025
0.17 0.8115 1.7363 0.2831 0.0160 0.0141 0.0919 0.0017 0.0014 0.0025
0.23 0.7473 1.5521 0.2446 0.0235 0.0200 0.1595 0.0017 0.0014 0.0025
0.29 0.6935 1.4047 0.2146 0.0306 0.0255 0.2346 0.0016 0.0014 0.0026
0.34 0.6482 1.2861 0.1909 0.0369 0.0301 0.3103 0.0016 0.0014 0.0026
0.40 0.6099 1.1900 0.1720 0.0424 0.0338 0.3818 0.0016 0.0014 0.0026
0.46 0.5775 1.1118 0.1568 0.0469 0.0367 0.4461 0.0015 0.0014 0.0026
0.52 0.5498 1.0478 0.1445 0.0505 0.0389 0.5018 0.0015 0.0014 0.0026
0.57 0.5262 0.9952 0.1344 0.0534 0.0404 0.5486 0.0015 0.0014 0.0026
0.63 0.5061 0.9518 0.1263 0.0555 0.0414 0.5871 0.0015 0.0014 0.0026
0.69 0.4889 0.9160 0.1196 0.0572 0.0421 0.6184 0.0014 0.0014 0.0026
0.75 0.4742 0.8863 0.1141 0.0584 0.0424 0.6434 0.0014 0.0014 0.0027
0.80 0.4616 0.8617 0.1096 0.0592 0.0425 0.6632 0.0014 0.0014 0.0027
0.86 0.4508 0.8412 0.1059 0.0598 0.0425 0.6789 0.0014 0.0014 0.0027
if cortix_ai:
issues = '+ Title your reponse as: Overview of Reaction Rates Density at Start-Up.'
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of Reaction Rates Density at Start-Up.
Data mapping (column index -> reaction)
Column 0 -> [C₄H₉O]₃PO(o) + H₂O(a) <-> H₂O*[C₄H₉O]₃PO(o)
Column 1 -> 2 [C₄H₉O]₃PO(o) + 2 H₂O(a) <-> [H₂O]₂*[[C₄H₉O]₃PO]₂(o)
Column 2 -> 3 [C₄H₉O]₃PO(o) + 6 H₂O(a) <-> [H₂O]₆*[[C₄H₉O]₃PO]₃(o)
Column 3 -> H⁺(a) + NO₃⁻(a) + [C₄H₉O]₃PO(o) <-> HNO₃*[C₄H₉O]₃PO(o)
Column 4 -> H⁺(a) + NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <-> HNO₃*[[C₄H₉O]₃PO]₂(o)
Column 5 -> UO₂²⁺(a) + 2 NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <-> UO₂[NO₃]₂*[[C₄H₉O]₃PO]₂(o)
Column 6 -> H₂O(a) <-> H₂O(v)
Column 7 -> O₂(v) <-> O₂(a)
Column 8 -> N₂(v) <-> N₂(a)
Column-by-column analysis (values in mM/s; time span 0 -> 48.16 s)
Col 0: 1.09551 -> 0.461584; monotonically decreasing (~−58%), large initial rate that falls toward ~0.46 mM/s.
Col 1: 2.64031 -> 0.861679; monotonically decreasing (~−67%), highest initial rate (2.64 mM/s) but declines substantially.
Col 2: 0.486462 -> 0.109569; monotonically decreasing (~−77%), small-to-moderate magnitude falling toward ~0.11 mM/s.
Col 3: 5.383×10⁻⁶ -> 0.059211; rises from essentially zero to ~5.9×10⁻² mM/s (very large relative increase), monotonic growth over the period.
Col 4: 5.287×10⁻⁶ -> 0.042505; similar pattern to Col 3, rising to ~4.25×10⁻² mM/s and showing signs of slowing increase near the end.
Col 5: 1.106×10⁻⁶ -> 0.663201; dramatic growth from ≈0 to ~0.66 mM/s (largest relative increase), monotonic rise throughout.
Col 6: 0.00192554 -> 0.00141653; small-magnitude values with a gradual decrease (~−26%), near-constant on the order of 10⁻³ mM/s.
Col 7: 0.00139164 -> 0.00142364; very small-magnitude, slight monotonic increase (~+2.3%), essentially steady at ~1.4×10⁻³ mM/s.
Col 8: 0.00242574 -> 0.00265994; small-magnitude, modest monotonic increase (~+9.7%), steady near 2.6×10⁻³ mM/s.
Key takeaways
Two distinct groups emerge: (a) reactions in Cols 0–2 start large and decrease over time, indicating consumption or relaxation of initially fast processes; (b) reactions in Cols 3–5 start near zero and increase substantially, indicating formation/activation during start-up (Col 5 grows most strongly to ~0.66 mM/s).
Gas–phase exchange reactions (Cols 6–8) remain small (10⁻³ mM/s) and show only modest changes, with Col 6 slowly decreasing and Cols 7–8 slightly increasing.
By the last recorded time (48.16 s) some increasing reactions (Cols 3–5) appear to be slowing (especially Col 4), while decreasing reactions (Cols 0–2) are still trending downward but with diminishing step size, suggesting approach toward new steady behavior on the timescale shown.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 6904
1.5.6. Species Generation Rate Density#
This quantity is on the basis of mixing volume so all species generation values can be compared.
'''Species generation rate density'''
import matplotlib.pyplot as plt
quant = stg.g_vec_history() # mole/m^3-s
quant.plot(title='Species Generation Rate Density @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', legend=stg.rxn_mech.species_names, show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Species generation rate density history at start-up.')
Figure 11: Species generation rate density history at start-up.
tbl_count += 1
print(f'Table {tbl_count}: Species generation rate density history at start-up.')
print('Time [min] g [mM/s]')
import pandas as pd
col_names = [f'a{i}' for i in range(len(stg.rxn_mech.species_names))]
time_name = 't [min]'
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(col_names, axis=1).rename_axis(time_name)
.round(4))
print(df.to_string(max_rows=20, min_rows=20))
Table 8: Species generation rate density history at start-up.
Time [min] g [mM/s]
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15
t [min]
0.00 -9.2968 0.0019 1.0955 0.0000 0.0000 -0.0000 0.0024 -0.0024 -0.0000 0.0014 -0.0014 0.0000 -0.0000 -7.8355 2.6403 0.4865
0.06 -7.8993 0.0018 0.9819 0.0029 0.0028 -0.0057 0.0025 -0.0025 -0.0220 0.0014 -0.0014 0.0082 -0.0082 -6.7269 2.2624 0.3985
0.11 -6.8181 0.0018 0.8885 0.0088 0.0080 -0.0169 0.0025 -0.0025 -0.0953 0.0014 -0.0014 0.0392 -0.0392 -5.9219 1.9663 0.3326
0.17 -5.9842 0.0017 0.8115 0.0160 0.0141 -0.0301 0.0025 -0.0025 -0.2138 0.0014 -0.0014 0.0919 -0.0919 -5.3612 1.7363 0.2831
0.23 -5.3208 0.0017 0.7473 0.0235 0.0200 -0.0435 0.0025 -0.0025 -0.3626 0.0014 -0.0014 0.1595 -0.1595 -4.9679 1.5521 0.2446
0.29 -4.7923 0.0016 0.6935 0.0306 0.0255 -0.0560 0.0026 -0.0026 -0.5252 0.0014 -0.0014 0.2346 -0.2346 -4.6974 1.4047 0.2146
0.34 -4.3675 0.0016 0.6482 0.0369 0.0301 -0.0670 0.0026 -0.0026 -0.6876 0.0014 -0.0014 0.3103 -0.3103 -4.5109 1.2861 0.1909
0.40 -4.0236 0.0016 0.6099 0.0424 0.0338 -0.0762 0.0026 -0.0026 -0.8398 0.0014 -0.0014 0.3818 -0.3818 -4.3797 1.1900 0.1720
0.46 -3.7433 0.0015 0.5775 0.0469 0.0367 -0.0836 0.0026 -0.0026 -0.9758 0.0014 -0.0014 0.4461 -0.4461 -4.2840 1.1118 0.1568
0.52 -3.5136 0.0015 0.5498 0.0505 0.0389 -0.0894 0.0026 -0.0026 -1.0929 0.0014 -0.0014 0.5018 -0.5018 -4.2106 1.0478 0.1445
0.57 -3.3247 0.0015 0.5262 0.0534 0.0404 -0.0938 0.0026 -0.0026 -1.1910 0.0014 -0.0014 0.5486 -0.5486 -4.1513 0.9952 0.1344
0.63 -3.1687 0.0015 0.5061 0.0555 0.0414 -0.0970 0.0026 -0.0026 -1.2713 0.0014 -0.0014 0.5871 -0.5871 -4.1012 0.9518 0.1263
0.69 -3.0396 0.0014 0.4889 0.0572 0.0421 -0.0992 0.0026 -0.0026 -1.3360 0.0014 -0.0014 0.6184 -0.6184 -4.0575 0.9160 0.1196
0.75 -2.9326 0.0014 0.4742 0.0584 0.0424 -0.1007 0.0027 -0.0027 -1.3875 0.0014 -0.0014 0.6434 -0.6434 -4.0188 0.8863 0.1141
0.80 -2.8438 0.0014 0.4616 0.0592 0.0425 -0.1017 0.0027 -0.0027 -1.4281 0.0014 -0.0014 0.6632 -0.6632 -3.9843 0.8617 0.1096
0.86 -2.7700 0.0014 0.4508 0.0598 0.0425 -0.1023 0.0027 -0.0027 -1.4600 0.0014 -0.0014 0.6789 -0.6789 -3.9534 0.8412 0.1059
'''Produced species generation rate density'''
import matplotlib.pyplot as plt
ids, quant_produced = stg.g_vec_history(produced=True) # mole/m^3-s
quant_produced.plot(title='Produced Species Generation Rate Density @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant_produced.unit+']', legend=list(np.array(stg.rxn_mech.species_names)[ids]), show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Produced species generation rate density history at start-up.')
Figure 12: Produced species generation rate density history at start-up.
if cortix_ai:
issues = '+ Title your reponse as: Overview of Species Generation Rates Density at Start-Up.'
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of Species Generation Rates Density at Start-Up.
Key points
Time series spans t = 0 … 48.1638 s with generation rates in mM/s for 16 ordered species (indices 0–15).
Several species show persistent production (positive rates) or consumption (negative rates); most rates evolve monotonically over the recorded interval.
Largest magnitudes: H₂O(a) and [C₄H₉O]₃PO(o) are the dominant consumption terms; UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o) and its counterpart UO₂²⁺(a) show large, opposite-signed changes, indicating a strong coupling.
Per-species time-evolution (mM/s)
H₂O(a): starts −9.29682, rises (less negative) monotonically to −2.84377 at 48.16 s — strong net consumption whose magnitude decreases with time.
H₂O(v): starts 0.00192554, decreases slowly to 0.00141653 — small positive vapor production, slightly decaying.
H₂O·[C₄H₉O]₃PO(o): starts 1.09551, decreases monotonically to 0.461584 — positive production declining over time.
HNO₃·[C₄H₉O]₃PO(o): near 0 at t=0 (5.38e-06), increases monotonically to 0.0592107 — growing production.
HNO₃·[[C₄H₉O]₃PO]₂(o): near 0 at t=0 (5.29e-06), increases to 0.0425051 — similar increasing production (smaller than the single adduct).
H⁺(a): near 0 at t=0 (−1.07e-05), becomes more negative to −0.101716 — increasing net consumption of H⁺ over time.
N₂(a): small positive, 0.00242574 -> 0.00265994, slight monotonic increase (production).
N₂(v): equal-and-opposite to N₂(a), −0.00242574 -> −0.00265994 (vapor sink mirrors aqueous source).
NO₃⁻(a): −1.288e-05 -> −1.42812, monotonic increase in consumption magnitude (notable growth in sink).
O₂(a): 0.00139164 -> 0.00142364, small positive slowly increasing (production).
O₂(v): −0.00139164 -> −0.00142364, equal-and-opposite to O₂(a) (vapor/aqueous exchange).
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): 1.1059e-06 -> 0.663201, marked monotonic production (large increase).
UO₂²⁺(a): −1.1059e-06 -> −0.663201, mirror consumption corresponding to the UO₂ complex formation.
[C₄H₉O]₃PO(o): −7.83553 -> −3.98427, large consumption that weakens (less negative) over time.
[H₂O]₂·[[C₄H₉O]₃PO]₂(o): 2.64031 -> 0.861679, positive production decreasing substantially.
[H₂O]₆·[[C₄H₉O]₃PO]₃(o): 0.486462 -> 0.109569, positive production decreasing to a small value.
Extrema and notable couplings
Largest absolute negative rates at t=0: H₂O(a) (−9.30) and [C₄H₉O]₃PO(o) (−7.84). By t≈48 s both approach ~−3 to −4 mM/s.
UO₂ complex (index 11) and UO₂²⁺(index 12) form a strongly coupled pair with nearly equal and opposite rates, indicating transfer/complexation converting free UO₂²⁺ into the solvate complex.
Vapor/aqueous pairs N₂(a)/N₂(v) and O₂(a)/O₂(v) show small, equal-and-opposite rates (exchange between phases).
NO₃⁻(a) consumption grows steadily and becomes a non-negligible sink (≈−1.43 mM/s by 48 s).
H⁺(a) becomes increasingly consumed (more negative), suggesting acid removal or neutralization processes increase over time.
Concise interpretation
The start-up interval shows a transition from stronger initial consumption of bulk species (notably H₂O(a) and [C₄H₉O]₃PO(o)) toward diminishing consumption magnitudes, while complexation (UO₂ complex formation) and nitrate consumption ramp up. Small vapor–aqueous exchanges (H₂O, N₂, O₂) persist with near-constant, low-magnitude rates. Overall dynamics are monotonic for almost all recorded species over the interval.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 7453
1.5.7. Mass Balance Residual#
This is actually the total mass generation rate density (mixing volume) residual; a small number in view of total mass conservation.
'''Mass balance residuals'''
import matplotlib.pyplot as plt
quant = stg.mass_balance_residual_history()
quant.plot(title='Total Mass Generation Rate Density @ %2.1f C Start-Up'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', markers_only=True, legend=['Residual'], show=True, figsize=[10,3])
fig_count += 1
print(f'Figure {fig_count}: Total mass generation rate density residual history at start-up.')
Figure 13: Total mass generation rate density residual history at start-up.
if cortix_ai:
issues = '+ Title your reponse as: Total Mass Generation Rate Density Residual at Start-Up.'
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Total Mass Generation Rate Density Residual at Start-Up.
Summary
The table reports the total mass generation rate density (unit: g/L-s) sampled at eight uniformly spaced times during start-up (0 to 48.1638 s). The reported residuals are numerically very small (≈10⁻¹⁶ g/L-s).
Rows: 8 samples (index 0–7)
Time column: 0.0000 s to 48.1638 s, uniform step 6.88054 s
Dependent column: Total Mass Generation Rate Density [g/L-s], values near zero (both negative and one exactly 0)
Exact values inspected
time [s]: 0, 6.88054, 13.7611, 20.6416, 27.5221, 34.4027, 41.2832, 48.1638
Residuals [g/L-s]: -9.72953e-17, -3.64292e-17, -1.17961e-16, -6.93889e-17, -1.11022e-16, -2.498e-16, 0, -8.32667e-17
Descriptive statistics (all samples)
Count: 8
Minimum: -2.4980e-16 g/L-s (largest magnitude residual)
Maximum: 0.0000e+00 g/L-s
Mean: -9.56e-17 g/L-s (≈ -9.6×10⁻¹⁷)
Median: -9.03e-17 g/L-s (average of central pair)
Standard deviation: ≈ 6.9e-17 g/L-s
Interpretation
All reported residuals are on the order of 10⁻¹⁶ g/L-s, i.e., effectively zero at typical numerical precision; signs alternate negative with one exact zero in the dataset.
The minimum value (-2.498e-16 g/L-s) sets the largest observed deviation from zero; the spread (std ≈ 6.9e-17 g/L-s) quantifies the small numerical scatter about the mean.
Time sampling is uniform with Δt = 6.88054 s, covering ~48.16 s of start-up history.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 5304
1.6. Steady-State Simulation#
Pick up from where it left from the past run() and continue to a longer time. This demonstrates how to continue a simulation from where it was interrupted. Note that the state of the system is automatically used as the initial condition for the next run().
end_time += 5 * stg.flow_residence_time_avg
time_step = 5 * stg.flow_residence_time_avg / 15
show_time = (True, 10*time_step)
stg.time_step = time_step
stg.initial_time = stg.end_time
stg.end_time = end_time
stg.show_time = show_time
'''Run system in parallel'''
stg.monitor_mass_flowrates = False
stg.monitor_mass_conservation_residual = False
system.run()
system.close() # Shutdown Cortix
[14410] 2025-12-02 14:10:18,229 - cortix - INFO - Launching Module <solvex.stage.Stage object at 0x7fba0fe1ead0>
[16691] 2025-12-02 14:10:20,025 - cortix - INFO - Stg-1::run():time[m]=0.9
[16691] 2025-12-02 14:10:20,652 - cortix - INFO - Stg-1::run():time[m]=3.7
Total mass rate density (mixture volume) residual [g/L-s]= -2.77556e-17
total mass inflow rate [g/min] = 7.419e+02
total mass outflow rate [g/min] = 7.417e+02
net total mass flow rate [g/min] = -1.590e-01
[16691] 2025-12-02 14:10:20,852 - cortix - INFO - Stg-1::run():time[m]=5.2 (et[s]=0.8)
[14410] 2025-12-02 14:10:21,074 - cortix - INFO - run()::Elapsed wall clock time [s]: 930.82
[14410] 2025-12-02 14:10:21,074 - cortix - INFO - Closed Cortix object.
_____________________________________________________________________________
T E R M I N A T I N G
_____________________________________________________________________________
... s . (TAAG Fraktur)
xH88"`~ .x8X :8 @88>
:8888 .f"8888Hf u. .u . .88 %8P uL ..
:8888> X8L ^""` ...ue888b .d88B :@8c :888ooo . .@88b @88R
X8888 X888h 888R Y888r ="8888f8888r -*8888888 .@88u ""Y888k/"*P
88888 !88888. 888R I888> 4888>"88" 8888 888E` Y888L
88888 %88888 888R I888> 4888> " 8888 888E 8888
88888 `> `8888> 888R I888> 4888> 8888 888E `888N
`8888L % ?888 ! u8888cJ888 .d888L .+ .8888Lu= 888E .u./"888&
`8888 `-*"" / "*888*P" ^"8888*" ^%888* 888& d888" Y888*"
"888. :" "Y" "Y" "Y" R888" ` "Y Y"
`""***~"` ""
https://cortix.org
_____________________________________________________________________________
[14410] 2025-12-02 14:10:21,075 - cortix - INFO - close()::Elapsed wall clock time [s]: 930.82
'''Recover stage'''
stg = system_net.modules[0]
1.6.1. Organic Phase Results#
'''Plot organic phase'''
# TODO: time axis normalized by phase flow residence time.
stg.organic_phase.plot(title='Organic Phase Steady-State', legend='Organic Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Organic phase species history dashboard at steady-state.')
Figure 14: Organic phase species history dashboard at steady-state.
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Organic Phase Data at Steady-State.\n'
'+ Do not explain the start-up process; cover the steady-state only.'
)
cortix_ai.explain(phase=stg.organic_phase, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview
The table gives concentrations (g·L⁻¹) vs time (s) for an organic phase initially containing [C₄H₉O]₃PO(o) and forming several associated species: H₂O·[C₄H₉O]₃PO(o), HNO₃·[C₄H₉O]₃PO(o), HNO₃·[[C₄H₉O]₃PO]₂(o), UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o), [H₂O]₂·[[C₄H₉O]₃PO]₂(o), and [H₂O]₆·[[C₄H₉O]₃PO]₃(o). Time points span 0 → 309.624 s. Initial [C₄H₉O]₃PO(o) = 291.75 g·L⁻¹; all other species start at 0.
Major net change: [C₄H₉O]₃PO(o) decreases monotonically to 191.977 g·L⁻¹ (Δ = −99.773 g·L⁻¹, ≈ −34.2%).
All listed associated species increase from zero and approach quasi‑steady values by the end of the record.
Time evolution by column (each species)
H₂O·[C₄H₉O]₃PO(o):
Rises rapidly at early times, from 0 to ≈9–11 g·L⁻¹, then approaches ~11.0 g·L⁻¹ by ~100 s and stays near that value to 309 s.
HNO₃·[C₄H₉O]₃PO(o):
Grows from 0 to ≈1.95 g·L⁻¹, with most growth in the first ~150 s; slow approach to ≈1.95–1.96 by 309 s.
HNO₃·[[C₄H₉O]₃PO]₂(o):
Increases to ≈2.29 g·L⁻¹, with a similar early rise and slow tailing off toward steady value.
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o):
Largest absolute increase among new species: reaches ≈68.40 g·L⁻¹ by 309 s. Growth is faster after ~10–50 s and continues to climb (slower) through the dataset.
[C₄H₉O]₃PO(o):
Monotonic decrease from 291.75 to 191.977 g·L⁻¹; fastest drop occurs in the first ~50 s (291.75 → ~215.5 g·L⁻¹), then slower decline to final value.
[H₂O]₂·[[C₄H₉O]₃PO]₂(o):
Rapid initial accumulation to ~40–44 g·L⁻¹, then slight decline/settling to ~42.08 g·L⁻¹ by 309 s.
[H₂O]₆·[[C₄H₉O]₃PO]₃(o):
Grows to ~8.14 g·L⁻¹, with most increase early and small decrease from a slight peak (≈10.23 g·L⁻¹ around 44–48 s) down to ~8.14 at 309 s.
Comparative magnitudes and ordering (final time = 309.624 s)
Final concentrations (approx, descending):
[C₄H₉O]₃PO(o): 191.98 g·L⁻¹
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): 68.40 g·L⁻¹
[H₂O]₂·[[C₄H₉O]₃PO]₂(o): 42.08 g·L⁻¹
H₂O·[C₄H₉O]₃PO(o): 11.01 g·L⁻¹
[H₂O]₆·[[C₄H₉O]₃PO]₃(o): 8.14 g·L⁻¹
HNO₃·[[C₄H₉O]₃PO]₂(o): 2.29 g·L⁻¹
HNO₃·[C₄H₉O]₃PO(o): 1.95 g·L⁻¹
Relative observations:
The UO₂-containing complex is the dominant newly formed species by mass.
Water‑containing ligand complexes ([H₂O]₂·… and H₂O·…) together account for a substantial fraction of the material redistributed from the free ligand pool.
Nitrate/HNO₃ complexes remain minor in absolute concentration compared with water‑ and UO₂‑bound forms.
Rates and timescales
Most conversion of free [C₄H₉O]₃PO(o) occurs early (first ~50–100 s); thereafter changes slow and values approach quasi‑steady levels.
H₂O·[C₄H₉O]₃PO(o), HNO₃ complexes, and the H₂O‑aggregate species reach near‑steady values earlier (~50–150 s) than the UO₂ complex, which continues appreciable growth beyond 100 s.
The monotonic trends (no reversals) indicate net forward formation of the associated species over the recorded timescale.
Notes on stoichiometry and apparent mass balance
Direct summation of formed complexes vs the loss of free [C₄H₉O]₃PO(o) is not directly interpretable without accounting for the stoichiometry of each complex (several complexes incorporate one, two or three ligand units). For example, species labeled with “…[[C₄H₉O]₃PO]₂” consume two ligand units per complex, so their mass contribution must be weighted accordingly to perform a ligand mass balance.
The observed concentrations are consistent with ligand consumption and redistribution among multiple complex types, with UO₂ complexes and water‑associated aggregates accounting for the largest shares by mass at the end of the run.
Key concise conclusions
Free ligand [C₄H₉O]₃PO(o) falls by ≈100 g·L⁻¹ (≈34%) over 309.6 s.
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o) becomes the largest new sink (~68.4 g·L⁻¹).
Water‑containing complexes (especially [H₂O]₂·[[C₄H₉O]₃PO]₂(o)) also accumulate substantially (~42.1 g·L⁻¹).
HNO₃‑bound species remain minor (~2 g·L⁻¹ each or less).
Most conversion occurs within the first 50–150 s; thereafter the system approaches quasi‑steady concentrations.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 7489
'''Organic phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('organic')
quant.plot(title='Organic Phase Mass Density @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+r'$-\rho_\text{diluent}$'
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Organic phase mass density history at steady-state.')
Figure 15: Organic phase mass density history at steady-state.
tbl_count += 1
print(f'Table {tbl_count}: Organic phase mass density history at steady-state.')
print('Time [s] Organic Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 9: Organic phase mass density history at steady-state.
Time [s] Organic Phase Mass Density [g/L]
0.000000 291.75
17.201342 296.10
34.402683 301.65
51.604025 307.60
137.610734 322.35
223.617442 325.28
309.624151 325.84
Name: Organic Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.6.2. Aqueous Phase Results#
'''Plot aqueous phase'''
# TODO: time axis normalized by phase flow residence time.
stg.aqueous_phase.plot(title='Aqueous Phase Steady-State', legend='Aqueous Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Aqueous phase species history dashboard at steady-state.')
Figure 16: Aqueous phase species history dashboard at steady-state.
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Aqueous Phase Data at Steady-State.\n'
'+ Do not explain the start-up process; cover the steady-state only.'
)
cortix_ai.explain(phase=stg.aqueous_phase, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview
The table records concentrations (g/L) vs time (s) for H₂O(a), H⁺(a), N₂(a), NO₃⁻(a), O₂(a) and UO₂²⁺(a) from 0 to 309.624 s; several species change markedly (NO₃⁻, UO₂²⁺, H⁺) while others remain nearly constant (N₂, O₂, H₂O within ≲0.6% of initial).
Major trends by species
H₂O(a): 992 → 986.797 g/L (net −5.203 g/L). Larger drop occurs early (to ~986.42 by 51.6 s), then small recovery; overall almost constant compared with strong solute changes.
H⁺(a): 0.00100739 → 0.490639 g/L (monotonic increase; large relative rise).
N₂(a): 0.00815443 → 0.00905861 g/L (very small, steady increase).
NO₃⁻(a): 0.0620055 → 19.2306 g/L (rapid, large accumulation; most growth by ~50 s, then slows).
O₂(a): 0.0053437 → 0.00548158 g/L (negligible, slight monotonic increase).
UO₂²⁺(a): 0 → 85.7313 g/L (strong, monotonic accumulation; major fraction accumulated early).
Quantitative rates and relative changes (0 → 309.624 s)
H₂O(a): Δ = −5.203 g/L, mean rate ≈ −0.01680 g·L⁻¹·s⁻¹, relative change ≈ −0.525%.
H⁺(a): Δ = +0.489632 g/L, mean rate ≈ +0.001582 g·L⁻¹·s⁻¹, increase ≈ 487× (factor).
N₂(a): Δ = +0.00090418 g/L, mean rate ≈ +2.92×10⁻⁶ g·L⁻¹·s⁻¹, ≈ +11.1%.
NO₃⁻(a): Δ = +19.1686 g/L, mean rate ≈ +0.06191 g·L⁻¹·s⁻¹, increase ≈ 310× (factor).
O₂(a): Δ = +0.00013788 g/L, mean rate ≈ +4.45×10⁻⁷ g·L⁻¹·s⁻¹, ≈ +2.6%.
UO₂²⁺(a): Δ = +85.7313 g/L, mean rate ≈ +0.27706 g·L⁻¹·s⁻¹, absolute accumulation is the largest observed.
Temporal pattern and correlations
NO₃⁻(a) and UO₂²⁺(a) show strong positive, monotonic increases and similar timing: by 51.6 s NO₃⁻ ≈ 15.16 g/L (≈79% of final) and UO₂²⁺ ≈ 60.56 g/L (≈71% of final). This indicates fast early accumulation followed by slowing toward the end of the record.
H⁺(a) growth parallels NO₃⁻ and UO₂²⁺ qualitatively (also rapid then slowing), suggesting linked processes (acidification accompanying nitrate/uranium appearance).
N₂(a) and O₂(a) remain nearly constant (small monotonic rises), so they are effectively inert on this timescale compared with NO₃⁻ and UO₂²⁺.
H₂O(a) changes are small and show minor nonmonotonicity (early decrease then slight rebound), so bulk solvent mass is essentially conserved with modest redistribution.
Notable points / concise summary
The dominant dynamics are production/accumulation of NO₃⁻(a) and UO₂²⁺(a) (final concentrations ~19.23 and 85.73 g/L), with H⁺(a) rising in parallel (final ~0.491 g/L).
N₂(a) and O₂(a) vary negligibly; H₂O(a) shows a small net decrease (~0.5%).
Most of the NO₃⁻ and UO₂²⁺ accumulation occurs in the first ~50 s, after which rates decrease, indicating a fast initial process followed by approach toward slower growth or saturation.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 6854
'''Aqueous phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('aqueous')
quant.plot(title='Aqueous Phase Mass Density @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Aqueous phase mass density history at Steady-State.')
Figure 17: Aqueous phase mass density history at Steady-State.
tbl_count += 1
print(f'Table {tbl_count}: Aqueous phase mass density history at steady-state.')
print('Time [s] Aqueous Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 10: Aqueous phase mass density history at steady-state.
Time [s] Aqueous Phase Mass Density [g/L]
0.000000 992.08
17.201342 1026.95
34.402683 1049.01
51.604025 1062.46
137.610734 1086.97
223.617442 1091.43
309.624151 1092.26
Name: Aqueous Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.6.3. Vapor Phase Results#
'''Plot vapor phase'''
# TODO: time axis normalized by phase flow residence time.
stg.vapor_phase.plot(title='Vapor Phase Steady-State', legend='Vapor Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Vapor phase species history dashboard at steady-state.')
Figure 18: Vapor phase species history dashboard at steady-state.
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Vapor Phase Data at Steady-State.\n'
'+ Do not explain the start-up process; cover the steady-state only.'
)
cortix_ai.explain(phase=stg.vapor_phase, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of the Vapor Phase Data at Steady-State.
Steady-state window
Measured points used for the steady-state analysis: t ≥ 120.409 s (12 samples: 120.409 … 309.624 s).
Summary of the selected window and sample count.
Quantitative summary (per species)
H₂O(v): mean ≈ 0.0271940 g/L; min = 0.0271717 g/L; max = 0.0272034 g/L; range = 3.17×10⁻⁵ g/L (≈ 0.12% of the mean).
N₂(v): mean ≈ 0.90623 g/L; min = 0.900343 g/L; max = 0.908398 g/L; range = 8.055×10⁻³ g/L (≈ 0.89% of the mean).
O₂(v): mean ≈ 0.257775 g/L; min = 0.257380 g/L; max = 0.257921 g/L; range = 5.41×10⁻⁴ g/L (≈ 0.21% of the mean).
Comparative observations
Abundance ordering (mean values): N₂(v) (≈0.906 g/L) > O₂(v) (≈0.258 g/L) > H₂O(v) (≈0.0272 g/L).
Ratios (approx., using means): N₂(v)/O₂(v) ≈ 3.52; N₂(v)/H₂O(v) ≈ 33.3; O₂(v)/H₂O(v) ≈ 9.48.
Relative stability: H₂O(v) shows the smallest relative variability (~0.12%), O₂(v) intermediate (~0.21%), and N₂(v) the largest (~0.89%) within the steady window.
Stability and trend notes
All three species display only small incremental changes across the steady window; N₂(v) and O₂(v) show a gentle upward drift, while H₂O(v) is essentially flat with tiny fluctuations.
Magnitudes of changes are small compared with the mean concentrations (sub-percent level), indicating a near steady-state composition over the analyzed time span.
Concise conclusion
For t ≥ 120.409 s the vapor composition is effectively steady: N₂ dominates the vapor phase, O₂ is intermediate, and H₂O is a minor component; concentration variations are small (≤ ~0.9% for N₂ and ≤ ~0.21% for O₂ and H₂O), consistent with a stable vapor-phase composition.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 7003
'''Vapor phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('vapor')
quant.plot(title='Vapor Phase Mass Density @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Vapor phase mass density history at steady-state.')
Figure 19: Vapor phase mass density history at steady-state.
tbl_count += 1
print(f'Table {tbl_count}: Vapor phase mass density history at steady-state.')
print('Time [s] Vapor Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 11: Vapor phase mass density history at steady-state.
Time [s] Vapor Phase Mass Density [g/L]
0.000000 1.09
17.201342 1.12
34.402683 1.14
51.604025 1.16
137.610734 1.19
223.617442 1.19
309.624151 1.19
Name: Vapor Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.6.3.1. Relative Humidity#
'''Compute relative humidity in the vapor phase'''
import matplotlib.pyplot as plt
from solvex import air_relative_humidity
from copy import deepcopy
h2o_vap_pd_series = deepcopy(stg.vapor_phase.df['H2O(v)'])
for idx, rho_h2o in enumerate(h2o_vap_pd_series):
h2o_vap_pd_series.iloc[idx] = air_relative_humidity(stg_temperature, rho_h2o)
rh_pd_series = h2o_vap_pd_series
time_unit = stg.vapor_phase.time_unit
rh_pd_series.name = f'Relative Humidity History [{time_unit}]'
quant = Quantity(name=rh_pd_series.name, latex_name='RH', unit='%', info=f'Relative Humidity History (Vapor Phase) [{time_unit}]')
quant.value = h2o_vap_pd_series
quant.plot(title='Stage Rel. Humidity @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature, 'K','C'),
x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+' ['+quant.unit+']', show=True,
figsize=[10,3])
fig_count += 1
print(f'Figure {fig_count}: Stage relative humidity history at steady-state.')
Figure 20: Stage relative humidity history at steady-state.
tbl_count += 1
print(f'Table {tbl_count}: Stage relative humidity history at steady-state.')
print('Time [s] Rel. Humidity [%]')
print(quant.value.loc[n_startup::5].apply(lambda x: round(x,2)))
Table 12: Stage relative humidity history at steady-state.
Time [s] Rel. Humidity [%]
17.201342 44.26
34.402683 48.88
51.604025 51.17
137.610734 53.25
223.617442 53.25
309.624151 53.24
Name: Relative Humidity History [s], dtype: float64
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Relative Humidity Data at Steady-State.\n'
'+ Do not explain the start-up process; cover the steady-state only.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h5>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of the Relative Humidity Data at Steady-State.
Steady-state window used: rows 6–10 (time = 103.208 s to 309.624 s).
Key numeric summary (Relative Humidity, unit = %)
Observations (rows 6–10): 53.0882, 53.2622, 53.2546, 53.2422, 53.2363
Mean (arithmetic): 53.2167 %
Minimum: 53.0882 %
Maximum: 53.2622 %
Range (max − min): 0.1740 %
Standard deviation (population): ≈ 0.0649 % (sample std ≈ 0.0726 %)
Coefficient of variation: ≈ 0.122 %
Time-column analysis
Time span covered in steady-state window: 206.416 s (from 103.208 s to 309.624 s)
Mean time of those samples: 206.416 s
Sampling interval between successive steady-state records: 51.604 s (uniform in this window)
Short-term trend / drift (least-squares linear fit over steady-state points)
Estimated slope: ≈ +5.36 × 10⁻⁴ % per s (≈ +1.93 % per hour)
Interpretation of slope: small positive drift; over the 206 s steady window the fitted trend corresponds to a total change of order 0.11–0.15 %, consistent with the observed range.
Concise interpretation
The variable has settled near 53.22 % with very small scatter (± ~0.07 % standard deviation) and only a tiny upward drift over the analyzed steady-state window.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 6014
1.6.4. Overall Stage Efficiency#
'''Stage overall efficiency'''
quant = stg.efficiency_history(mean=True)
quant.plot(title='Stage Efficiency @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=True)
fig_count += 1
print(f'Figure {fig_count}: Stage efficiency history steady-state.')
Figure 21: Stage efficiency history steady-state.
tbl_count += 1
print(f'Table {tbl_count}: Stage efficiency history at steady-state.')
print('Time [s] (Stage. Eff., +-std) [%]')
time_name = ''
import pandas as pd
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(['',''], axis=1).rename_axis(time_name)
.round(3))
print(df.to_string(max_rows=20, min_rows=20))
Table 13: Stage efficiency history at steady-state.
Time [s] (Stage. Eff., +-std) [%]
0.00 15.000 21.603
0.06 18.148 19.824
0.11 21.110 18.304
0.17 23.868 17.030
0.23 26.420 15.966
0.29 28.764 15.069
0.34 30.907 14.289
0.40 32.862 13.584
0.46 34.642 12.925
0.52 36.260 12.291
... ... ...
2.58 50.051 1.830
2.87 50.188 1.636
3.15 50.279 1.523
3.44 50.340 1.457
3.73 50.382 1.419
4.01 50.410 1.396
4.30 50.431 1.382
4.59 50.445 1.374
4.87 50.455 1.368
5.16 50.462 1.364
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Stage Efficiency Data at Steady-State.\n'
'+ Do not explain the start-up process; cover the steady-state only.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of the Stage Efficiency Data at Steady-State.
Steady-state selection
Steady-state is taken from the rows with time ≥ 103.208 s (indices 6–10).
Time column (steady-state portion): 103.208 s, 154.812 s, 206.416 s, 258.02 s, 309.624 s — evenly spaced by 51.604 s.
Column meanings (all columns analyzed)
time [s]: measurement times in seconds.
Column “0”: reported mean stage efficiency [%].
Column “1”: reported standard deviation [%] associated with the mean.
Numerical summary (steady-state: indices 6–10)
Number of steady-state samples: 5.
Mean of the reported means: 50.0444 % (average of 48.9384, 50.0512, 50.3398, 50.4306, 50.462).
Spread of the reported means: population standard deviation ≈ 0.572 % (sample std ≈ 0.639 %); absolute range = 1.5236 % (min 48.9384 %, max 50.462 %).
Average reported standard deviation (column “1”): 1.9016 %.
Final (last time) values: mean = 50.462 %, reported std = 1.36446 %, coefficient of variation ≈ 2.70 % (1.36446 / 50.462).
Observations
The stage mean efficiency has settled near 50.0 % during the selected steady-state window.
The variability reported for each sample decreases across the steady-state samples (from 3.475 % down to 1.364 %), producing an average reported uncertainty of ≈ 1.90 %.
Variability of the mean values themselves is small (population std ≈ 0.572 %), indicating a stable plateau with only minor residual drift across the steady-state interval.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4987
'''Individual reaction efficiency'''
quant = stg.efficiency_history()
quant.plot(title='Reaction Efficiency @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', legend=stg.rxn_mech.reactions, show=True, figsize=[10,3])
fig_count += 1
print(f'Figure {fig_count}: Reaction efficiency history at steady-state.')
Figure 22: Reaction efficiency history at steady-state.
'''Individual reaction efficiency'''
quant = stg.efficiency_history()
tbl_count += 1
print(f'Table {tbl_count}: Reaction efficiency history at steady-state.')
print('Time [min] Rxn Eff. [%]')
col_names = [f'r{i}' for i in range(len(stg.rxn_mech.reactions))]
time_name = ''
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(col_names, axis=1).rename_axis(time_name)
.round(3))
print(df.to_string(max_rows=20, min_rows=20))
Table 14: Reaction efficiency history at steady-state.
Time [min] Rxn Eff. [%]
r0 r1 r2 r3 r4 r5 r6 r7 r8
0.00 0.000 0.000 0.000 0.000 0.000 0.000 35.002 50.000 50.000
0.06 6.372 6.507 6.645 2.418 2.445 1.934 37.458 49.910 49.642
0.11 12.135 12.589 13.066 4.736 4.827 3.830 39.608 49.839 49.359
0.17 17.277 18.105 19.026 7.031 7.209 5.751 41.489 49.783 49.140
0.23 21.835 23.048 24.451 9.311 9.590 7.704 43.133 49.741 48.972
0.29 25.843 27.393 29.272 11.579 11.969 9.692 44.570 49.710 48.847
0.34 29.350 31.169 33.491 13.833 14.340 11.711 45.825 49.688 48.758
0.40 32.408 34.418 37.137 16.062 16.691 13.752 46.920 49.674 48.698
0.46 35.066 37.193 40.254 18.256 19.007 15.801 47.876 49.665 48.661
0.52 37.369 39.548 42.895 20.397 21.271 17.840 48.710 49.661 48.644
... ... ... ... ... ... ... ... ... ...
2.58 50.305 50.351 51.105 48.227 48.810 47.639 54.304 49.946 49.774
2.87 50.239 50.261 50.795 48.760 49.197 48.326 54.318 49.961 49.836
3.15 50.181 50.192 50.570 49.128 49.451 48.809 54.323 49.972 49.882
3.44 50.135 50.140 50.408 49.384 49.621 49.151 54.324 49.980 49.915
3.73 50.099 50.101 50.292 49.564 49.737 49.394 54.323 49.986 49.939
4.01 50.072 50.073 50.208 49.690 49.816 49.567 54.322 49.990 49.956
4.30 50.052 50.053 50.149 49.779 49.870 49.690 54.321 49.993 49.969
4.59 50.038 50.038 50.107 49.842 49.908 49.778 54.320 49.995 49.978
4.87 50.027 50.027 50.076 49.887 49.935 49.841 54.319 49.996 49.984
5.16 50.020 50.020 50.055 49.920 49.954 49.886 54.319 49.997 49.988
if cortix_ai:
issues = ('+ Title your reponse as: Overview of Individual Reaction Efficiency at Steady-State'
'+ Do not explain the start-up process; cover the steady-state only.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of Individual Reaction Efficiency at Steady-State+ Do not explain the start-up process; cover the steady-state only.
Steady-state selection and method
Steady-state is taken from the long-time portion of the provided time history (rows at times 103.208 s through 309.624 s). For each reaction (columns 0–8 mapped to the ordered reaction list) I report the mean efficiency across those five late time points and the observed min–max range (all values in %).
Reaction mapping (column index -> reaction)
Column 0: [C₄H₉O]₃PO(o) + H₂O(a) <=> H₂O·[C₄H₉O]₃PO(o)
Column 1: 2 [C₄H₉O]₃PO(o) + 2 H₂O(a) <=> [H₂O]₂·[[C₄H₉O]₃PO]₂(o)
Column 2: 3 [C₄H₉O]₃PO(o) + 6 H₂O(a) <=> [H₂O]₆·[[C₄H₉O]₃PO]₃(o)
Column 3: H⁺(a) + NO₃⁻(a) + [C₄H₉O]₃PO(o) <=> HNO₃·[C₄H₉O]₃PO(o)
Column 4: H⁺(a) + NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <=> HNO₃·[[C₄H₉O]₃PO]₂(o)
Column 5: UO₂²⁺(a) + 2 NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <=> UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o)
Column 6: H₂O(a) <=> H₂O(v)
Column 7: O₂(v) <=> O₂(a)
Column 8: N₂(v) <=> N₂(a)
Steady-state efficiencies (mean across final five time points; min–max)
Column 0 — [C₄H₉O]₃PO + H₂O: 50.1607% (50.0195% – 50.3050%)
Column 1 — 2 [C₄H₉O]₃PO + 2 H₂O: 50.2408% (50.0196% – 50.6408%)
Column 2 — 3 [C₄H₉O]₃PO + 6 H₂O: 50.8817% (50.0546% – 52.6925%)
Column 3 — H⁺ + NO₃⁻ + [C₄H₉O]₃PO: 48.3691% (44.5359% – 49.9196%)
Column 4 — H⁺ + NO₃⁻ + 2 [C₄H₉O]₃PO: 48.8048% (45.7689% – 49.9538%)
Column 5 — UO₂²⁺ + 2 NO₃⁻ + 2 [C₄H₉O]₃PO: 47.9092% (43.1786% – 49.8864%)
Column 6 — H₂O(a) <=> H₂O(v): 54.2640% (54.0526% – 54.3237%)
Column 7 — O₂(v) <=> O₂(a): 49.9553% (49.8605% – 49.9973%)
Column 8 — N₂(v) <=> N₂(a): 49.8138% (49.4232% – 49.9884%)
Compact ranking by steady-state mean (highest -> lowest)
Column 6 (H₂O phase transfer): 54.2640%
Column 2 (3× [C₄H₉O]₃PO + 6 H₂O cluster): 50.8817%
Column 1: 50.2408%
Column 0: 50.1607%
Column 7 (O₂ transfer): 49.9553%
Column 8 (N₂ transfer): 49.8138%
Column 4: 48.8048%
Column 3: 48.3691%
Column 5 (UO₂ complexation): 47.9092%
Concise interpretation
At steady-state the H₂O(a) <=> H₂O(v) exchange (column 6) shows the largest mean efficiency (~54.26%) and the smallest relative spread (<0.3% absolute range), indicating the most consistently higher efficiency among the nine processes.
The multi-molecule cluster formation (column 2) is the next highest (~50.88%) but shows larger variability over the sampled late times (range ~2.64%).
The UO₂ complexation pathway (column 5) and the protonation/nitrate-associated ligand binding pathways (columns 3–5) have the lowest steady-state means (≈47.9%–48.8%) and display the largest min–max spreads, indicating more variability at late times compared with simple phase transfers.
Units and numeric note
All reported values are efficiencies in % (dependent quantity unit: [%]). The means are arithmetic averages over the five final time points shown (times 103.208 s to 309.624 s).
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 7953
1.6.5. Reaction Rate Density#
This quantity only makes sense per volume of the mixture.
'''Reaction rate density'''
import matplotlib.pyplot as plt
quant = stg.r_vec_history()# mole/m^3-s
quant.plot(title='Reaction Rate Density @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', legend=stg.rxn_mech.reactions, show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Reaction rate density history at steady-state.')
Figure 23: Reaction rate density history at steady-state.
tbl_count += 1
print(f'Table {tbl_count}: Reaction rate density history at steady-state.')
print('Time [min] r [mM/s]')
import pandas as pd
col_names = [f'r{i}' for i in range(len(stg.rxn_mech.reactions))]
time_name = 't [min]'
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(col_names, axis=1).rename_axis(time_name)
.round(4))
print(df.iloc[n_startup:].to_string(max_rows=20, min_rows=20))
Table 15: Reaction rate density history at steady-state.
Time [min] r [mM/s]
r0 r1 r2 r3 r4 r5 r6 r7 r8
t [min]
1.15 0.4164 0.7805 0.0953 0.0607 0.0414 0.7194 0.0014 0.0014 0.0027
1.43 0.4006 0.7562 0.0914 0.0605 0.0402 0.7322 0.0013 0.0014 0.0027
1.72 0.3933 0.7464 0.0901 0.0601 0.0395 0.7369 0.0013 0.0014 0.0027
2.01 0.3899 0.7424 0.0897 0.0599 0.0390 0.7388 0.0013 0.0014 0.0027
2.29 0.3883 0.7407 0.0896 0.0597 0.0388 0.7397 0.0013 0.0014 0.0027
2.58 0.3875 0.7400 0.0895 0.0596 0.0386 0.7401 0.0013 0.0014 0.0027
2.87 0.3871 0.7397 0.0895 0.0596 0.0385 0.7402 0.0013 0.0014 0.0027
3.15 0.3869 0.7395 0.0896 0.0596 0.0385 0.7403 0.0013 0.0014 0.0027
3.44 0.3869 0.7394 0.0896 0.0595 0.0385 0.7404 0.0013 0.0014 0.0027
3.73 0.3868 0.7394 0.0896 0.0595 0.0385 0.7404 0.0013 0.0014 0.0027
4.01 0.3868 0.7394 0.0896 0.0595 0.0384 0.7404 0.0013 0.0014 0.0027
4.30 0.3868 0.7394 0.0896 0.0595 0.0384 0.7404 0.0013 0.0014 0.0027
4.59 0.3868 0.7394 0.0896 0.0595 0.0384 0.7404 0.0013 0.0014 0.0027
4.87 0.3868 0.7394 0.0896 0.0595 0.0384 0.7404 0.0013 0.0014 0.0027
5.16 0.3868 0.7394 0.0896 0.0595 0.0384 0.7404 0.0013 0.0014 0.0027
if cortix_ai:
issues = ('+ Title your reponse as: Overview of Reaction Rates Density at Steady-State.\n'
'+ Do not explain the start-up process; cover the steady-state only.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of Reaction Rates Density at Steady-State
Steady-state window
Steady-state is taken from the late-time rows (time ≥ 103.208 s; table rows indexed 6–10). All reported rates are reaction-rate densities in mM/s and are averaged over that window; stability is quantified by the relative range = (max − min)/mean × 100%.
Summary statistics per reaction (steady-state mean ± relative range)
Reaction 1 — [C₄H₉O]₃PO(o) + H₂O(a) <=> H₂O·[C₄H₉O]₃PO(o): 0.388233 mM/s, relative range ≈ 1.68%
Reaction 2 — 2 [C₄H₉O]₃PO(o) + 2 H₂O(a) <=> [H₂O]₂·[[C₄H₉O]₃PO]₂(o): 0.740913 mM/s, relative range ≈ 0.96%
Reaction 3 — 3 [C₄H₉O]₃PO(o) + 6 H₂O(a) <=> [H₂O]₆·[[C₄H₉O]₃PO]₃(o): 0.089671 mM/s, relative range ≈ 0.64%
Reaction 4 — H⁺(a) + NO₃⁻(a) + [C₄H₉O]₃PO(o) <=> HNO₃·[C₄H₉O]₃PO(o): 0.059672 mM/s, relative range ≈ 1.01%
Reaction 5 — H⁺(a) + NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <=> HNO₃·[[C₄H₉O]₃PO]₂(o): 0.038690 mM/s, relative range ≈ 2.67%
Reaction 6 — UO₂²⁺(a) + 2 NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <=> UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): 0.739640 mM/s, relative range ≈ 0.47%
Reaction 7 — H₂O(a) <=> H₂O(v): 0.00132881 mM/s, relative range ≈ 0.80%
Reaction 8 — O₂(v) <=> O₂(a): 0.00142763 mM/s, relative range ≈ 0.02%
Reaction 9 — N₂(v) <=> N₂(a): 0.00269515 mM/s, relative range ≈ 0.13%
Key steady-state observations
The largest steady-state rates are Reaction 2 (mean ≈ 0.7409 mM/s) and Reaction 6 (mean ≈ 0.7396 mM/s), essentially tied and about double Reaction 1 (≈ 0.3882 mM/s).
The smallest rates are gas-phase exchange / transfer reactions (Reactions 7–9) with means between ~1.3×10⁻³ and 2.7×10⁻³ mM/s.
All reactions show small relative changes over the steady window (most ≲ 1%–3%), indicating a stable steady-state; Reaction 5 shows the largest relative variability (~2.7%), still small in absolute terms given its low rate (~0.039 mM/s).
Concise conclusion
At steady-state (time ≥ 103 s) the system displays stable reaction-rate densities: two extraction/complexation-type reactions dominate (~0.74 mM/s), a moderate-rate adsorption/association (Reaction 1) is present (~0.39 mM/s), and gas exchange processes run at orders-of-magnitude lower rates (~10⁻³ mM/s). Overall fluctuations across the steady window are minor (sub–few percent).
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 8626
1.6.6. Species Generation Rate Density#
This quantity is on the basis of mixing volume so all species generation values can be compared.
'''Species generation rate density'''
import matplotlib.pyplot as plt
quant = stg.g_vec_history() # mole/m^3-s
quant.plot(title='Species Generation Rate Density @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', legend=stg.rxn_mech.species_names, show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Species generation rate density history at steady-state.')
Figure 24: Species generation rate density history at steady-state.
tbl_count += 1
print(f'Table {tbl_count}: Species generation rate density history at steady-state.')
print('Time [min] g [mM/s]')
import pandas as pd
col_names = [f'a{i}' for i in range(len(stg.rxn_mech.species_names))]
time_name = 't [min]'
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(col_names, axis=1).rename_axis(time_name)
.round(4))
print(df.iloc[n_startup:].to_string(max_rows=20, min_rows=20))
Table 16: Species generation rate density history at steady-state.
Time [min] g [mM/s]
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15
t [min]
1.15 -2.5505 0.0014 0.4164 0.0607 0.0414 -0.1021 0.0027 -0.0027 -1.5408 0.0014 -0.0014 0.7194 -0.7194 -3.8454 0.7805 0.0953
1.43 -2.4631 0.0013 0.4006 0.0605 0.0402 -0.1007 0.0027 -0.0027 -1.5652 0.0014 -0.0014 0.7322 -0.7322 -3.7928 0.7562 0.0914
1.72 -2.4282 0.0013 0.3933 0.0601 0.0395 -0.0996 0.0027 -0.0027 -1.5734 0.0014 -0.0014 0.7369 -0.7369 -3.7694 0.7464 0.0901
2.01 -2.4141 0.0013 0.3899 0.0599 0.0390 -0.0989 0.0027 -0.0027 -1.5766 0.0014 -0.0014 0.7388 -0.7388 -3.7593 0.7424 0.0897
2.29 -2.4084 0.0013 0.3883 0.0597 0.0388 -0.0985 0.0027 -0.0027 -1.5778 0.0014 -0.0014 0.7397 -0.7397 -3.7550 0.7407 0.0896
2.58 -2.4060 0.0013 0.3875 0.0596 0.0386 -0.0983 0.0027 -0.0027 -1.5784 0.0014 -0.0014 0.7401 -0.7401 -3.7531 0.7400 0.0895
2.87 -2.4050 0.0013 0.3871 0.0596 0.0385 -0.0981 0.0027 -0.0027 -1.5786 0.0014 -0.0014 0.7402 -0.7402 -3.7522 0.7397 0.0895
3.15 -2.4046 0.0013 0.3869 0.0596 0.0385 -0.0981 0.0027 -0.0027 -1.5787 0.0014 -0.0014 0.7403 -0.7403 -3.7518 0.7395 0.0896
3.44 -2.4044 0.0013 0.3869 0.0595 0.0385 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7516 0.7394 0.0896
3.73 -2.4043 0.0013 0.3868 0.0595 0.0385 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7515 0.7394 0.0896
4.01 -2.4043 0.0013 0.3868 0.0595 0.0384 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7515 0.7394 0.0896
4.30 -2.4043 0.0013 0.3868 0.0595 0.0384 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7515 0.7394 0.0896
4.59 -2.4042 0.0013 0.3868 0.0595 0.0384 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7515 0.7394 0.0896
4.87 -2.4042 0.0013 0.3868 0.0595 0.0384 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7515 0.7394 0.0896
5.16 -2.4042 0.0013 0.3868 0.0595 0.0384 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7514 0.7394 0.0896
'''Produced species generation rate density'''
import matplotlib.pyplot as plt
ids, quant_produced = stg.g_vec_history(produced=True) # mole/m^3-s
quant_produced.plot(title='Produced Species Generation Rate Density @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant_produced.unit+']', legend=list(np.array(stg.rxn_mech.species_names)[ids]), show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Produced species generation rate density history at steady-state.')
Figure 25: Produced species generation rate density history at steady-state.
if cortix_ai:
issues = ('+ Title your reponse as: Overview of Species Generation Rates Density at Steady-State.\n'
'+ Do not explain the start-up process; cover the steady-state only.\n'
'+ Identify the groups of produced species and consumed species.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of Species Generation Rates Density at Steady-State
Steady-state snapshot
At steady-state (final reported time t = 309.624 s, where rates have plateaued) the species generation rates [mM/s] from the table are as reported below; positive = net production, negative = net consumption.
Produced species (net positive generation)
H₂O(v): 0.00132637
H₂O·[C₄H₉O]₃PO(o): 0.386769
HNO₃·[C₄H₉O]₃PO(o): 0.0595228
HNO₃·[[C₄H₉O]₃PO]₂(o): 0.038441
N₂(a): 0.00269596
O₂(a): 0.0014277
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): 0.740424
[H₂O]₂·[[C₄H₉O]₃PO]₂(o): 0.739353
[H₂O]₆·[[C₄H₉O]₃PO]₃(o): 0.0895736
Consumed species (net negative generation)
H₂O(a): −2.40424
H⁺(a): −0.0979638
N₂(v): −0.00269596
NO₃⁻(a): −1.57881
O₂(v): −0.0014277
UO₂²⁺(a): −0.740424
[C₄H₉O]₃PO(o): −3.75145
Concise quantitative observations
Dominant net producers (largest positive rates): UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o) ≈ 0.7404 mM/s and [H₂O]₂·[[C₄H₉O]₃PO]₂(o) ≈ 0.7394 mM/s.
Dominant net consumers (largest magnitude negatives): [C₄H₉O]₃PO(o) ≈ −3.7515 mM/s and H₂O(a) ≈ −2.4042 mM/s, followed by NO₃⁻(a) ≈ −1.5788 mM/s.
Clear 1:1 pairing at steady-state: UO₂²⁺(a) consumption (−0.740424 mM/s) matches formation of the UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o) complex (+0.740424 mM/s), indicating net transfer of uranium from the aqueous ionic form into that neutral complex.
Multiple extractant-bound and hydrated species are produced while the free extractant [C₄H₉O]₃PO(o) is strongly consumed, consistent with extraction/complexation and hydration processes reflected in the steady-state rates.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 7287
1.6.7. Mass Balance Residual#
This is actually the total mass generation rate density (mixing volume) residual; a small number in view of total mass conservation.
'''Mass balance residuals'''
import matplotlib.pyplot as plt
quant = stg.mass_balance_residual_history()
quant.plot(title='Total Mass Generation Rate Density @ %2.1f C Steady-State'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', markers_only=True, legend=['Residual'], show=True, figsize=[10,3])
fig_count += 1
print(f'Figure {fig_count}: Total mass generation rate density residual history at steady-state.')
Figure 26: Total mass generation rate density residual history at steady-state.
if cortix_ai:
issues = ('+ Title your reponse as: Total Mass Generation Rate Density Residual at Steady-State.\n'
'+ Do not explain the start-up process; cover the steady-state only.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Total Mass Generation Rate Density Residual at Steady-State.
Dataset summary
Time column: values span 0 s to 309.624 s; later (steady-state) samples occur at 103.208, 154.812, 206.416, 258.02, 309.624 s (approximately 51.604 s spacing).
Dependent quantity (Total Mass Generation Rate Density [g/L-s]): entries are near machine zero, ranging (entire table) from 0 to about -1.66533e-16 g/L-s; units are g/L-s (mass generation rate density in the mixing volume).
Steady-state window and selection
Steady-state window considered: t >= 103.208 s (indices 6–10 in the table). This subset contains 5 samples used for the residual analysis.
Numerical summary for steady-state residuals (t >= 103.208 s)
Values (g/L-s): -5.55112e-17, -1.38778e-16, -2.77556e-17, -2.77556e-17, -2.77556e-17.
Count: 5 samples.
Mean (arithmetic): -5.55112e-17 g/L-s.
Median: -2.77556e-17 g/L-s.
Minimum (most negative): -1.38778e-16 g/L-s.
Maximum (least negative): -2.77556e-17 g/L-s.
Range (max − min): 1.11022e-16 g/L-s.
Population standard deviation: ≈ 4.30e-17 g/L-s.
RMS (root-mean-square): ≈ 7.03e-17 g/L-s.
Interpretation (steady-state)
Residual magnitudes are on the order of 10⁻¹⁶–10⁻¹⁷ g/L-s, i.e., effectively numerical roundoff-level deviations from zero for the mass generation rate density.
The mean residual is slightly negative (≈ −5.55e-17 g/L-s) and the largest individual deviation is −1.38778e-16 g/L-s; these magnitudes indicate mass balance is satisfied to within numerical precision in the steady-state window.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 5681
1.7. Shut-down Simulation#
From steady-state, shutdown in three flow residence times.
end_time += 3 * stg.flow_residence_time_avg
time_step = 3 * stg.flow_residence_time_avg / 20
show_time = (True, 10*time_step)
stg.time_step = time_step
stg.initial_time = stg.end_time
stg.end_time = end_time
stg.show_time = show_time
'''Abrupt turn-off of TBP concentration in the organic phase in the inflow'''
tbp_mass_cc_org = 0.0
stg.inflow_organic_phase.set_value(tbp_org_name, tbp_mass_cc_org)
'''Run system in parallel'''
stg.monitor_mass_flowrates = False
stg.monitor_mass_conservation_residual = False
system.run()
system.close() # Shutdown Cortix
[14410] 2025-12-02 14:18:58,226 - cortix - INFO - Launching Module <solvex.stage.Stage object at 0x7fba0b1ca210>
[18043] 2025-12-02 14:18:59,339 - cortix - INFO - Stg-1::run():time[m]=5.2
[18043] 2025-12-02 14:18:59,718 - cortix - INFO - Stg-1::run():time[m]=6.5
Total mass rate density (mixture volume) residual [g/L-s]= 8.67362e-19
total mass inflow rate [g/min] = 5.668e+02
total mass outflow rate [g/min] = 5.752e+02
net total mass flow rate [g/min] = 8.365e+00
[18043] 2025-12-02 14:19:00,125 - cortix - INFO - Stg-1::run():time[m]=7.7 (et[s]=0.8)
[14410] 2025-12-02 14:19:00,337 - cortix - INFO - run()::Elapsed wall clock time [s]: 1450.08
[14410] 2025-12-02 14:19:00,337 - cortix - INFO - Closed Cortix object.
_____________________________________________________________________________
T E R M I N A T I N G
_____________________________________________________________________________
... s . (TAAG Fraktur)
xH88"`~ .x8X :8 @88>
:8888 .f"8888Hf u. .u . .88 %8P uL ..
:8888> X8L ^""` ...ue888b .d88B :@8c :888ooo . .@88b @88R
X8888 X888h 888R Y888r ="8888f8888r -*8888888 .@88u ""Y888k/"*P
88888 !88888. 888R I888> 4888>"88" 8888 888E` Y888L
88888 %88888 888R I888> 4888> " 8888 888E 8888
88888 `> `8888> 888R I888> 4888> 8888 888E `888N
`8888L % ?888 ! u8888cJ888 .d888L .+ .8888Lu= 888E .u./"888&
`8888 `-*"" / "*888*P" ^"8888*" ^%888* 888& d888" Y888*"
"888. :" "Y" "Y" "Y" R888" ` "Y Y"
`""***~"` ""
https://cortix.org
_____________________________________________________________________________
[14410] 2025-12-02 14:19:00,338 - cortix - INFO - close()::Elapsed wall clock time [s]: 1450.08
'''Recover stage'''
stg = system_net.modules[0]
1.7.1. Organic Phase Results#
'''Plot organic phase'''
# TODO: time axis normalized by phase flow residence time.
stg.organic_phase.plot(title='Organic Phase Shut-Down', legend='Organic Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Organic phase species history dashboard at shut-down.')
Figure 27: Organic phase species history dashboard at shut-down.
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Organic Phase Data at Shut-Down.\n'
f'+ Cover only the period after {round(stg.initial_time,2)} seconds when the concentration of TBP in the inflow is set to zero.'
)
cortix_ai.explain(phase=stg.organic_phase, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of the Organic Phase Data at Shut-Down.
Period analyzed: t ≥ 309.624 s (the moment after which the inflow TBP concentration is set to zero). The following points summarize and compare the behavior of every organic-phase column in that period.
At t = 309.624 s -> t = 464.436 s:
[C₄H₉O]₃PO(o) (TBP): 191.98 g/L -> 9.67065 g/L (−94.9%). Approximate half-life ≈ 31 s (TBP concentration falls rapidly immediately after shut-down).
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): 68.3955 g/L -> 4.02987 g/L (−94.1%). Approximate half-life ≈ 50–55 s (metal complex declines substantially but a bit slower than TBP).
H₂O·[C₄H₉O]₃PO(o): 11.0056 g/L -> 1.33011 g/L (−87.9%). Approximate half-life ≈ 60–62 s (one of the slower relative decays).
HNO₃·[C₄H₉O]₃PO(o): 1.95395 g/L -> 0.228862 g/L (−88.3%). Approximate half-life ≈ 60–63 s (similar timescale to the monohydrate adduct).
HNO₃·[[C₄H₉O]₃PO]₂(o): 2.28548 g/L -> 0.0405566 g/L (−98.2%). Approximate half-life ≈ 30–35 s (very fast relative loss).
[H₂O]₂·[[C₄H₉O]₃PO]₂(o): 42.0769 g/L -> 1.08662 g/L (−97.4%). Approximate half-life ≈ 45–50 s (rapid loss of this dihydrate complex).
[H₂O]₆·[[C₄H₉O]₃PO]₃(o): 8.14239 g/L -> 0.0643521 g/L (−99.2%). Approximate half-life ≈ 30–35 s (the fastest proportional decline).
Immediate post-shutdown change (first recorded step 309.624 → 317.365 s):
TBP shows the largest absolute immediate drop (≈ 19.4% in ~7.7 s), while many associated complexes show smaller first-step changes and then continue to fall. This indicates a rapid removal (or dilution/redistribution) of the free TBP followed by coordinated losses of solvates and metal–TBP complexes.
Comparative ranking by proportional loss over the interval (largest → smallest decrease):
[H₂O]₆·[[C₄H₉O]₃PO]₃(o) ≈ HNO₃·[[C₄H₉O]₃PO]₂(o) > [H₂O]₂·[[C₄H₉O]₃PO]₂(o) > TBP ≈ UO₂-complex > HNO₃·[C₄H₉O]₃PO(o) ≈ H₂O·[C₄H₉O]₃PO(o).
Characteristic timescales (rough, from data):
Fast group (t₁/₂ ≈ 30–35 s): HNO₃·[[C₄H₉O]₃PO]₂(o), [H₂O]₆·[[C₄H₉O]₃PO]₃(o), and TBP (TBP’s absolute fall is fast even though some adducts lag).
Intermediate (t₁/₂ ≈ 45–55 s): [H₂O]₂·[[C₄H₉O]₃PO]₂(o), UO₂ complex.
Slowest (t₁/₂ ≈ 60–65 s): H₂O·[C₄H₉O]₃PO(o), HNO₃·[C₄H₉O]₃PO(o).
Interpretation notes (data-only, concise):
Stopping TBP inflow triggers a rapid depletion of free TBP in the organic phase and a coordinated decline of TBP-associated species (water adducts, nitrate adducts, and UO₂ complexes).
Species that are multiply coordinated (e.g., HNO₃·(TBP)₂, [H₂O]₆·(TBP)₃) show the largest proportional losses and the shortest characteristic times, while monoadducts (HNO₃·TBP, H₂O·TBP) persist proportionally longer.
By the end of the recorded period (≈ 155 s after shut-down), concentrations of all tracked organic-phase species are reduced to single-digit percent levels (or lower) of their pre-shutdown values.
Key takeaways
All organic-phase species decline substantially after TBP inflow is set to zero; proportional losses range from ≈88% to >99% across species by t = 464.436 s.
Characteristic half-lives inferred from the data span roughly 30–65 s, with multiply-ligated adducts typically decaying faster than simple monoadducts.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 11181
'''Organic phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('organic')
quant.plot(title='Organic Phase Mass Density @ %2.1f C Shut-Down'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+r'$-\rho_\text{diluent}$'
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Organic phase mass density history at shut-down.')
Figure 28: Organic phase mass density history at shut-down.
tbl_count += 1
print(f'Table {tbl_count}: Organic phase mass density history at shut-down.')
print('Time [s] Organic Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 17: Organic phase mass density history at shut-down.
Time [s] Organic Phase Mass Density [g/L]
0.000000 291.75
17.201342 296.10
34.402683 301.65
51.604025 307.60
137.610734 322.35
223.617442 325.28
309.624151 325.84
348.327170 159.30
387.030188 75.74
425.733207 35.48
464.436226 16.45
Name: Organic Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.7.2. Aqueous Phase Results#
'''Plot aqueous phase'''
# TODO: time axis normalized by phase flow residence time.
stg.aqueous_phase.plot(title='Aqueous Phase Shut-Down', legend='Aqueous Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Aqueous phase species history dashboard at shut-down.')
Figure 29: Aqueous phase species history dashboard at shut-down.
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Aqueous Phase Data at Shut-Down.\n'
f'+ Cover only the period after {round(stg.initial_time,2)} seconds when the concentration of TBP in the inflow is set to zero.'
)
cortix_ai.explain(phase=stg.aqueous_phase, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of the Aqueous Phase Data at Shut-Down.
The analysis below covers only the data recorded after 309.624 s (the moment the TBP inflow is set to zero). Key trends, quantitative changes over the interval 309.624 s -> 464.436 s, and comparative observations are given.
Summary of main trends
After 309.624 s all tracked aqueous species show monotonic increases (no declines) through 464.436 s.
The largest absolute increases are in UO₂²⁺(a) and NO₃⁻(a); H₂O(a) increases modestly in absolute terms but remains the dominant mass fraction; H⁺(a) shows a small absolute rise; the dissolved gases N₂(a) and O₂(a) are essentially unchanged (very small absolute changes).
Quantitative changes (309.624 s -> 464.436 s; Δt = 154.812 s)
H₂O(a): start 986.797 g/L -> end 991.804 g/L; Δ = +5.007 g/L; mean rate ≈ +0.0323 g·L⁻¹·s⁻¹; relative change ≈ +0.51%.
H⁺(a): start 0.490639 g/L -> end 0.502712 g/L; Δ = +0.012073 g/L; mean rate ≈ +7.80×10⁻⁵ g·L⁻¹·s⁻¹; relative change ≈ +2.46%.
N₂(a): start 0.00905861 g/L -> end 0.00906259 g/L; Δ ≈ +3.98×10⁻⁶ g/L; mean rate ≈ +2.57×10⁻⁸ g·L⁻¹·s⁻¹; relative change ≈ +0.044% (negligible).
NO₃⁻(a): start 19.2306 g/L -> end 30.2959 g/L; Δ = +11.0653 g/L; mean rate ≈ +0.0714 g·L⁻¹·s⁻¹; relative change ≈ +57.6%.
O₂(a): start 0.00548158 g/L -> end 0.00548215 g/L; Δ ≈ +5.7×10⁻⁷ g/L; mean rate ≈ +3.68×10⁻⁹ g·L⁻¹·s⁻¹; relative change ≈ +0.010% (negligible).
UO₂²⁺(a): start 85.7313 g/L -> end 108.573 g/L; Δ = +22.8417 g/L; mean rate ≈ +0.1475 g·L⁻¹·s⁻¹; relative change ≈ +26.6%.
Comparative observations and relations
Absolute and rate comparison:
UO₂²⁺(a) shows the largest absolute increase (≈ +22.84 g/L) and the fastest absolute rate (~0.1475 g·L⁻¹·s⁻¹).
NO₃⁻(a) also increases substantially in absolute terms (+11.07 g/L) at ~0.0714 g·L⁻¹·s⁻¹.
H₂O(a) increases by ~5.01 g/L, much smaller than U and nitrate but still the largest single-species mass in the solution.
Relative (percentage) changes:
NO₃⁻(a) shows the largest percent increase (~57.6%), indicating a strong concentration rise relative to its starting value.
UO₂²⁺(a) percent increase (~26.6%) is substantial but smaller than nitrate’s percent rise because uranium started at a higher baseline.
H⁺(a) rises by a few percent (~2.5%), while dissolved gases change by <<0.1% (effectively constant).
Correlated behavior:
The substantial simultaneous increases of NO₃⁻(a) and UO₂²⁺(a) are the dominant chemical changes in the aqueous phase during this shut-down period; their increases are comparable in timing and persistence.
N₂(a) and O₂(a) remain effectively constant and do not contribute materially to mass changes.
Notable time-local features
An inflection/acceleration in concentration increases is visible shortly after 309.624 s (notably around 317–340 s) where UO₂²⁺(a), NO₃⁻(a), and H₂O(a) begin a more steady upward trend that continues through 464.436 s.
No species shows a transient decrease or oscillation in the covered interval — all trends are monotonic increases.
Brief mass-context note
The aqueous phase remains overwhelmingly H₂O(a) by mass (~987–992 g/L). Even with the increases in solutes (UO₂²⁺ and NO₃⁻), water mass dominates the mixture and solute changes are small relative to bulk water mass but chemically significant for U and NO₃ concentrations.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 8578
'''Aqueous phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('aqueous')
quant.plot(title='Aqueous Phase Mass Density @ %2.1f C Shut-Down'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Aqueous phase mass density history at shut-down.')
Figure 30: Aqueous phase mass density history at shut-down.
tbl_count += 1
print(f'Table {tbl_count}: Aqueous phase mass density history at shut-down.')
print('Time [s] Aqueous Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 18: Aqueous phase mass density history at shut-down.
Time [s] Aqueous Phase Mass Density [g/L]
0.000000 992.08
17.201342 1026.95
34.402683 1049.01
51.604025 1062.46
137.610734 1086.97
223.617442 1091.43
309.624151 1092.26
348.327170 1107.56
387.030188 1120.67
425.733207 1127.80
464.436226 1131.19
Name: Aqueous Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.7.3. Vapor Phase Results#
'''Plot vapor phase'''
# TODO: time axis normalized by phase flow residence time.
stg.vapor_phase.plot(title='Vapor Phase Shut-Down', legend='Vapor Phase', nrows=2,ncols=3, show=True, figsize=[12,6])
fig_count += 1
print(f'Figure {fig_count}: Vapor phase species history dashboard at shut-down.')
Figure 31: Vapor phase species history dashboard at shut-down.
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Vapor Phase Data at Shut-Down.\n'
f'+ Cover only the period after {round(stg.initial_time,2)} seconds when the concentration of TBP in the inflow is set to zero.'
)
cortix_ai.explain(phase=stg.vapor_phase, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save)
Cortix AI assistant: working on explanation...
Overview of the Vapor Phase Data at Shut-Down.
This summary covers only the period after 309.624 s (rows with time ≥ 309.624 s), when the TBP inflow is set to zero.
Key facts about the time window
The covered interval is 309.624 s -> 464.436 s, Δt = 154.812 s.
Sampling is irregular but sufficiently dense to resolve slow concentration changes over this interval.
Absolute and relative changes (start = values at 309.624 s; end = values at 464.436 s)
H₂O(v): start 0.0271895 g/L -> end 0.0270780 g/L; absolute Δ = −0.0001115 g/L; relative Δ ≈ −0.41%.
N₂(v): start 0.908398 g/L -> end 0.908598 g/L; absolute Δ = +0.000200 g/L; relative Δ ≈ +0.022%.
O₂(v): start 0.257921 g/L -> end 0.257934 g/L; absolute Δ = +0.000013 g/L; relative Δ ≈ +0.005%.
Average rates of change over the interval
H₂O(v): rate ≈ −7.20 × 10⁻⁷ g·L⁻¹·s⁻¹ (small net decline).
N₂(v): rate ≈ +1.29 × 10⁻⁶ g·L⁻¹·s⁻¹ (small net increase).
O₂(v): rate ≈ +8.40 × 10⁻⁸ g·L⁻¹·s⁻¹ (negligible net increase).
Trends and monotonicity
H₂O(v) shows a slow, monotonic decrease across the interval (values stepwise lower from 0.0271895 down to 0.027078), indicating gradual vapor-phase depletion or redistribution after shut-down.
N₂(v) increases monotonically and approaches ≈ 0.9086 g/L, suggesting a slow rise toward a new equilibrium concentration.
O₂(v) is nearly constant, with only a trace increase (third significant digit unchanged), indicating O₂ is effectively at steady state for this period.
Stability and approach to steady state
Magnitudes of all changes are very small (<0.5% for H₂O and ≪0.1% for N₂ and O₂), consistent with the system approaching a new steady state after shut-down.
Final values near 464.436 s (H₂O ≈ 0.02708 g/L, N₂ ≈ 0.90860 g/L, O₂ ≈ 0.257934 g/L) are good practical estimates of the post-shutdown vapor-phase concentrations.
Practical summary
After TBP inflow is set to zero at ≈309.62 s, the vapor-phase composition changes are minor and slow: H₂O decreases slightly, N₂ increases slightly, and O₂ remains effectively constant, all toward a stable post-shutdown composition.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 6285
'''Vapor phase mass density'''
import matplotlib.pyplot as plt
quant = stg.mass_density_history('vapor')
quant.plot(title='Vapor Phase Mass Density @ %2.1f C Shut-Down'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Vapor phase mass density history at shut-down.')
Figure 32: Vapor phase mass density history at shut-down.
tbl_count += 1
print(f'Table {tbl_count}: Vapor phase mass density history at shut-down.')
print('Time [s] Vapor Phase Mass Density [g/L]')
print(quant.value[::5].apply(lambda x: round(x,2)))
Table 19: Vapor phase mass density history at shut-down.
Time [s] Vapor Phase Mass Density [g/L]
0.000000 1.09
17.201342 1.12
34.402683 1.14
51.604025 1.16
137.610734 1.19
223.617442 1.19
309.624151 1.19
348.327170 1.19
387.030188 1.19
425.733207 1.19
464.436226 1.19
Name: Vapor Phase Mass Density [g/L]; Time History in [s], dtype: float64
1.7.3.1. Relative Humidity#
'''Compute relative humidity in the vapor phase'''
import matplotlib.pyplot as plt
from solvex import air_relative_humidity
from copy import deepcopy
h2o_vap_pd_series = deepcopy(stg.vapor_phase.df['H2O(v)'])
for idx, rho_h2o in enumerate(h2o_vap_pd_series):
h2o_vap_pd_series.iloc[idx] = air_relative_humidity(stg_temperature, rho_h2o)
rh_pd_series = h2o_vap_pd_series
time_unit = stg.vapor_phase.time_unit
rh_pd_series.name = f'Relative Humidity History [{time_unit}]'
quant = Quantity(name=rh_pd_series.name, latex_name='RH', unit='%', info=f'Relative Humidity History (Vapor Phase) [{time_unit}]')
quant.value = h2o_vap_pd_series
quant.plot(title='Stage Rel. Humidity @ %2.1f C Shut-Down'%unit.convert_temperature(stg_temperature, 'K','C'),
x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+' ['+quant.unit+']', show=True,
figsize=[10,3])
fig_count += 1
print(f'Figure {fig_count}: Stage relative humidity history at shut-down.')
Figure 33: Stage relative humidity history at shut-down.
tbl_count += 1
print(f'Table {tbl_count}: Stage relative humidity history at shut-down.')
print('Time [s] Rel. Humidity [%]')
print(quant.value.loc[n_startup::5].apply(lambda x: round(x,2)))
Table 20: Stage relative humidity history at shut-down.
Time [s] Rel. Humidity [%]
17.201342 44.26
34.402683 48.88
51.604025 51.17
137.610734 53.25
223.617442 53.25
309.624151 53.24
348.327170 53.19
387.030188 53.11
425.733207 53.05
464.436226 53.02
Name: Relative Humidity History [s], dtype: float64
if cortix_ai:
issues = ('+ Title your reponse as: Overview of the Relative Humidity Data at Shut-Down.\n'
f'+ Cover only the period after {round(stg.initial_time,2)} seconds when the concentration of TBP in the inflow is set to zero.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h5>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of the Relative Humidity Data at Shut-Down.
Data selection (post‑309.62 s)
The subset of rows after 309.624 s (time when TBP inflow concentration is set to zero) contains four measurements:
time = 309.624 s, Relative Humidity = 53.2363 %
time = 356.068 s, Relative Humidity = 53.1784 %
time = 402.511 s, Relative Humidity = 53.0874 %
time = 448.955 s, Relative Humidity = 53.0299 %
Statistical summary (these four points)
Count: 4 measurements
Time span: 448.955 − 309.624 = 139.331 s (≈ 2.32 min)
Mean RH: 53.133 %
Median RH: 53.133 %
Minimum RH: 53.0299 % (at 448.955 s)
Maximum RH: 53.2363 % (at 309.624 s)
Total change (end − start): −0.2064 percentage points
Average rate (start→end): −0.2064 / 139.331 ≈ −0.00148 %/s (≈ −0.0889 %/min)
Dispersion: population standard deviation ≈ 0.080 %; sample standard deviation ≈ 0.092 %
Trend and interpretation
The RH series after shut‑down shows a small, monotonic decrease from 53.2363 % to 53.0299 % over ~139 s.
The decrease is very small in magnitude (~0.2 percentage points) and the variability across measurements is low (~0.08–0.09 %).
The average decline rate is about −1.5×10⁻³ %/s, indicating a slow downward relaxation of vapor‑phase relative humidity after the inflow TBP concentration is set to zero.
Conclusion
After 309.624 s, relative humidity remains effectively stable near 53.13 %, with a slight, steady decline of ~0.2 percentage points over ~2.3 minutes and low short‑term variability.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4240
1.7.4. Reaction Rate Density#
This quantity only makes sense per volume of the mixture.
'''Reaction rate density'''
import matplotlib.pyplot as plt
quant = stg.r_vec_history()
quant.plot(title='Reaction Rate Density @ %2.1f C Shut-Down'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', legend=stg.rxn_mech.reactions, show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Reaction rate density history at shut-down.')
Figure 34: Reaction rate density history at shut-down.
tbl_count += 1
print(f'Table {tbl_count}: Reaction rate density history at shut-down.')
print('Time [min] r [mM/s]')
import pandas as pd
col_names = [f'r{i}' for i in range(len(stg.rxn_mech.reactions))]
time_name = 't [min]'
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(col_names, axis=1).rename_axis(time_name)
.round(4))
print(df.iloc[n_startup:].to_string(max_rows=20, min_rows=20))
Table 21: Reaction rate density history at shut-down.
Time [min] r [mM/s]
r0 r1 r2 r3 r4 r5 r6 r7 r8
t [min]
1.15 0.4164 0.7805 0.0953 0.0607 0.0414 0.7194 0.0014 0.0014 0.0027
1.43 0.4006 0.7562 0.0914 0.0605 0.0402 0.7322 0.0013 0.0014 0.0027
1.72 0.3933 0.7464 0.0901 0.0601 0.0395 0.7369 0.0013 0.0014 0.0027
2.01 0.3899 0.7424 0.0897 0.0599 0.0390 0.7388 0.0013 0.0014 0.0027
2.29 0.3883 0.7407 0.0896 0.0597 0.0388 0.7397 0.0013 0.0014 0.0027
2.58 0.3875 0.7400 0.0895 0.0596 0.0386 0.7401 0.0013 0.0014 0.0027
2.87 0.3871 0.7397 0.0895 0.0596 0.0385 0.7402 0.0013 0.0014 0.0027
3.15 0.3869 0.7395 0.0896 0.0596 0.0385 0.7403 0.0013 0.0014 0.0027
3.44 0.3869 0.7394 0.0896 0.0595 0.0385 0.7404 0.0013 0.0014 0.0027
3.73 0.3868 0.7394 0.0896 0.0595 0.0385 0.7404 0.0013 0.0014 0.0027
... ... ... ... ... ... ... ... ... ...
6.58 0.0289 -0.0550 -0.0072 0.0046 -0.0028 -0.0040 0.0013 0.0014 0.0027
6.71 0.0245 -0.0477 -0.0056 0.0037 -0.0023 -0.0087 0.0013 0.0014 0.0027
6.84 0.0208 -0.0407 -0.0044 0.0029 -0.0019 -0.0118 0.0013 0.0014 0.0027
6.97 0.0177 -0.0343 -0.0034 0.0023 -0.0016 -0.0137 0.0013 0.0014 0.0027
7.10 0.0151 -0.0287 -0.0026 0.0019 -0.0013 -0.0146 0.0013 0.0014 0.0027
7.22 0.0130 -0.0238 -0.0020 0.0015 -0.0011 -0.0147 0.0013 0.0014 0.0027
7.35 0.0111 -0.0196 -0.0015 0.0012 -0.0008 -0.0143 0.0013 0.0014 0.0027
7.48 0.0095 -0.0161 -0.0011 0.0010 -0.0007 -0.0136 0.0013 0.0014 0.0027
7.61 0.0081 -0.0131 -0.0009 0.0008 -0.0005 -0.0126 0.0013 0.0014 0.0027
7.74 0.0069 -0.0106 -0.0007 0.0006 -0.0004 -0.0115 0.0013 0.0014 0.0027
if cortix_ai:
issues = ('+ Title your reponse as: Overview of Reaction Rates Density at Shut-Down.\n'
f'+ Cover only the period after {round(stg.initial_time,2)} seconds when the concentration of TBP in the inflow is set to zero.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of Reaction Rates Density at Shut-Down.
Scope
This analysis covers only the times after 309.624 s (rows with times 356.068 s, 402.511 s, 448.955 s) when TBP concentration in the inflow is set to zero. Units are mM/s. The nine columns correspond, in order, to the provided ordered reactions.
Summary of selected time points and general trend
Time points considered: 356.068 s, 402.511 s, 448.955 s.
General behaviour: most reaction rates that involve the TBP species ([C4H9O]3PO) decay in magnitude toward zero over these times; the three phase-exchange equilibria (H2O, O2, N2) show small, essentially constant rates.
Mapping of columns to reactions
Column → Reaction (as given)
0 → [C4H9O]3PO(o) + H2O(a) <-> H2O*[C4H9O]3PO(o)
1 → 2 [C4H9O]3PO(o) + 2 H2O(a) <-> [H2O]2*[[C4H9O]3PO]2(o)
2 → 3 [C4H9O]3PO(o) + 6 H2O(a) <-> [H2O]6*[[C4H9O]3PO]3(o)
3 → H⁺(a) + NO3⁻(a) + [C4H9O]3PO(o) <-> HNO3*[C4H9O]3PO(o)
4 → H⁺(a) + NO3⁻(a) + 2 [C4H9O]3PO(o) <-> HNO3*[[C4H9O]3PO]2(o)
5 → UO2²⁺(a) + 2 NO3⁻(a) + 2 [C4H9O]3PO(o) <-> UO2[NO3]2*[[C4H9O]3PO]2(o)
6 → H2O(a) <-> H2O(v)
7 → O2(v) <-> O2(a)
8 → N2(v) <-> N2(a)
Per-reaction numeric behaviour after shut-down (356.068 s → 402.511 s → 448.955 s)
Reaction 0 (TBP + H2O association/dissociation)
Values: 0.0757225, 0.0244535, 0.00950356 mM/s.
Interpretation: positive rate decreasing steadily toward zero — ongoing but diminishing forward net rate (association) as TBP feed is removed.
Reaction 1 (dimeric TBP hydration complex formation)
Values: -0.0611065, -0.0476898, -0.0160664 mM/s.
Interpretation: net negative rate (reverse direction) with magnitude shrinking toward zero — net decomposition/dispersion diminishing over time.
Reaction 2 (trimeric TBP hydration complex)
Values: -0.0195418, -0.00561512, -0.00114763 mM/s.
Interpretation: small negative rates rapidly approaching zero — near re-equilibration for the larger complex.
Reaction 3 (proton + nitrate + TBP ↔ HNO3·TBP)
Values: 0.0130879, 0.0036673, 0.000951764 mM/s.
Interpretation: small positive forward rate that decreases toward zero — formation continuing but weakening.
Reaction 4 (HNO3 complex with two TBP)
Values: -0.00499938, -0.00233347, -0.00068135 mM/s.
Interpretation: small negative (net reverse) rate diminishing toward zero — decomplexation slowing.
Reaction 5 (UO2 complexation with TBP)
Values: 0.0587553, -0.00872149, -0.0135787 mM/s.
Interpretation: notable: positive at 356 s then switches to negative at later times. This indicates an initial net formation (or release depending on sign convention) followed by net consumption/decomplexation as the system relaxes after TBP inflow stops.
Reaction 6 (H2O liquid-vapor exchange)
Values: 0.00132159, 0.00131955, 0.00131904 mM/s.
Interpretation: very small, essentially constant positive rate — phase-exchange maintained and effectively unchanged by TBP shut-down over this interval.
Reaction 7 (O2 vapor-liquid exchange)
Values: 0.0014277, 0.0014277, 0.0014277 mM/s.
Interpretation: constant small positive rate — no detectable change.
Reaction 8 (N2 vapor-liquid exchange)
Values: 0.00269596, 0.00269596, 0.00269596 mM/s.
Interpretation: constant small positive rate — unchanged over the sampled times.
Net observations and implications
All TBP-involving reactions (columns 0–5) approach zero magnitude after shut-down, consistent with exhaustion of TBP supply; many switch sign or decay rapidly as complexes dissociate or re-equilibrate.
Reaction 5 (UO2 complex) shows the largest and most notable sign change, indicating transient dynamics in actinide complexation after feed removal.
The three phase-exchange reactions (columns 6–8) remain steady and small, indicating that gas–liquid exchanges (H2O, O2, N2) are not strongly perturbed by the TBP shut-down during this interval.
By 448.955 s rates for most TBP-related reactions are within ~10⁻³ to 10⁻² mM/s of zero, indicating approach toward a new equilibrium or very slow kinetics thereafter.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 5700
1.7.5. Species Generation Rate Density#
This quantity is on the basis of mixing volume so all species generation values can be compared.
'''Species generation rate density'''
import matplotlib.pyplot as plt
quant = stg.g_vec_history()
quant.plot(title='Species Generation Rate Density @ %2.1f C Shut-Down'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', legend=stg.rxn_mech.species_names, show=True, figsize=[10,3], error_data=False)
fig_count += 1
print(f'Figure {fig_count}: Species generation rate density history at shut-down.')
Figure 35: Species generation rate density history at shut-down.
tbl_count += 1
print(f'Table {tbl_count}: Species generation rate density history at shut-down.')
print('Time [min] g [mM/s]')
import pandas as pd
col_names = [f'a{i}' for i in range(len(stg.rxn_mech.species_names))]
time_name = 't [min]'
df = (quant.value.apply(pd.Series).mul(1)
.rename(index=lambda i: round(i/unit.min,2))
.set_axis(col_names, axis=1).rename_axis(time_name)
.round(4))
print(df.iloc[n_startup:].to_string(max_rows=20, min_rows=20))
Table 22: Species generation rate density history at shut-down.
Time [min] g [mM/s]
a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15
t [min]
1.15 -2.5505 0.0014 0.4164 0.0607 0.0414 -0.1021 0.0027 -0.0027 -1.5408 0.0014 -0.0014 0.7194 -0.7194 -3.8454 0.7805 0.0953
1.43 -2.4631 0.0013 0.4006 0.0605 0.0402 -0.1007 0.0027 -0.0027 -1.5652 0.0014 -0.0014 0.7322 -0.7322 -3.7928 0.7562 0.0914
1.72 -2.4282 0.0013 0.3933 0.0601 0.0395 -0.0996 0.0027 -0.0027 -1.5734 0.0014 -0.0014 0.7369 -0.7369 -3.7694 0.7464 0.0901
2.01 -2.4141 0.0013 0.3899 0.0599 0.0390 -0.0989 0.0027 -0.0027 -1.5766 0.0014 -0.0014 0.7388 -0.7388 -3.7593 0.7424 0.0897
2.29 -2.4084 0.0013 0.3883 0.0597 0.0388 -0.0985 0.0027 -0.0027 -1.5778 0.0014 -0.0014 0.7397 -0.7397 -3.7550 0.7407 0.0896
2.58 -2.4060 0.0013 0.3875 0.0596 0.0386 -0.0983 0.0027 -0.0027 -1.5784 0.0014 -0.0014 0.7401 -0.7401 -3.7531 0.7400 0.0895
2.87 -2.4050 0.0013 0.3871 0.0596 0.0385 -0.0981 0.0027 -0.0027 -1.5786 0.0014 -0.0014 0.7402 -0.7402 -3.7522 0.7397 0.0895
3.15 -2.4046 0.0013 0.3869 0.0596 0.0385 -0.0981 0.0027 -0.0027 -1.5787 0.0014 -0.0014 0.7403 -0.7403 -3.7518 0.7395 0.0896
3.44 -2.4044 0.0013 0.3869 0.0595 0.0385 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7516 0.7394 0.0896
3.73 -2.4043 0.0013 0.3868 0.0595 0.0385 -0.0980 0.0027 -0.0027 -1.5788 0.0014 -0.0014 0.7404 -0.7404 -3.7515 0.7394 0.0896
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
6.58 0.1229 0.0013 0.0289 0.0046 -0.0028 -0.0018 0.0027 -0.0027 0.0062 0.0014 -0.0014 -0.0040 0.0040 0.1117 -0.0550 -0.0072
6.71 0.1033 0.0013 0.0245 0.0037 -0.0023 -0.0013 0.0027 -0.0027 0.0161 0.0014 -0.0014 -0.0087 0.0087 0.1062 -0.0477 -0.0056
6.84 0.0855 0.0013 0.0208 0.0029 -0.0019 -0.0010 0.0027 -0.0027 0.0227 0.0014 -0.0014 -0.0118 0.0118 0.0983 -0.0407 -0.0044
6.97 0.0698 0.0013 0.0177 0.0023 -0.0016 -0.0008 0.0027 -0.0027 0.0267 0.0014 -0.0014 -0.0137 0.0137 0.0893 -0.0343 -0.0034
7.10 0.0565 0.0013 0.0151 0.0019 -0.0013 -0.0006 0.0027 -0.0027 0.0286 0.0014 -0.0014 -0.0146 0.0146 0.0799 -0.0287 -0.0026
7.22 0.0452 0.0013 0.0130 0.0015 -0.0011 -0.0004 0.0027 -0.0027 0.0290 0.0014 -0.0014 -0.0147 0.0147 0.0707 -0.0238 -0.0020
7.35 0.0359 0.0013 0.0111 0.0012 -0.0008 -0.0003 0.0027 -0.0027 0.0283 0.0014 -0.0014 -0.0143 0.0143 0.0618 -0.0196 -0.0015
7.48 0.0282 0.0013 0.0095 0.0010 -0.0007 -0.0003 0.0027 -0.0027 0.0269 0.0014 -0.0014 -0.0136 0.0136 0.0536 -0.0161 -0.0011
7.61 0.0220 0.0013 0.0081 0.0008 -0.0005 -0.0002 0.0027 -0.0027 0.0249 0.0014 -0.0014 -0.0126 0.0126 0.0462 -0.0131 -0.0009
7.74 0.0170 0.0013 0.0069 0.0006 -0.0004 -0.0002 0.0027 -0.0027 0.0227 0.0014 -0.0014 -0.0115 0.0115 0.0395 -0.0106 -0.0007
if cortix_ai:
issues = ('+ Title your reponse as: Overview of Species Generation Rates Density at Shut-Down.\n'
f'+ Cover only the period after {round(stg.initial_time,2)} seconds when the concentration of TBP in the inflow is set to zero.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Overview of Species Generation Rates Density at Shut-Down.
Only times after 309.624 s are covered: 356.068 s, 402.511 s and 448.955 s. Dependent quantity units: mM/s.
H₂O(a): 0.16242, 0.103297, 0.028196 mM/s — positive generation that decays toward zero over the period.
H₂O(v): 0.00132159, 0.00131955, 0.00131904 mM/s — very small, essentially constant positive generation.
H₂O·[C₄H₉O]₃PO(o): 0.0757225, 0.0244535, 0.00950356 mM/s — positive production that falls by roughly an order of magnitude across the interval.
HNO₃·[C₄H₉O]₃PO(o): 0.0130879, 0.0036673, 0.000951764 mM/s — positive production decreasing toward zero.
HNO₃·[[C₄H₉O]₃PO]₂(o): -0.00499938, -0.00233347, -0.00068135 mM/s — net consumption (negative) with magnitude diminishing toward zero.
H⁺(a): -0.00808849, -0.00133382, -0.000270414 mM/s — net consumption that rapidly relaxes toward zero.
N₂(a): 0.00269596, 0.00269596, 0.00269596 mM/s — small, constant positive generation (no measurable trend).
N₂(v): -0.00269596, -0.00269596, -0.00269596 mM/s — small, constant negative rate (mirror of N₂(a)).
NO₃⁻(a): -0.125599, 0.0161092, 0.0268871 mM/s — large initial consumption at 356.068 s that flips to small positive production by 402.511–448.955 s.
O₂(a): 0.0014277, 0.0014277, 0.0014277 mM/s — small, constant positive generation.
O₂(v): -0.0014277, -0.0014277, -0.0014277 mM/s — small, constant negative rate (mirror of O₂(a)).
UO₂[NO₃]₂·[[C₄H₉O]₃PO]₂(o): 0.0587553, -0.00872149, -0.0135787 mM/s — production at 356.068 s that changes sign and becomes modest consumption thereafter (consumption magnitude increases slightly).
UO₂²⁺(a): -0.0587553, 0.00872149, 0.0135787 mM/s — opposite of the previous complex: consumption at 356.068 s flips to production at later times, increasing in magnitude.
[C₄H₉O]₃PO(o): -0.0154836, 0.106214, 0.0536406 mM/s — small consumption at 356.068 s, then a pronounced production spike at 402.511 s that partially decays by 448.955 s (net positive through the end).
[H₂O]₂·[[C₄H₉O]₃PO]₂(o): -0.0611065, -0.0476898, -0.0160664 mM/s — net consumption that relaxes toward zero.
[H₂O]₆·[[C₄H₉O]₃PO]₃(o): -0.0195418, -0.00561512, -0.00114763 mM/s — consumption with a clear decay toward zero.
Summary: after TBP inflow is set to zero (post‑309.62 s) most species’ generation rates decay toward zero over the 356–449 s window, indicating relaxation of transient behavior. Notable sign changes occur for NO₃⁻(a), the UO₂ complex and UO₂²⁺(a), and [C₄H₉O]₃PO(o) (TBP), showing redistribution between dissolved ionic species and organophosphorus‑bound complexes; N₂ and O₂ phase fluxes remain small and essentially constant.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 7705
1.7.6. Mass Balance Residual#
This is actually the total mass generation rate density (mixing volume) residual; a small number in view of total mass conservation.
'''Mass balance residuals'''
import matplotlib.pyplot as plt
quant = stg.mass_balance_residual_history()
quant.plot(title='Total Mass Generation Rate Density @ %2.1f C Shut-Down'%unit.convert_temperature(stg_temperature,
'K','C'), x_scaling=1/stg.flow_residence_time_avg, x_label=r'Time [$\bar{\tau}$]', y_label=quant.latex_name+
' ['+quant.unit+']', markers_only=True, legend=['Residual'], show=True, figsize=[10,3])
fig_count += 1
print(f'Figure {fig_count}: Total mass generation rate density residual history at shut-down.')
Figure 36: Total mass generation rate density residual history at shut-down.
if cortix_ai:
issues = ('+ Title your reponse as: Total Mass Generation Rate Density Residual at Shut-Down.\n'
f'+ Cover only the period after {round(stg.initial_time,2)} seconds when the concentration of TBP in the inflow is set to zero.'
)
cortix_ai.explain(quant=quant, issues=issues, markdown_header_level='<h4>', markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Cortix AI assistant: working on explanation...
Total Mass Generation Rate Density Residual at Shut-Down.
Numerical summary
Post-shutdown data points (times > 309.624 s) and their Total Mass Generation Rate Density [g/L-s]:
356.068 s: 6.93889e-18 g/L-s
402.511 s: -4.33681e-19 g/L-s
448.955 s: 2.60209e-18 g/L-s
Mean (arithmetic): 3.036e-18 g/L-s
RMS (root-mean-square): 4.29e-18 g/L-s
Maximum absolute residual: 6.94e-18 g/L-s (at 356.068 s)
Local slopes (finite-difference):
356.068 -> 402.511 (Δt = 46.443 s): slope ≈ -1.59e-19 g/L-s²
402.511 -> 448.955 (Δt = 46.444 s): slope ≈ 6.54e-20 g/L-s²
Interpretation
All residuals after shut-down are on the order of 10⁻¹⁸–10⁻¹⁹ g/L-s and alternate sign; magnitudes are extremely small relative to typical macroscopic rates.
RMS and max-absolute values indicate residuals at the level of numerical round-off / noise rather than a sustained mass source or sink.
The small, sign-changing finite differences show no persistent trend (no monotonic increase or decrease) in mass generation after TBP inflow is set to zero.
Conclusion: after 309.62 s the mixing volume exhibits negligible total-mass-generation residuals (≈10⁻¹⁸ g/L-s), consistent with conservation within numerical tolerance.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ Total # of tokens = 4769
1.8. AI Reports#
The Stage AI Report agent builds a Solvex Knowledge Base from the simulation up to this point, and uses it as a retrieval augmented generation (RAG) AI to analyze the results and provide a report.
'''Create and Configure a StageAI Object for a particular stg'''
if stage_ai:
from stage_ai import StageAI
stage_ai = StageAI(stg, llm_model='gpt-5-mini', llm_cleverness=0.8)
stage_ai.n_chunks = 8
stage_ai.markdown_header_level = '<h4>'
1.8.1. Aqueous Phase#
Request a report from the LLM.
if stage_ai:
issues = ('+ Title your response as: Aqueous Phase Analysis.\n'
'+ Discuss in particular, for the aqueous phase, why the simulated steady-state concentration of O2 in the aqueous'
' phase is lower than N2 when the solubility of O2 in the aqueous phase is higher than the solubility of N2.\n'
f'+ Consider stage efficiency only up to {round(stg.initial_time,2)} seconds.'
)
stage_ai.report(phase='aqueous', issues=issues, markdown_display=markdown_display, save_supporting_info=db_save, print_prompt=False)
Stage AI assistant: working on report...
Aqueous Phase Analysis
1. Introduction
The following report analyzes the aqueous phase from the provided Stage Knowledge Base (source: stg.aqueous_phase). The system is an aqueous–organic–vapor multi-phase mixing/extraction system. All time-series concentrations are reported in mass concentration units (g/L) and time in seconds (s). Stage efficiency (referred to below explicitly as stage) is used to assess how close the stage approaches chemical equilibrium; only stage efficiency values up to 309.62 s are considered for equilibrium statements as requested.
2. Species characteristics (commercial names and formulas)
Species characteristics (commercial chemical name, chemical formula, phase, molar mass in kg/mol, atoms, charge):
| Commercial name | Chemical formula (as in KB) | Phase | Molar mass [kg/mol] | Major atoms | Charge | |—|—:|—|—:|—|—:| | Water | H2O(a) | aqueous phase | 0.018015 | 2H, O | 0 | | Hydrogen ion (hydron) | H^+(a) | aqueous phase | 0.0010074 | H | +1 | | Nitrogen (gas) | N2(a) | aqueous phase (dissolved) | 0.028013 | 2N | 0 | | Nitrate ion | NO3^-(a) | aqueous phase | 0.062006 | N, 3O | -1 | | Oxygen (gas) | O2(a) | aqueous phase (dissolved) | 0.031999 | 2O | 0 | | Uranyl ion (uranyl) | UO2^2+(a) | aqueous phase | 0.27130 | U, 2*O | +2 |
3. Time-dependent species concentrations (mass concentration, g/L; time in seconds)
Time-series mass concentrations (units: g/L). Time stamp units: seconds (s).
| Time [s] | Water (g/L) | H^+ (g/L) | N2 (g/L) | NO3^- (g/L) | O2 (g/L) | UO2^2+ (g/L) | |—:|—:|—:|—:|—:|—:|—:| | 0.0 | 992.00 | 0.0010074 | 0.0081544 | 0.062006 | 0.0053437 | 0.00000 | | 20.64 | 987.65 | 0.16541 | 0.0082590 | 9.5685 | 0.0053606 | 34.925 | | 41.28 | 986.57 | 0.27341 | 0.0084418 | 13.970 | 0.0053894 | 54.319 | | 103.21 | 986.52 | 0.42580 | 0.0088442 | 17.875 | 0.0054504 | 76.882 | | 206.42 | 986.76 | 0.48289 | 0.0090319 | 19.072 | 0.0054778 | 84.686 | | 309.62 | 986.80 | 0.49064 | 0.0090586 | 19.231 | 0.0054816 | 85.731 | | 356.07 | 989.41 | 0.49654 | 0.0090611 | 24.403 | 0.0054819 | 96.415 | | 402.51 | 991.14 | 0.50055 | 0.0090621 | 28.205 | 0.0054821 | 104.26 | | 448.96 | 991.72 | 0.50238 | 0.0090625 | 29.984 | 0.0054821 | 107.93 |
4. Stage efficiency time history (mean ± std) — used up to 309.62 s for equilibrium discussion
Stage efficiency (mean, ± std) in percent; time in seconds. (Only stage data up to 309.62 s are used for equilibrium statements.)
| Time [s] | Efficiency mean [%] | Efficiency std [%] | |—:|—:|—:| | 0.0 | 15.000 | 21.600 | | 20.64 | 30.910 | 14.290 | | 41.28 | 40.250 | 10.470 | | 103.21 | 48.940 | 3.470 | | 206.42 | 50.340 | 1.460 | | 309.62 | 50.460 | 1.360 |
5. Reaction mechanism involving the aqueous phase
I reviewed the provided reaction mechanism and extracted the reactions that involve aqueous-phase species. Each entry below reproduces the reaction (UTF-8 characters and using <-> for reversible steps), the description from the Stage Knowledge Base, and an extra short interpretive remark.
[C4H9O]3PO(o) + H2O(a) <-> H2O*[C4H9O]3PO(o)
KB description: complexation of one H2O molecule
Additional information: This reaction is the complexation (extraction) of one aqueous-phase water molecule by tributyl phosphate (TBP) in the organic phase, forming a TBP·H2O adduct (organic phase). It couples the aqueous phase (H2O(a)) to the organic phase (TBP(o)).
2 [C4H9O]3PO(o) + 2 H2O(a) <-> [H2O]2*[[C4H9O]3PO]2(o)
KB description: complexation of two H2O molecules
Additional information: This is a higher-order TBP complexation where two aqueous H2O molecules become associated with two TBP molecules in the organic phase; it represents multi-molecule solvation/extraction of water.
3 [C4H9O]3PO(o) + 6 H2O(a) <-> [H2O]6*[[C4H9O]3PO]3(o)
KB description: complexation of six H2O molecules
Additional information: Larger TBP–water aggregate formation; significant aqueous water transfer to organic phase when this pathway is active.
H^+(a) + NO3^-(a) + [C4H9O]3PO(o) <-> HNO3*[C4H9O]3PO(o)
KB description: one TBP complexation of HNO3
Additional information: Proton and nitrate in the aqueous phase form a neutral HNO3 species that is complexed by one TBP molecule and transferred to the organic phase. This couples aqueous ionic species (H^+(a), NO3^-(a)) to extraction into the organic phase.
H^+(a) + NO3^-(a) + 2 [C4H9O]3PO(o) <-> HNO3*[[C4H9O]3PO]2(o)
KB description: two TBP complexation of HNO3
Additional information: Similar to the previous reaction but with two TBP molecules stabilizing the extracted HNO3; represents stronger extraction of HNO3 from the aqueous phase.
UO2^2+(a) + 2 NO3^-(a) + 2 [C4H9O]3PO(o) <-> UO2[NO3]2*[[C4H9O]3PO]2(o)
KB description: uranyl nitrate complexation with 2TBP
Additional information: Aqueous uranyl and nitrate combine and are extracted into the organic phase as a TBP-stabilized uranyl nitrate complex. This is a primary extraction reaction removing UO2^2+(a) (uranyl) from the aqueous phase into the organic phase.
H2O(a) <-> H2O(v)
KB description: H2O vaporization in air
Additional information: Aqueous-phase water volatilizes into the vapor phase; this is a reversible phase transfer between aqueous and vapor phases.
O2(v) <-> O2(a)
KB description: O2 aeration of aqueous phase
Additional information: Dissolution (aeration) of gaseous oxygen into the aqueous phase and its reverse (stripping); this is the intended gas–liquid mass-transfer route for O2.
N2(v) <-> N2(a)
KB description: N2 aeration of aqueous phase
Additional information: Dissolution (aeration) of gaseous nitrogen into the aqueous phase and its reverse; gas–liquid mass transfer for N2.
Note: I checked the reaction list for missing aqueous-phase reactions. All provided reactions that reference (a) species are included above; no additional aqueous-phase reactions were present in the KB content.
6. Comments on reaction mechanism and implications for aqueous-phase composition
The mechanism is dominated by extraction/complexation reactions between aqueous ions or neutral species (H^+(a), NO3^-(a), H2O(a), UO2^2+(a)) and the organic-phase ligand tributyl phosphate ([C4H9O]3PO(o)). These reactions transfer species from the aqueous phase into the organic phase (o), thereby altering aqueous-phase concentrations (for example, UO2^2+ and NO3^- decrease in aqueous phase when extraction proceeds).
Gas–liquid mass transfer reactions (O2(v) <-> O2(a), N2(v) <-> N2(a), H2O(a) <-> H2O(v)) connect the aqueous phase to the vapor phase; these are the only direct routes for dissolved gases in the KB. No chemical consumption (e.g., oxidation reactions consuming O2(a)) is present in the KB, so the model does not include aqueous chemical sinks for dissolved O2.
Because key transfer steps are reversible and involve inter-phase transport, the system can be far from chemical equilibrium if interfacial mass transfer (stage) or extraction kinetics are limited.
7. Why simulated aqueous O2 concentration is lower than N2 despite higher O2 solubility
Observed at 309.62 s: N2(a) = 0.0090586 g/L, O2(a) = 0.0054816 g/L. Oxygen solubility (mole basis) is typically greater than nitrogen, yet the model shows a lower aqueous-phase mass concentration of O2. From the KB and the stage data the following, model-consistent explanations apply:
Phase and units: the reported values are mass concentrations (g/L). The molar masses differ (N2: 0.028013 kg/mol; O2: 0.031999 kg/mol), so mass concentration differences correspond to different molar concentrations. N2 being higher in g/L implies an even larger difference in molar concentration favoring N2.
Gas-phase driving force (partial pressures / composition): The gas feed is conceptually “air” (the header refers to presence of air). In air the mole fraction of N2 (~0.78) greatly exceeds that of O2 (~0.21). Henry’s law equilibrium for dissolved gas i is c_i(aq) = H_i * p_i(gas). Even if H_O2 > H_N2 (i.e., O2 is more soluble per unit partial pressure), the much larger partial pressure of N2 in the gas phase can yield larger aqueous N2 concentrations if H_O2/H_N2 is not large enough to overcome the partial-pressure ratio (p_N2/p_O2 ~ 3.7 for air). The KB does not provide Henry constants or gas-phase composition, but the presence of air as the gas phase makes this explanation physically plausible and consistent with the data.
Non-equilibrium mass transfer (stage effects): stage efficiency up to 309.62 s is far from 100% (≈50.46% mean at 309.62 s). Because the stage is operating at partial efficiency (stage), the system is not at thermodynamic equilibrium. Transient mass transfer kinetics and limited interfacial transfer rates (different mass transfer coefficients for O2 and N2) can produce aqueous concentrations that differ from equilibrium predictions. In particular, if the mass-transfer coefficient or interfacial resistance for O2 is lower (or the gas-side partial pressure of O2 is depleted locally), aqueous O2 will lag behind N2 in the transient simulation.
Absence of O2 chemical sink in KB: The KB does not include chemical reactions that consume aqueous O2(a). Therefore the lower O2(a) is not due to modeled chemical consumption; the cause must therefore be gas-phase partial-pressure balance and/or kinetics/transport limitations in the simulation.
Synthesis: the dominant explanations consistent with the KB are (1) gas-phase composition that strongly favors N2 (air) and (2) incomplete mass transfer (stage efficiency < 100%) plus potentially different mass-transfer kinetics for O2 and N2. Together these explain why N2 mass concentration in the aqueous phase exceeds that of O2 despite higher intrinsic O2 solubility per unit partial pressure.
8. Equilibrium assessment using stage data (up to 309.62 s)
The stage mean efficiency increases from 15.00% at t = 0 s to ~50.46% at t = 309.62 s. Because stage efficiency is substantially below ~100% by 309.62 s, the stage is not at chemical equilibrium (chemical equilibrium would require stage ≈ 100%). Therefore any statements about thermodynamic equilibrium for aqueous–vapor partitioning or aqueous–organic extraction at or before 309.62 s would be unsupported.
The observed time trends (species concentrations still changing between 206.42 s and 309.62 s, and beyond) are consistent with ongoing mass transfer and extraction kinetics; the system remains transient through the considered time window.
9. Additional observations
Nitrate (NO3^-) and UO2^2+ concentrations in g/L show significant increases over time in the aqueous phase for many time points (e.g., NO3^- rises from 0.062006 g/L at t=0 to 19.231 g/L at t=309.62 s). The presence of extraction reactions in the mechanism indicates competition between aqueous-phase retention and transfer to the organic phase; the time series shows dynamic behavior.
The stage mass-balance residuals provided in the KB (Total Mass Generation Rate Density [g/L-s]) are effectively zero for all recorded time stamps, indicating that the model enforces mass conservation (mass balance residual values are -0.0, 0.0 etc.). Thus mass conservation holds per the KB data.
10. Recommendations
If the objective is to reach chemical equilibrium between phases for dissolved gases (O2, N2), increase stage residence time or improve interfacial mass transfer (e.g., higher gas–liquid contact, agitation) to raise the stage efficiency closer to 100% (stage).
Measure or supply gas-phase composition (partial pressures) and, if possible, Henry’s law constants or mass-transfer coefficients for O2 and N2 used in the simulation to confirm whether gas-side composition or transport kinetics dominate the observed O2 < N2 result.
If O2 consumption is suspected in reality (biological or chemical oxidation), add explicit aqueous-phase reaction(s) that consume O2 to the model and re-run to check if they explain the lower O2 concentration; the KB currently contains no such sink for O2.
11. Conclusion
The aqueous phase in the provided Stage Knowledge Base is a multicomponent aqueous solution (aqueous-phase) containing dissolved gases (O2(a), N2(a)), ions (H^+(a), NO3^-(a)), water (H2O(a)), and dissolved uranyl (UO2^2+(a)). The overall system is an aqueous–organic–vapor mixture system and the aqueous phase is a non-ideal multicomponent liquid solution subject to inter-phase mass transfer and extraction.
The simulated steady-state (or transient) mass concentration of O2(a) is lower than N2(a) because the model conditions (air-like gas-phase composition with much higher N2 partial pressure) together with incomplete mass transfer (stage efficiency ≈ 50% by 309.62 s) and kinetic/transport differences dominate the partitioning; the KB does not include aqueous O2-consuming reactions, so consumption is not responsible according to the provided mechanism.
Chemical equilibrium is not justified by the provided stage data up to 309.62 s because stage efficiency remains well below ~100% (stage mean ≈ 50.46%). Therefore observed concentrations are consistent with non-equilibrium mass transfer and extraction kinetics rather than thermodynamic equilibrium.
12. Data provenance
All data and mechanism items in this report are taken from the supplied Stage Knowledge Base: source key “stg.aqueous_phase”. Stage mass balance residuals and stage efficiency time history were used directly from that source.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ RAG = stg.aqueous_phase
+ Total # of tokens = 16536
Done with report...
1.8.2. Organic Phase#
Request a report from the LLM.
if stage_ai:
issues = ('+ Title your response as: Organic Phase Analysis.\n'
f'+ Consider stage efficiency only up to {round(stg.initial_time,2)} seconds.'
)
stage_ai.report(phase='organic', issues=issues, markdown_display=markdown_display, save_supporting_info=db_save)
Stage AI assistant: working on report...
Organic Phase Analysis
Abstract
This report analyzes the organic phase of a three-phase (aqueous–organic–vapor) mixing/extraction system using the provided Stage Knowledge Base (source: stg.organic_phase). The organic phase contains tributyl phosphate (TBP) and its hydrates and complexes with HNO3 and uranyl nitrate. Time-dependent concentration data, species characteristics, stage efficiency (considered only up to 309.62 s), reaction mechanism entries that involve the organic phase, and stage mass-balance residuals from the Stage Knowledge Base were used. Stage information (stage efficiency and mass-balance residuals) was required and consulted.
System description and scope
The system is a multiphase extraction system (aqueous–organic–vapor) where the organic phase (liquid) contains TBP (tributyl phosphate), TBP hydrates (monomer, dimer, trimer hydrates), HNO3·TBP complexes, and UO2(NO3)2·2TBP complexes. The organic phase is explicitly indicated by the tag “(o)” in species formulas and was the focus of this analysis.
Data provenance
The data used in this report come directly from the provided Stage Knowledge Base entry titled “Stage Knowledge Base (Stg-1):” organic phase (source key: stg.organic_phase). Time-history arrays, reaction mechanism entries, species characteristics, stage efficiency history, and stage mass-balance residuals were used as provided.
Chemical species (commercial names and formulas)
Species table (commercial name and chemical formula).
| Commercial name | Chemical formula | |—|—| | TBP Monomer Hydrate | H₂O·[C₄H₉O]₃PO(o) | | HNO₃–TBP complex | HNO₃·[C₄H₉O]₃PO(o) | | HNO₃–2TBP complex | HNO₃·[[C₄H₉O]₃PO]₂(o) | | Uranyl nitrate · 2TBP | UO₂(NO₃)₂·[[C₄H₉O]₃PO]₂(o) | | Free TBP | [C₄H₉O]₃PO(o) | | TBP Dimer Hydrate | [H₂O]₂·[[C₄H₉O]₃PO]₂(o) | | TBP Trimer Hexahydrate | [H₂O]₆·[[C₄H₉O]₃PO]₃(o) |
Chemical species characteristics
Species characteristics (molar mass and composition, rounded to five significant digits).
| Commercial name | Formula | Phase | Molar mass (kg/mol) | Num atoms | Num nuclide types | |—|—:|—|—:|—:|—:| | TBP Monomer Hydrate | H₂O·[C₄H₉O]₃PO(o) | (o) | 0.28433 | 47 | 4 | | HNO₃–TBP complex | HNO₃·[C₄H₉O]₃PO(o) | (o) | 0.32933 | 49 | 5 | | HNO₃–2TBP complex | HNO₃·[[C₄H₉O]₃PO]₂(o) | (o) | 0.59564 | 93 | 5 | | Uranyl nitrate · 2TBP | UO₂(NO₃)₂·[[C₄H₉O]₃PO]₂(o) | (o) | 0.92794 | 99 | 6 | | Free TBP | [C₄H₉O]₃PO(o) | (o) | 0.26631 | 44 | 4 | | TBP Dimer Hydrate | [H₂O]₂·[[C₄H₉O]₃PO]₂(o) | (o) | 0.56866 | 94 | 4 | | TBP Trimer Hexahydrate | [H₂O]₆·[[C₄H₉O]₃PO]₃(o) | (o) | 0.90704 | 150 | 4 |
Time-dependent species data (organic phase)
Time-history of organic-phase species concentrations. Time units: seconds (s). Concentration units: the Stage Knowledge Base lists time histories for concentrations but does not specify units for the species time-series in the provided entry; the numerical values below are the reported quantities from the Stage Knowledge Base (unit not specified in the source). Only data up to 309.62 s are considered for interpretation per the instructions.
| Time (s) | TBP Monomer Hydrate (H₂O·[C₄H₉O]₃PO(o)) | HNO₃–TBP complex (HNO₃·[C₄H₉O]₃PO(o)) | HNO₃–2TBP complex (HNO₃·[[C₄H₉O]₃PO]₂(o)) | Uranyl nitrate · 2TBP (UO₂(NO₃)₂·[[C₄H₉O]₃PO]₂(o)) | Free TBP ([C₄H₉O]₃PO(o)) | TBP Dimer Hydrate ([H₂O]₂·[[C₄H₉O]₃PO]₂(o)) | TBP Trimer Hexahydrate ([H₂O]₆·[[C₄H₉O]₃PO]₃(o)) | |—:|—:|—:|—:|—:|—:|—:|—:| | 0.0000 | 0.00000 | 0.00000 | 0.00000 | 0.00000 | 291.75 | 0.00000 | 0.00000 | | 20.640 | 7.6569 | 0.19533 | 0.29990 | 3.8192 | 243.26 | 33.119 | 8.7198 | | 41.280 | 10.296 | 0.67405 | 0.95367 | 17.829 | 222.20 | 41.948 | 10.202 | | 103.21 | 11.313 | 1.5900 | 1.9844 | 51.964 | 199.46 | 43.549 | 9.1041 | | 206.42 | 11.059 | 1.9132 | 2.2570 | 66.409 | 192.83 | 42.284 | 8.2570 | | 309.62 | 11.006 | 1.9540 | 2.2855 | 68.396 | 191.98 | 42.077 | 8.1424 |
Stage information: efficiency and mass-balance residuals (up to 309.62 s)
Stage efficiency time-history (considered only up to 309.62 s as requested). Values are mean ± standard deviation in percent [%]; time units in seconds.
| Time (s) | Efficiency mean (%) | Efficiency std (%) | |—:|—:|—:| | 0.0000 | 15.000 | 21.600 | | 20.640 | 30.910 | 14.290 | | 41.280 | 40.250 | 10.470 | | 103.21 | 48.940 | 3.4700 | | 206.42 | 50.340 | 1.4600 | | 309.62 | 50.460 | 1.3600 |
Interpretation: Stage efficiency values up to 309.62 s increase from 15% (±21.6%) at t = 0 s to approximately 50.46% (±1.36%) at t = 309.62 s. None of these values approach ~100% (chemical equilibrium threshold per instructions), therefore thermodynamic chemical equilibrium in the stage cannot be assumed within the considered timeframe.
Stage mass-balance residuals (total mass generation rate density) for times up to 309.62 s. Units in the source: [g/L-s]; time units: s. The reported residuals are effectively zero in the Stage Knowledge Base.
| Time (s) | Total mass generation rate density (g·L⁻¹·s⁻¹) | |—:|—:| | 0.0000 | -0.00000 | | 20.640 | -0.00000 | | 41.280 | 0.00000 | | 103.21 | -0.00000 | | 206.42 | -0.00000 | | 309.62 | -0.00000 |
Interpretation: The stage mass-balance residuals reported are effectively zero at the recorded times, indicating no significant net mass generation in the stage (mass conservation appears satisfied in the provided data). This supports consistency of the reported time histories, though no detailed species-by-species balance residuals were provided beyond the total mass residual.
Reaction mechanism (reactions involving the organic phase)
The following reactions in the Stage Knowledge Base involve the organic phase (species explicitly marked with phase (o)). Each listed reaction is reversible and is presented with the information content available in the Stage Knowledge Base, followed by a short mechanistic comment.
Reaction 1: [C₄H₉O]₃PO(o) + H₂O(a) <=> H₂O·[C₄H₉O]₃PO(o)
Information from Stage Knowledge Base: “complexation of one H2O molecule”
Additional comment: This is an interphase complexation where a TBP monomer in the organic phase binds a single aqueous H₂O molecule (phase label: H₂O(a) indicates the water is in the aqueous phase); the complex resides in the organic phase as a TBP monomer hydrate. Mechanistically this is an associative solvation/complexation event and implies transfer of one water molecule into an organic-phase solvation shell.
Reaction 2: 2 [C₄H₉O]₃PO(o) + 2 H₂O(a) <=> [H₂O]₂·[[C₄H₉O]₃PO]₂(o)
Information from Stage Knowledge Base: “complexation of two H2O molecules”
Additional comment: Two TBP molecules in the organic phase complex two water molecules (each likely associated with TBP), forming a dimeric TBP hydrate in the organic phase. This again is an extraction/solvation step and requires water transfer from the aqueous to the organic phase.
Reaction 3: 3 [C₄H₉O]₃PO(o) + 6 H₂O(a) <=> [H₂O]₆·[[C₄H₉O]₃PO]₃(o)
Information from Stage Knowledge Base: “complexation of six H2O molecules”
Additional comment: Formation of a trimeric TBP hydrate incorporating six water molecules. This likely arises at higher local water activity and represents stronger solvation/aggregation of TBP with multiple water molecules in the organic phase.
Reaction 4: H⁺(a) + NO₃⁻(a) + [C₄H₉O]₃PO(o) <=> HNO₃·[C₄H₉O]₃PO(o)
Information from Stage Knowledge Base: “one TBP complexation of HNO3”
Additional comment: Proton and nitrate from the aqueous phase combine (effectively HNO₃) with one TBP in the organic phase to form an HNO₃–TBP complex in the organic phase. This is a classic acid extraction complexation step (neutral complex formation) where TBP extracts nitric acid from the aqueous phase by forming a neutral HNO₃·TBP complex in the organic phase.
Reaction 5: H⁺(a) + NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <=> HNO₃·[[C₄H₉O]₃PO]₂(o)
Information from Stage Knowledge Base: “two TBP complexation of HNO3”
Additional comment: Similar to Reaction 4 but forming a HNO₃ complex coordinated by two TBP molecules (2:1 TBP:HNO₃ stoichiometry). This increases organic-phase solubility of nitric acid and is commonly observed in TBP extraction chemistry at higher TBP loading.
Reaction 6: UO₂²⁺(a) + 2 NO₃⁻(a) + 2 [C₄H₉O]₃PO(o) <=> UO₂(NO₃)₂·[[C₄H₉O]₃PO]₂(o)
Information from Stage Knowledge Base: “uranyl nitrate complexation with 2TBP”
Additional comment: This is the uranyl nitrate extraction step: uranyl ion and nitrate from the aqueous phase form a neutral UO₂(NO₃)₂ species that is complexed by two TBP molecules in the organic phase, producing the hydrophobic UO₂(NO₃)₂·2TBP complex. Mechanistically this is the key metal extraction complexation reaction enabling transfer of UO₂²⁺ from aqueous to organic phase.
Verification of completeness: The Stage Knowledge Base lists the TBP hydrates (mono-, di-, tri-hydrates), HNO₃–TBP complexes (1:1 and 1:2), and UO₂(NO₃)₂·2TBP complexation. No additional organic-phase-involving reactions were present in the provided mechanism besides these entries; the remaining listed reactions in the source involve only aqueous–vapor exchanges (H₂O vaporization, O₂ and N₂ aeration) and are not commented on here per instructions.
Mechanistic remarks and interpretation
The organic-phase chemistry is dominated by complexation (coordination/solvation) reactions: TBP forms solvates with water (mono-, di-, trimer hydrates) and forms neutral complexes with nitric acid and uranyl nitrate. These reactions reflect typical liquid–liquid extraction chemistry with TBP as an extractant.
Time-history interpretation (up to 309.62 s):
Free TBP concentration decreases markedly from 291.75 (initial) to ~191.98 (at 309.62 s), concurrent with increases in TBP-bound species (especially the uranyl nitrate · 2TBP complex and TBP dimer hydrate). This indicates uptake of inorganic solutes (HNO₃, UO₂²⁺ with NO₃⁻) and water into the organic phase.
The UO₂(NO₃)₂·2TBP complex rises strongly between t = 0 and t = 309.62 s (from 0 to ~68.396 in reported units), indicating active extraction of uranyl into the organic phase over this period.
HNO₃–TBP complexes (both 1:1 and 1:2) also increase over time (to ~1.95 and ~2.29, respectively, at 309.62 s), consistent with nitric acid extraction that often accompanies metal extraction.
TBP hydrates increase initially and then reach plateau-ish values (monomer hydrate ~11.0; dimer hydrate ~42; trimer hydrate ~8.14 at 309.62 s), indicating significant solvation of TBP by water transferred from the aqueous phase.
Given the stage efficiency values (≤ ~50% at 309.62 s) and non-unity efficiency, the system should not be assumed to have reached chemical equilibrium in the stage by 309.62 s; reaction extents are partial and kinetically or transport-limited within the stage timeframe.
Conclusions and recommendations
Conclusions:
The organic phase (liquid organic solvent containing TBP) contains free TBP, TBP hydrates (mono-, di-, trimer), HNO₃–TBP complexes (1:1 and 1:2), and UO₂(NO₃)₂·2TBP. The mixture is a multiphase extraction mixture (aqueous–organic–vapor system) with the organic phase actively extracting nitric acid and uranyl nitrate from the aqueous phase.
Stage efficiency values up to 309.62 s (maximum ≈ 50.46% ± 1.36%) indicate that the stage is not at thermodynamic chemical equilibrium; therefore, reported species distributions represent non-equilibrium (partial extraction) states.
Mass-balance residuals reported for the stage are effectively zero (Total Mass Generation Rate Density ≈ 0 g·L⁻¹·s⁻¹ at recorded times), suggesting global mass conservation in the provided stage data.
Recommendations (succinct):
If the aim is to reach near-equilibrium extraction in this stage, extend contact time and/or improve mass transfer (mixing or interfacial area) because stage efficiency is ≈50% at 309.62 s. Monitor stage efficiency over longer times to determine asymptotic approach to equilibrium.
If quantitative unit-aware concentrations are required for modelling or design, request or confirm the concentration units for species time-histories from the data provider (Stage Knowledge Base did not specify per-species concentration units in the provided entry).
Additional notes
The analysis used stage-level information (stage efficiencies and mass-balance residuals) and the explicit word “stage” has been referenced in the report where stage-level data were required.
No comment on per-species mass conservation beyond the provided total mass residuals was possible because per-species balance residuals were not provided.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ RAG = stg.organic_phase
+ Total # of tokens = 17596
Done with report...
1.8.3. Vapor Phase#
Request a report from the LLM.
if stage_ai:
issues = ('+ Title your response as: Vapor Phase Analysis.\n'
f'+ Consider stage efficiency only up to {round(stg.initial_time,2)} seconds.'
)
stage_ai.report(phase='vapor', issues=issues, markdown_display=markdown_display, save_supporting_info=db_save)
Stage AI assistant: working on report...
Vapor Phase Analysis
Executive summary
This report analyzes the vapor phase content and behavior from the provided Stage Knowledge Base (source: stg.vapor_phase). The system is an aqueous–organic–vapor multi‑phase mixing system. Time‑series mass concentrations (g/L) for vapor species H₂O(v), N₂(v), and O₂(v) are presented and evaluated together with stage performance data (stage overall efficiency) considered only up to 309.62 s as requested. Reaction mechanism entries that involve the vapor phase are extracted, itemized, and analyzed. Mass balance residuals are reported and used to comment on mass conservation.
Data sources and scope
The data used are from the Stage Knowledge Base entry “stg.vapor_phase”.
Time base: seconds (s). Concentration units: grams per liter (g/L).
Stage efficiency is considered only for timepoints <= 309.62 s (explicitly used: 0.0, 20.64, 41.28, 103.21, 206.42, 309.62 s).
The analysis relies on the provided stage module information (stage); therefore stage-level data were consulted.
Species characteristics
Species characteristics (commercial name and chemical formula with phase) and selected properties.
Table: Chemical species characteristics (molar mass in kg/mol; number of atoms; nuclide types; phase)
| Commercial name | Chemical formula (phase) | Molar mass [kg/mol] | Number of atoms | Number of nuclide types | |—|—:|—:|—:|—:| | Water (vapor) | H₂O(v) | 0.018015 | 3.0 | 2 | | Nitrogen (vapor) | N₂(v) | 0.028013 | 2.0 | 1 | | Oxygen (vapor) | O₂(v) | 0.031999 | 2.0 | 1 |
Time‑dependent vapor species concentrations
Mass concentrations of vapor species vs time. Time unit: seconds (s). Concentration unit: grams per liter (g/L).
Table: Time history of vapor phase mass concentrations (g/L)
| Time [s] | H₂O(v) [g/L] | N₂(v) [g/L] | O₂(v) [g/L] | |—:|—:|—:|—:| | 0.0000 | 0.017876 | 0.81754 | 0.25142 | | 20.640 | 0.023215 | 0.84912 | 0.25380 | | 41.280 | 0.025531 | 0.86938 | 0.25525 | | 103.21 | 0.027114 | 0.89705 | 0.25716 | | 206.42 | 0.027199 | 0.90705 | 0.25783 | | 309.62 | 0.027190 | 0.90840 | 0.25792 | | 356.07 | 0.027160 | 0.90852 | 0.25793 | | 402.51 | 0.027113 | 0.90857 | 0.25793 | | 448.96 | 0.027084 | 0.90859 | 0.25793 |
Notes: Values rounded to 5 significant digits. The vapor water concentration increases from ~0.017876 g/L to ≈0.02719 g/L and then plateaus; N₂ and O₂ show monotonic increases toward apparent steady values.
Stage overall efficiency (considered up to 309.62 s)
Stage performance is summarized as mean ± standard deviation [%] at discrete times. Only timepoints <= 309.62 s were used for thermodynamic equilibrium consideration as requested.
Table: Stage efficiency time history (mean ± std) — times in seconds, efficiency in percent [%]
| Time [s] | Mean efficiency [%] | Std [%] | |—:|—:|—:| | 0.0000 | 15.000 | 21.600 | | 20.640 | 30.910 | 14.290 | | 41.280 | 40.250 | 10.470 | | 103.21 | 48.940 | 3.4700 | | 206.42 | 50.340 | 1.4600 | | 309.62 | 50.460 | 1.3600 |
Interpretation: Stage efficiency stabilizes near ~50.46% (±1.36%) by 309.62 s. This is substantially below ~100%, so chemical equilibrium (thermodynamic equilibrium) should not be assumed for stage‑level reactions.
Stage mass balance residuals
Total mass generation rate density time history. Units: g·L⁻¹·s⁻¹ (g/L-s); time: s.
Table: Stage mass balance residuals (g/L-s)
| Time [s] | Total mass generation rate density [g/L-s] | |—:|—:| | 0.0000 | 0.00000 | | 20.640 | 0.00000 | | 41.280 | 0.00000 | | 103.21 | 0.00000 | | 206.42 | 0.00000 | | 309.62 | 0.00000 | | 356.07 | 0.00000 | | 402.51 | 0.00000 | | 448.96 | 0.00000 |
Comment: The provided mass balance residuals are effectively zero at the reported precision, indicating no detectable net mass generation or loss in the stage-level accounting for these timepoints.
Reactions involving the vapor phase
Only reactions that explicitly involve vapor phase species (species with phase marker (v)) are considered here. Reactions that do not involve vapor phase species are not discussed per instructions.
Itemized list of vapor‑phase reactions and available information from the Stage Knowledge Base:
Reaction 1:
Reaction: H₂O(a) <=> H₂O(v)
Information content from Stage Knowledge Base: “H2O vaporization in air”
Additional note: This reaction represents phase transfer (vaporization/condensation) of water between the aqueous phase (a) and the vapor phase (v). The mechanism is mass transfer limited and depends on interfacial area, temperature, and partial pressure gradients; it is not necessarily at chemical equilibrium because stage efficiency is ≈50% by 309.62 s.
Reaction 2:
Reaction: O₂(v) <=> O₂(a)
Information content from Stage Knowledge Base: “O2 aeration of aqueous phase”
Additional note: This is a gas–liquid mass transfer (aeration) process for oxygen. The direction and rate are determined by dissolved oxygen concentration, Henry’s law partitioning, and mass transfer coefficients; aeration can proceed until saturation or dynamic steady state, but equilibrium requires near‑complete stage efficiency which is not observed here.
Reaction 3:
Reaction: N₂(v) <=> N₂(a)
Information content from Stage Knowledge Base: “N2 aeration of aqueous phase”
Additional note: Similar to O₂ exchange, this is nitrogen gas exchange between vapor and aqueous phases. Nitrogen is less soluble than oxygen; mass transfer will be governed by partial pressure differences and solubility (Henry’s law) and is expected to equilibrate more slowly.
Double check for missing vapor‑phase reactions: the mechanism content provided lists other complexation reactions that involve organic (o) and aqueous (a) species but none of those include (v) species. Therefore, beyond the three above, no additional vapor‑phase reactions are present in the provided mechanism.
Reaction mechanism commentary
The vapor‑phase reactions are phase‑transfer processes (vaporization/condensation, aeration) rather than intrinsic chemical transformations (no gas‑phase chemical reaction or gas‑phase radical reactions are listed).
Mechanistically, these are controlled by mass transfer across phase boundaries and by gas–liquid equilibria (Henry’s law for O₂ and N₂; vapor pressure/temperature dependence for H₂O). Kinetic limitations, interfacial area, and local hydrodynamics will determine rates.
Because stage efficiency remains ≈50% at 309.62 s, the system is not at chemical equilibrium between phases; mass transfer is only partially effective at achieving equilibrium within the stage module during the observed period.
Discussion on thermodynamic equilibrium and stage efficiency
Thermodynamic (chemical) equilibrium between phases would require near‑complete equilibration (stage efficiency ~100%). The measured stage efficiency time history shows mean values rising from 15% to ≈50.46% by 309.62 s, with decreasing standard deviation — far below 100%.
Therefore, do not assume phase equilibrium for H₂O, O₂, or N₂ at or before 309.62 s. Observed concentration trends (H₂O increasing and then plateauing; N₂ and O₂ increasing) are consistent with ongoing mass transfer toward equilibrium but not yet complete at the stage scale.
The provided zero mass balance residuals indicate the stage accounting conserves mass within reported precision; lack of net mass generation suggests observed concentration changes are due to redistribution among phases rather than source/sink mass creation.
Conclusions
The vapor phase contains Water (vapor) H₂O(v), Nitrogen (vapor) N₂(v), and Oxygen (vapor) O₂(v) in the reported multi‑phase system (aqueous–organic–vapor mixture).
Time trends show vapor concentrations moving toward steady values; H₂O(v) increases from ~0.017876 g/L to ~0.02719 g/L, while N₂(v) and O₂(v) increase to ~0.90859 g/L and ~0.25793 g/L, respectively.
Stage efficiency up to 309.62 s is ≈50.46% (±1.36%), well below the ~100% required for thermodynamic equilibrium; therefore chemical equilibrium between phases should not be assumed.
The relevant reactions are phase‑transfer processes (vaporization and aeration) rather than chemical transformations; kinetics are expected to be mass transfer limited.
Key recommendations
To assess approach to equilibrium, extend monitoring beyond 309.62 s and/or run experiments under improved mass transfer (increased interfacial area or mixing) to raise stage efficiency toward 100%.
Record temperature and pressure concurrently with concentrations to enable conversion to partial pressures and application of Henry’s law / vapor pressure correlations for quantitative phase‑equilibrium analysis.
If quantitative kinetic modeling is desired, obtain or estimate gas–liquid mass transfer coefficients (k_La), interfacial area, and Henry’s constants for O₂ and N₂ at the system temperature.
Data provenance
Primary source: Stage Knowledge Base entry “stg.vapor_phase”. Reaction mechanism header (metadata) indicates a prototype extraction context and includes a comment line timestamp from upstream data. The stage module information (stage) and time histories for species, stage efficiency, and mass balance residuals were used directly as provided.
AI Parameters:
+ LLM model (OpenAI) = gpt-5-mini
+ LLM cleverness = 1.0
+ RAG = stg.vapor_phase
+ Total # of tokens = 12987
Done with report...
1.9. References#
[1] V. F. de Almeida, Cortix, Network Dynamics Simulation, Cortix Tech, Lowell, MA 01854, USA.