Reaction Engineering Patterns
Guide for chemical reactor modeling in NeqSim using the reactor equipment classes.
Reactor Type Selection
| Reactor Type | NeqSim Class | When to Use |
|---|---|---|
| Equilibrium | GibbsReactor |
High-T reactions approaching equilibrium (reforming, combustion, water-gas shift) |
| Plug Flow (PFR) | PlugFlowReactor |
Tubular reactors with axial kinetics, catalytic fixed beds |
| Stirred Tank (CSTR) | StirredTankReactor |
Perfectly mixed, steady-state with kinetics |
| Stoichiometric | StoichiometricReaction |
Known fixed conversion, simple mass balance |
| CO2-specific equilibrium | GibbsReactorCO2 |
CO2 capture reactions with optimized convergence |
| Fermenter | Fermenter |
Bio-reactions, enzymatic processes |
Equilibrium Reactor (GibbsReactor)
Best for high-temperature reactions where equilibrium is reached.
Setup Pattern
import neqsim.process.equipment.reactor.GibbsReactor;
import neqsim.process.equipment.stream.Stream;
import neqsim.thermo.system.SystemSrkEos;
// Create feed fluid with reactants
SystemInterface fluid = new SystemSrkEos(273.15 + 800.0, 30.0);
fluid.addComponent("methane", 1.0);
fluid.addComponent("water", 3.0); // Steam-to-carbon ratio = 3
fluid.addComponent("hydrogen", 0.0001); // Products must be present
fluid.addComponent("CO", 0.0001);
fluid.addComponent("CO2", 0.0001);
fluid.setMixingRule("classic");
Stream feed = new Stream("feed", fluid);
feed.setFlowRate(1000.0, "kg/hr");
// Configure Gibbs reactor
GibbsReactor reactor = new GibbsReactor("SMR Reactor", feed);
reactor.setEnergyMode(GibbsReactor.EnergyMode.ISOTHERMAL);
reactor.setMaxIterations(5000);
reactor.setConvergenceTolerance(1e-8);
reactor.addInertComponent("nitrogen"); // Exclude from reaction
reactor.run();
// Check results
if (reactor.hasConverged()) {
Stream outlet = (Stream) reactor.getOutletStream();
double methaneConversion = reactor.getComponentConversion("methane");
double massBalErr = reactor.getMassBalanceError();
}
Adiabatic Mode
reactor.setEnergyMode(GibbsReactor.EnergyMode.ADIABATIC);
reactor.run();
double outletTemp = reactor.getOutletStream().getTemperature() - 273.15; // °C
double heatOfReaction = reactor.getEnthalpyOfReactions(); // W
Kinetic PFR (PlugFlowReactor)
For reactions with known kinetics and axial profiles.
Define Reactions
import neqsim.process.equipment.reactor.KineticReaction;
import neqsim.process.equipment.reactor.CatalystBed;
import neqsim.process.equipment.reactor.PlugFlowReactor;
// Define reaction: CH4 + H2O -> CO + 3H2
KineticReaction smr = new KineticReaction("Steam Methane Reforming");
smr.addReactant("methane", 1, 1.0); // (component, stoich coeff, reaction order)
smr.addReactant("water", 1, 0.5);
smr.addProduct("CO", 1);
smr.addProduct("hydrogen", 3);
smr.setPreExponentialFactor(1.17e15); // mol/(m3·s)
smr.setActivationEnergy(240100.0); // J/mol
smr.setHeatOfReaction(206000.0); // J/mol (endothermic = positive)
smr.setReactionType(KineticReaction.ReactionType.POWER_LAW);
Configure Catalyst
CatalystBed catalyst = new CatalystBed();
catalyst.setParticleDiameter(3.0, "mm");
catalyst.setVoidFraction(0.40);
catalyst.setBulkDensity(800.0); // kg/m3
catalyst.setCatalystDensity(2100.0); // kg/m3
catalyst.setTortuosity(3.5);
catalyst.setSpecificSurfaceArea(50.0); // m2/g
Build and Run PFR
PlugFlowReactor pfr = new PlugFlowReactor("PFR-1", feedStream);
pfr.addReaction(smr);
pfr.setCatalystBed(catalyst);
pfr.setLength(5.0, "m");
pfr.setDiameter(0.5, "m");
pfr.setNumberOfIncrements(200);
pfr.setIntegrationMethod(PlugFlowReactor.IntegrationMethod.RK4);
pfr.setEnergyMode(PlugFlowReactor.EnergyMode.ADIABATIC);
pfr.run();
// Get axial profiles
double[] positions = pfr.getAxialPositions();
double[] temperatures = pfr.getTemperatureProfile();
double[] conversions = pfr.getConversionProfile("methane");
double[] pressures = pfr.getPressureProfile();
Cooled PFR with External Heat Exchange
pfr.setEnergyMode(PlugFlowReactor.EnergyMode.COOLANT);
pfr.setCoolantTemperature(273.15 + 300.0); // Kelvin
pfr.setOverallHeatTransferCoefficient(150.0); // W/(m2·K)
CSTR (StirredTankReactor)
For perfectly mixed continuous reactors.
import neqsim.process.equipment.reactor.StirredTankReactor;
StirredTankReactor cstr = new StirredTankReactor("CSTR-1", feedStream);
cstr.addReaction(reaction);
cstr.setVolume(10.0); // m3
cstr.run();
double outletConcentration = cstr.getOutletStream()
.getFluid().getComponent("product").getx();
Stoichiometric Reactor
For known conversions without kinetics.
import neqsim.process.equipment.reactor.StoichiometricReaction;
StoichiometricReaction reactor = new StoichiometricReaction("fixed-conv", feed);
// Specify fixed conversion
reactor.run();
Process Integration with Reactors
ProcessSystem process = new ProcessSystem();
Stream feed = new Stream("feed", fluid);
feed.setFlowRate(5000.0, "kg/hr");
process.add(feed);
// Pre-heat
Heater preheater = new Heater("preheat", feed);
preheater.setOutTemperature(273.15 + 800.0);
process.add(preheater);
// React
GibbsReactor reactor = new GibbsReactor("reactor", preheater.getOutletStream());
reactor.setEnergyMode(GibbsReactor.EnergyMode.ISOTHERMAL);
process.add(reactor);
// Cool product
Cooler cooler = new Cooler("cool", reactor.getOutletStream());
cooler.setOutTemperature(273.15 + 40.0);
process.add(cooler);
// Separate
Separator sep = new Separator("flash", cooler.getOutletStream());
process.add(sep);
process.run();
Key Analysis Metrics
| Metric | How to Calculate |
|---|---|
| Conversion | reactor.getComponentConversion("methane") |
| Mass balance error | reactor.getMassBalanceError() |
| Heat of reaction | reactor.getEnthalpyOfReactions() |
| Outlet temperature | reactor.getOutletStream().getTemperature() - 273.15 |
| Convergence | reactor.hasConverged() |
Common Pitfalls
- Products must be in feed: For GibbsReactor, add trace amounts (1e-4) of expected products
- Missing inerts: Always mark inert components with
addInertComponent() - Convergence: Increase
setMaxIterations()and decreasesetConvergenceTolerance()for difficult systems - Damping: Use
setDampingComposition(0.01)for oscillating systems - Energy mode: Choose ISOTHERMAL vs ADIABATIC based on the physical reactor — wrong choice gives wrong results
- Unit consistency: Activation energy in J/mol, heat of reaction in J/mol
Bioprocessing Reactors
NeqSim includes specialized bioreactors beyond the generic StirredTankReactor/Fermenter.
AnaerobicDigester
Substrate-specific biogas production from organic waste. Not a flash-based reactor — uses empirical biogas yield models.
AnaerobicDigester digester = new AnaerobicDigester("AD-1", feedStream);
digester.setSubstrateType(AnaerobicDigester.SubstrateType.FOOD_WASTE);
// Options: FOOD_WASTE, SEWAGE_SLUDGE, MANURE, CROP_RESIDUE, MIXED_WASTE
digester.setDigesterTemperature(37.0, "C"); // mesophilic
digester.setFeedRate(10000.0, 0.25); // kg/hr, total solids fraction
digester.setVesselVolume(6000.0); // m³
digester.run();
// Outputs: getBiogasOutStream(), getDigestateOutStream()
// Metrics: getMethaneProductionNm3PerDay(), getBiogasFlowRateNm3PerDay()
FermentationReactor
Extends Fermenter with kinetic models and operation modes.
FermentationReactor reactor = new FermentationReactor("FR-1", sugarFeed);
reactor.setKineticModel(FermentationReactor.KineticModel.MONOD);
// Options: MONOD, CONTOIS, SUBSTRATE_INHIBITED
reactor.setOperationMode(FermentationReactor.OperationMode.CONTINUOUS);
// Options: BATCH, FED_BATCH, CONTINUOUS
reactor.setSubstrateConcentration(100.0); // g/L
reactor.setBiomassConcentration(1.0); // g/L
reactor.setMaxSpecificGrowthRate(0.30); // 1/hr
reactor.setMonodConstant(1.0); // g/L (Ks)
reactor.setYieldBiomass(0.10); // g biomass / g substrate
reactor.setYieldProduct(0.45); // g product / g substrate
reactor.setResidenceTime(10.0, "hr");
reactor.run();
Map<String, Object> results = reactor.getResults();
// Keys: substrateConversion, finalProductConc_g_per_L, finalBiomassConc_g_per_L,
// productivity_g_per_L_hr
BiogasUpgrader
Splits biogas into biomethane + offgas using technology-specific split factors.
BiogasUpgrader upgrader = new BiogasUpgrader("BGU-1", biogasStream);
upgrader.setTechnology(BiogasUpgrader.UpgradingTechnology.MEMBRANE);
// Options: WATER_SCRUBBING, AMINE_SCRUBBING, PSA, MEMBRANE
upgrader.run();
// Outputs: getBiomethaneOutStream(), getOffgasOutStream()
// Enum getters: tech.getMethaneRecovery(), tech.getCo2RemovalEfficiency()
Pre-built Biorefinery Modules
| Module | Purpose | Key Methods |
|---|---|---|
BiogasToGridModule |
AD → upgrading → compression → grid | setGridPressureBara(), setGridTemperatureC(), getResults() |
GasificationSynthesisModule |
Biomass → syngas → FT liquids | setBiomass(BiomassCharacterization, kgPerHr) |
WasteToEnergyCHPModule |
AD → gas engine CHP | setElectricalEfficiency(), getElectricalPowerKW(), getHeatOutputKW() |
SustainabilityMetrics
Utility class for CO₂-equivalent tracking and LCA:
SustainabilityMetrics metrics = new SustainabilityMetrics();
metrics.setBiogasProductionNm3PerYear(3_000_000.0);
metrics.setMethaneContentFraction(0.60);
metrics.setMethaneSlipPercent(1.5);
metrics.setElectricityProductionMWhPerYear(8000.0);
metrics.setHeatProductionMWhPerYear(10000.0);
metrics.setParasiticElectricityMWhPerYear(800.0);
metrics.setFossilReferenceEmissionFactor(0.450); // kgCO2/kWh
metrics.setFossilHeatEmissionFactor(0.250);
metrics.calculate();
// getTotalEmissionsTCO2eqPerYear(), getCarbonIntensityKgCO2PerMWh()
// getFossilFuelDisplacementTCO2PerYear(), getNetCarbonBalanceTCO2PerYear()
// getEnergyReturnOnInvestment(), getRenewableEnergyFraction()
Uncontrolled and self-sustaining reactions
Two distinct escalation questions sit outside the reactor classes above, and the choice between them depends on whether heat loss is spatially resolved:
| Question | Model | Class |
|---|---|---|
| A charged batch reacts adiabatically — how hot does it get, and how long until maximum rate? | Lumped adiabatic (MTSR, dT_ad, TMR_ad) | neqsim.process.safety.reaction.RunawayReactionAnalyzer |
| A material generates heat throughout its volume and conducts it to a boundary — will it self-ignite, at what size and after how long? | Frank-Kamenetskii / Semenov criticality | neqsim.process.safety.selfheating |
RunawayReactionAnalyzer assumes a well-stirred mass with no spatial
conduction, so it has no concept of a critical thickness or a critical ambient
temperature. Do not use it to assess spontaneous ignition of a combustible liquid
absorbed into porous insulation (a lagging fire) — see
neqsim-self-heating-ignition.
Note also that KineticReaction and GibbsReactor are complementary but not
interchangeable for ignition questions: equilibrium will report complete oxidation
of any hydrocarbon at ambient temperature, which says nothing about whether the
reaction proceeds at a measurable rate. Ignition is always a kinetic question.