Process: BFD, PFD and streams¶
The block flow diagram (BFD) and process flow diagram (PFD) are generated, never drawn by hand.
Their source is two YAML files, schema cheme/process/v1:
process/materials.yaml(kindprocess-materials): components (id, name, formula, CAS, molar mass, optional InChIKey and SMILES, checked bycheme validate), templates (ordered component lists) and the property method;process/flowsheet.yaml(kindprocess-flowsheet): units (every value without its own unit is in these; pressure absolute, gauge only as an explicit unit,Nm3/honly withnormal_conditions), cases, the balance tolerance,feedsandproducts(DEXPISourceandSink),blocksandstepswith explicitports(in/out, material/energy/information),streamsfrom port to port with a per-casestate(pressure, temperature, flow, duty, composition, phase),linksto the lists and sheets, andlayout.bfd/layout.pfdpins. Each value carriesprovenance: given | assumed | calculatedand, when given, asource. A value or composition may give anuncertainty(the LCA's shape, e.g.{gsd2: 1.2}or{distribution: triangular, low: 0.9, high: 1.1});cheme assess lcasamples a feed's or product's flow with it, and no calculated value's fingerprint includes it. An optionaloriginsays what kind of figure it is: givenmeasured,vendororliterature; assumeddesign-rule,estimateorliterature; calculatedsimulated(cheme process simulate --fillwrites it) orcorrelation(cheme process props --fill,cheme datasheets fill). Another pairing ischeme.provenance-origin(warning). A DEXPI sheet mapsmeasuredto OBSERVED, andlca.yaml'suncertainty.defaultsmay be keyed by an origin (measured: {gsd2: 1.02}), which wins over the provenance's.
plant.yaml declares the sheets by kind: {id: BFD, kind: bfd, file: process/BFD.xml} and
{id: PFD, kind: pfd, file: process/PFD.xml}. A kind: validate gate with generated: [pfd]
(the template's process-basic) fails until the PFD is declared and built.
plant.yaml's requires: cheme-as-code>=X.Y (written by cheme init) names the oldest
cheme-as-code the repository works with; an older one stops every command, the pre-commit hook
included, with cheme.version-required and an upgrade hint before any schema error.
cheme process build [SHEET...] [--force]writes DEXPI 2.0.1ProcessModels with a drawing. The BFD shows blocks, feeds and products and every stream that crosses a block boundary; the PFD nests the steps in their blocks, with block ports delegating to step ports (superReference), and adds the internal streams. Values becomeQualifiedValues with their provenance and case. The layout is layered left to right with recycles routed back, labels placed clear of lines and nodes, and thelayout.<view>pins applied. A rebuild is byte-identical; a title rename moves nothing.- The PFD drawing (ADR 0046). The sheet is the smallest that holds the drawing and the stream
table: ISO 216 A3 to A0 or an ISO 5457 elongated one (A3x3 to A0x3), landscape, with the
frame, zone markers and a title block (title, project, drawing number, case, sheet). A layout
wider than an A0 wraps into rows, read like text. Steps are drawn with the ISO symbol of
their class (a step's optional
symbol:overrides it: a symbol name,junctionorbox; an unknown name ischeme.process-symbol, warning); mixing and splitting steps are junction tees. A utility terminal (category: utility, autility:given, or only energy ports) is not a box with a line to every user: each of its streams is a short stub on top of the step it serves, with the utility's id. Process lines are drawn heavier than equipment, with flow arrowheads; blocks holding steps are light dash-dot process areas. Below the drawing the stream table lists each material stream's phase, temperature (°C), pressure (bar), mass flow and the flows of the largest components, and each energy stream's duty, for the default case (assumed values marked*, stale ones blank). The BFD gets the same frame and title block. - Calculated values and phase in the sheets. Each material stream's
MaterialState(itsMaterialStateType) carries thestate.calculatedvalues DEXPI 2.0.1 has a property for:density(kg/m3) andspecificEnthalpy, oneQualifiedValueper case with the YAML provenance (calculated: CALCULATED) and source. DEXPI bindsspecificEnthalpyto mass units, so kJ/kmol is divided by the case's molar mass (kJ/kg; the display text keeps the kJ/kmol value, the description names the molar mass and the enthalpy reference); without a molar mass the enthalpy is not aQualifiedValue. The phase is the material state'sphases: oneMaterialStateTypeper phase present (identifiervapororliquid, two-phase gives both), its description<case>: <phase>, vapour fraction <x>; <provenance>; source: .... DEXPI has no property for the molar mass (or any other calculated value): those are linescalculated molar_mass (<case>) = 28.96 kg/kmol; source: ...in the state type's description; no class or attribute is invented. Stale values are not exported: a value or phase reported bycheme.process-calculated-staleis left out of the sheet, and the description saysstale, not exported (<case>): density, phase, so a DEXPI consumer never reads a value its inputs no longer give. Because staleness follows from the YAML, the sheet isgenerated-outdateduntil rebuilt; recalculate (props --fill) and rebuild to export again. - BFD aggregation.
bfd: {aggregate: true}in the flowsheet draws parallel boundary streams between the same two BFD nodes (same kind, same direction) as one connection;aggregate: [[A, B], ...]limits it to those pairs (cheme.process-aggregateif one is not on the BFD). The connection keeps its first stream's id and ports, its label and description list every stream (represents: S2 S3), and it carries no values; the PFD, stream table and balances are unchanged (examples/process-aggregate/). - Refinement keeps stream ids. When a block gets steps, a boundary stream keeps its id and
moves its end from
BLOCK.porttoSTEP.port, whose portdelegatesthe block port. Balances count it once; the BFD and its balances are unchanged by a refinement (examples/process-blocks/before,examples/process-recycle/after). - Nested blocks. A block with
parent: Pis drawn inside P on the PFD (with its own steps and nested blocks) and is a sub process step of P; the BFD shows top-level blocks. Its portsdelegateP's ports where a stream crosses P's boundary, just like step ports (examples/process-nested/). Inside a block, ports are ordered by where their lines go and the step order with the fewest crossings wins. - Generated-sheet provenance. Each sheet records a fingerprint of its YAML source and of its
own XML.
validatereportscheme.generated-outdated(warning: the YAML changed, rebuild),cheme.generated-edited(error: the XML was edited by hand;buildrefuses it unless--force) andcheme.generated-version(warning: cheme-as-code's generator version, generator code (generator_sha256, a hash of thechemeascode.domains.processsources) or schema version changed). A YAML edit never reportsgenerated-edited. - Balances. Steady-state, non-reactive mass and component balances per block and per step,
on its boundary streams (internal streams cancel), with
|in - out| <= abs + rel * max(in, out), in the default case and every other case that streams give a state for.cheme.process-balance(error) names the block, the case and both sums;process-balance-not-evaluable(info, default case only: in another case a balance with missing values is not reported) names the missing values;process-component-balance-not-evaluable(info) a mass balance that closes while the component balance lacks compositions (accepted by the concept gate, not byprocess-basic). - Reactions live in
process/reactions.yaml(schemacheme/reactions/v1,cheme schema process-reactions): stoichiometry over the component ids (reactants negative), an optional conversion of a key component, heat of reaction, catalyst and sources.validatechecks each reaction's element balance from the components'formula(cheme.reaction-*). A step or block namingreactions: [ID, ...](optionallyconversion: {ID: fraction}), or a block withbalance_scope: reactive, balances mass and elements instead of components, and the component changes must be explained by extents of its reactions (least squares;process streamsprints the extents). A block holding a reactor step is reactive too.reactor: equilibriumtakes the named reactions to equilibrium at the outlet's temperature (plusapproach) and pressure,reactor: gibbsminimises the Gibbs energy over every component of the streams (both ideal gas, inprocess propagate), with ln K from each component'sideal_gasdata (formation enthalpy, entropy, Cp/R polynomial) or a reaction'sequilibrium_constant. Withfugacity: peng_robinsonthe equilibrium is that of a real gas (Peng-Robinson fugacity coefficients from the components'critical:constants;domains/chemistry/eos.py).examples/process-ammoniais an ammonia synthesis loop with a real-gas equilibrium converter, purge and recycle. - Fixed beds (
reactor: pfrwithcatalyst:andbed:):process propagateintegrates the reactions' rate laws along the catalyst mass (1D plug flow, adiabatic, isothermal or cooled, Ergun pressure drop;domains/process/fixed_bed.py) and writes the outlet's composition, flow, temperature and pressure;process reactorsadds the bed (catalyst mass and volume, pressure drop, hot spot, profile).datasheets fillsizes a reactor's datasheet from its bed (or the catalyst's space velocity) andassess equipmentadds its first catalyst fill at the catalyst's price.examples/process-shiftis an adiabatic high-temperature shift bed. cheme process reactors [--case C] [--json]reports per reactor its model, the extents, each reaction's heat and ln K at the outlet,ln(Q/K)(0 at equilibrium) and the duty H(out) - H(in) on the elements basis, ideal gas.- Catalysts live in
process/catalysts.yaml(schemacheme/catalysts/v1): active phase, promoters, support, particle and bed data, the temperature and pressure window, space velocity, poisons, deactivation and price, each with provenance. A reactor names itscatalyst:;validatewarns when its streams leave the window or a feed carries a poison. Reactions take arate:(power law or LHHW, per kg of catalyst or per m3, optional(1 - Q/K)), whichprocess reactorsevaluates at the outlet. A reaction'scatalyst:, itsrate.catalystand itsmechanism.catalystname a catalyst by id;validatewarns on one the file does not declare (cheme.catalyst-unknown). - Reactive hazard screening:
cheme process reactor-hazards [--json]gives each reactor's adiabatic rise, MTSR, MTT and, with a TD24 from calorimetry (hazard:on the reactor), Stoessel's criticality class; a cooled bed's hot-spot sensitivity (domains/safety/reaction_hazards.py, findingscheme.reactor-hazard-*). - Reactor choice:
cheme process reactor-choice NODE --product P [--key K] [--tanks N] [--svg FILE] [--json | --csv]compares an ideal PFR, one CSTR and N CSTRs in series for a step's reactions: yield and selectivity of P against the key's conversion over a range of space times, the largest yield of each, and the PFR's instantaneous selectivity (domains/process/reactor_choice.py; the SVG in light and dark);--region-svg FILEadds the candidate attainable region (domains/process/attainable_region.py). - Reactor uncertainty:
cheme process reactor-uncertainty NODE [--samples 200] [--seed 1] [--case C] [--json]samples theuncertaintyof a step's reaction data (rate.uncertaintyof A and Ea, and that ofheat_of_reaction,equilibrium_constantandconversion) by Monte Carlo through the step's own reactor model (a fixed bed, a stirred tank or batch, a conversion reactor; equilibrium and Gibbs reactors are not sampled) and prints the conversion of the key (the first fed reactant), the outlet and the highest temperature as declared, with their mean and 5th, 50th and 95th percentile; draws that do not solve are counted and named (domains/process/reactor_uncertainty.py). Nothing is written. Exit 2 when the step cannot be sampled (not one inlet, no reaction with an uncertainty, not solvable as declared). - Selectivity as a design input: a conversion reactor step may give
selectivity: {key, conversion, products: {id: S}}(kmol formed per kmol of the key converted);process propagatesolves the extents of its parallel and series reactions (chemistry.reactions.selectivity_extents), refusing selectivities the reactions cannot give. - Stirred tanks and batches:
reactor: cstr(atank:of a volume or residence time,counttanks in series, isothermal or adiabatic) andreactor: batch(abatch:time or target conversion) solve the reactions' rate laws (domains/process/stirred_tank.py), gas or liquid;process propagate --fillwrites the outlet andprocess reactorsreports the tanks' conversions and duty, or the batch's profile and heat release. - Liquid-phase reactions: a reaction with
phase: liquidtakes itsequilibrium_constantonbasis: mole_fraction,activity(UNIFAC from the components' CAS numbers, extraprops;activity_model: idealinreactions.yamlfor activity coefficients of 1) orconcentration(with each component'sliquid_molar_volume), and its rate law ondriving: mole_fraction,activityorconcentration(domains/chemistry/liquid.py).reactor: equilibriumsolves a liquid equilibrium andprocess reactorsreports ln(Q/K) and the rate on that basis;validatewarns when a basis does not fit the phase (cheme.reaction-phase-basis). - Solids, crystallizers and filters: a component with
phase: solidinprocess/materials.yamlis a solid (a crystal, or a hydrate with its water in the formula,Na2CO3(H2O)10), a separate component from the same substance dissolved; every stream then divides into dry solid and liquid (mother liquor or moisture) with their compositions (domains/process/solids.py; no particle size or population balance). A block or step withcrystallizer:(one solute, one solvent, one material inlet) is a cooling and/or evaporative crystallizer with an optional specified filter or centrifuge (separation:solid recovery and cake liquid) and mother-liquorrecycle:;basis: solubilitypredicts the crystals from sourced solubility data (the mother liquor leaves saturated; points interpolated in temperature, never extrapolated),basis: yieldimposes them (specified, compared with the solubility when given).solid_separation:is a filter or centrifuge on its own.process propagate --fillwrites the outlets,validatebalances the node on mass and elements, the model basis issolubility-equilibrium(predictive, not simulated),imposed-yieldorsolid-split(specified).cheme process crystallizers [--case C] [--json]reports the crystals formed (and at equilibrium), recovery, loop iterations, mass, solute and element residuals, the lumped duty (withenergy:), the outlets as dry solid, liquid and moisture, and refuses impurities, several solutes or a temperature outside the data (cheme.process-crystallizer-unsupported). Qualified against Felder & Rousseau Example 4.5-2 and Geankoplis Examples 12.11-1 and 12.11-2 (tests/test_crystallization.py;docs/adr/0025-solid-components-and-a-bounded-crystallizer-adapter.md). - Reaction evidence:
process/reaction-evidence.yaml(schemacheme/reaction-evidence/v1,cheme schema reaction-evidence, skillsreaction-evidence-searchandroute-selection) records the searches, sources, observations, candidate routes, conflicts and the model basis behind the reactions, before any flowsheet.cheme validatechecks it (cheme.evidence-*: references, DOIs and patent numbers, a location for every reported value, no numbers from a source only seen as a snippet, units, the yield basis, duplicate evidence, kinetics validity, an explicit model basis whose reactionsreactions.yamldeclares).cheme chemistry evidence [--json]prints the route comparison. Worked example:examples/reaction-evidence-ethyl-acetate(three routes to ethyl acetate from papers and patents). - The reaction library:
cheme chemistry library [ID] [--add]lists, shows and adds 23 industrial processes (ammonia, methanol, reforming, shift, methanation, ethylene oxide, sulfuric and nitric acid, Claus, Fischer-Tropsch, hydrotreating, styrene, acrylonitrile, propylene oxide, cracking, vinyl chloride, formaldehyde, maleic anhydride, cumene, ethylbenzene, MTBE, HCN): components with chemicals' ideal-gas data and critical constants, main and side reactions, typical catalysts and windows, with sources (domains/process/data/library/, built byscripts/build_reaction_library.pyfromscripts/reaction_library.yaml).--addappends to the plant's process files, keeping their comments. Each entry'smodel_basissays which reactor model its own data support, checked against the data:equilibrium(8 entries: every reaction reversible, gas phase, ideal-gas data over the window; an equilibrium limit, not a kinetic model) orstoichiometry_only(15 entries: no conversion, selectivity, equilibrium basis or rate law);conversion_selectivityandkineticneed sourced conversions or rate laws no entry has yet. The listing,--addandGET /api/libraryshow it: an entry brings chemistry, the reactor model is chosen separately. - Microkinetics: a reaction's
mechanism:lists its elementary surface steps (adsorbatesX*, free sites*, per-site rate constants or sticking coefficients).cheme chemistry microkinetics ID -T K -p ID=BAR ... [--cantera] [--export FILE]solves the mean-field steady state (coverages, TOF, degree of rate control per step, apparent activation energy, orders;domains/chemistry/microkinetics.py), checks it against Cantera (extracantera) and writes Cantera's YAML input. - Rate laws from data:
experiments/kinetics/*.yaml(schemacheme/kinetic-data/v1) holds rates of a reaction;cheme experiments fit-rate FILE [--form lhhw] [--reverse]fits a power law (linear least squares on ln r) or an LHHW law (Levenberg-Marquardt), each parameter with its 95 % interval;cheme experiments sample-mechanismmakes such data from a mechanism, to lump it into a rate law for a fixed bed. Mechanism steps may give DFTenergies:(transition state theory) instead of rate constants. - Electrochemical cells and measurements:
cheme process cell [--json]evaluates the cells ofprocess/cells.yaml(schemacheme/electrochemical-cells/v1): the Faraday balance, power, heat duty and kWh per kg, from specified values or, withperformance: measured, from a measured series looked up at the cell's current density (refused outside its range, never extrapolated), and an optional membrane block (water dissociation efficiency, co-ion fluxes, anolyte hydroxide deficit;docs/framework/electrochemical-cells.md).experiments/electrochemistry/*.yaml(schemacheme/electrochemical-measurements/v1) holds a measured series with replicates;cheme experiments electrochem FILE [--json] [--plot DIR]computes per replicate and point (mean, sd, n) the Faradaic efficiency of each product from the outlet flow and mole fractions (standard conditions required), partial current densities, the water dissociation efficiency from the anolyte titration, power, heat and the charge and carbon balances, and writes polarisation, efficiency, partial-current and water-dissociation plots only for the inputs that support them (docs/framework/electrochemical-measurements.md). - Electrolysis duties:
process/duties.yaml(schemacheme/electrolysis-duties/v1) states dynamic duties of a cell ofprocess/cells.yamlagainst its anolyte reservoir: a current, power or on/off schedule, the reservoir (volume, composition, anolyte model by id, optional CO2 degassing), dosing on a pH trigger or continuous, bleed, make-up water, the stated pH threshold (a design criterion) and the horizon.cheme process duty [IDS] [--json] [--plot DIR] [--compare ID,ID --co KG]integrates them (Dormand-Prince 5(4) with events): the voltage, Faradaic efficiency and water dissociation efficiency at each instant from the measured series (never extrapolated: the transient stops at the first exit from the measured range and the time outside is summed) or the specified cell; pH, inventory, product, charge, energy, dosing and bleed over time; the time to the specified pH threshold;--comparecompares duties at equal delivered product;--plotwrites aligned time plots and an operating map (WDE x reservoir volume) with the measured WDE points apart from the simulated values. The first anolyte model iswell-mixed-carbonatev1 (equilibrium carbonate speciation on a concentration basis;docs/framework/electrolysis-duties.md). A duty a stack ofprocess/cells.yamlnames runs at the arrangement that stack is sized at (cheme.duty-arrangement), as incheme process stack, and so does a duty that names the stack itself (stack: STK-1, a comparison duty on the skid's hardware; anothercells_in_seriesorstackson it is an error). The totals are split by route (the cation and carbon dosed, bled and through the membrane, the initial inventories), so both balances close on their own.reservoir.size: to_horizon(optionaltime_margin) reports the exact volume that reaches the horizon,V0 Q(target) / Q*, for a duty without dosing, bleed, make-up or degassing at one operating point, raised to keep a stated minimum inventory and the anolyte model's validity to the target (required_volume,limited_by; otherwisecheme.duty-size, error). - Electrolyser stacks: the optional
stacks:section ofprocess/cells.yaml(same schema, optional fields only) names per stack design a cell, the product duty, the electrical limits, the assumed envelope per cell, clearances, the replacement interval and the skid equipment a duty sizes.cheme process stack [IDS] [--json] [--write]sizes it at the cell's operating point (refused outside its measured or declared range, never extrapolated):A = n F Ndot / (nu FE j),ceil(A / A_cell)cells, at mostfloor(U_max / U)in series per stack, the stacks, the reconciled voltage, current, DC power, output and margin, kWh per kg, the envelope and footprint; and the reservoir, dosing and bleed tanks and the recirculation and dosing pumps' inputs from the named duty, run at that arrangement (a duty at another current density or of another cell, or not feasible, gives none:cheme.stack-duty); a dosing tank'srefill_intervaland a bleed tank'sempty_intervalsize it for the most dosed or bled in any such interval, with the fills or emptyings per campaign;product.recovery(default 1) sizes the stacks for the product formed, rate / recovery, and reports formed and delivered output; a duty withreservoir.size: to_horizongets its reservoir volume written intoprocess/duties.yamlby--write. Without--writeit writes nothing and reports what differs (cheme.stack-outdated);--writeedits the equipment-listdimensionsin place and the pumps' datasheettype_specific(the recirculation pump's with its end pointssuction_fromanddischarge_to), never over a value written by hand (cheme.stack-kept), from whichcheme layout buildandcheme datasheets draft/filltake them; a value it wrote that is no longer given is cleared, a tag two stacks name is written by neither, and--write --jsonaddswritten(file -> tags). The LCA does not read the stacks: their power and dosing go into the flowsheet streams by hand. Exit 1 on an error finding (aprocess/cells.yamlthat does not follow its schema among them, its findings printed), 2 without the file or stacks or for an unknown id (docs/framework/electrolyser-stacks.md, ADR 0044, skillskid-design). cheme chemistry kinetics -r ID=NAME ... [-p ID=NAME ...] [--rmg-endpoint URL]reduces the NIST (and RMG) records of an elementary gas-phase reaction to one Arrhenius record and prints it as arate:entry (network). An entry that is a formula and also another compound's SMILES (CO,NO,O,C) is refused as ambiguous: give a name (carbon monoxide), an explicit SMILES ([C-]#[O+]) orsmiles:COfor the SMILES reading.cheme chemistry thermo NAME... [--fit LO,HI](extraprops) prints a component'sideal_gasentry from chemicals: ATcT formation enthalpy, entropy and Poling's Cp/R (to 1000 K), or Cp/R fitted to thermo's ideal-gas heat capacity over LO to HI K.cheme process streams [--case C] [--md | --csv | --json]prints the stream table (value, unit and provenance per property, calculated values, stale ones marked) and the balances.cheme process propagate --fill | --checkderives missing compositions and flows through steps whose streams determine them: pass-through classes (compressing, pumping, cooling, heating, filtering, transporting, flow control; heat exchanging with one stream in and one out) keep composition and flow, mixers combine, splitters keep the composition and split bysplit: {outlet port: fraction}(else the one missing outlet flow is the mass balance remainder), and a conversion reactor (a step namingreactions:, each with a conversion) converts its fraction of each key component's inlet flow; a crystallizer or a filter (crystallizer:,solid_separation:, below) fills its outlets. Other separators, and reactors without conversions, are only checked, never solved. Values arecalculatedwith source and input fingerprint, never overwrite given or assumed ones, and are reported stale when an upstream value changes. Run it beforeprops --fill.--jsonprints the run as an adapter result (cheme/adapter-result/v1,cheme schema adapter-result):convergedwhen every node is solved, elsenot-convergedwith the unsolved nodes inmessagesandunits;iterationsthe sweeps through the flowsheet; per stream the derived values and those written (filled). It writes no file of its own.cheme process props --fill | --check | --coverage(extraprops) calculates phase, vapour fraction, molar mass, density and enthalpy, only inside the validated range of the process's property package (property_method.packageinmaterials.yaml, else the package covering the most streams;--coveragelists it and, per stream, whether it is covered or why not, writing nothing). Packageair-pr-chemsep: N2, O2 and Ar; 77 to 315 K; vapour 100 to 3200 kPa(a); liquid and two-phase 100 to 600 kPa(a). Peng-Robinson (thermo, ChemSep kij) for equilibrium and vapour density, COSTALD for liquid density, enthalpy on ideal gas at 298.15 K and 101.325 kPa. The references (NIST WebBook equations,tests/data/process_props_references.yaml) pass within 0.5 % (gas density at ambient pressure), 1 % (gas density up to 31 bar), 1.5 % (liquid density) and 0.3 K (saturation temperatures). Packagesyngas-pr-vapor: H2, N2, Ar, CH4, CO, CO2 and their mixtures, NH3 pure and in mixtures with H2, N2, Ar and CH4 up to mole fraction 0.25 (ammonia synthesis loop gas), vapour only, in per-component temperature-pressure boxes within 250 to 1000 K and 100 to 30000 kPa(a), benchmarked bytests/test_property_packages.pyagainst CoolProp's reference equations of state, GERG-2008 mixtures and the Neumann et al. (2020) ammonia mixture models, and against measured densities (density 2 % pure, 3 % mixtures; enthalpy differences 300 kJ/kmol). A state outside the range, another component, a pressure that is not absolute and positive, solids, or a phase the flash contradicts is refused with a finding, and nothing is written.--fillnever overwrites given or assumed values, records the method, its package versions and range inmaterials.yaml, and stores an input fingerprint with each calculated value; when an input changes,validatereportscheme.process-calculated-staleuntil it is recalculated (a refused recalculation keeps the old values, marked stale).--checkrecalculates and reports only (exit 1 on a warning);--fillexits 3 when it refused a stream and names the refused streams on its summary line.--jsonprints the run as an adapter result (solverthermowith its version and EOS;not-convergedwith the refused or uncalculable streams inmessages;iterationsnull, one flash per stream).cheme process simulate --fill | --check [--save x.dwxmz]solves the flowsheet in DWSIM.process/simulation.yamlgives the property package and what each step needs beyond its streams (every value with its provenance):
schema: cheme/simulation/v1
kind: simulation
property_package: Peng-Robinson (PR)
specs:
K1: {outlet_pressure: {value: 800, provenance: given}, efficiency: {value: 0.78, provenance: assumed}}
E1: {outlet_temperature: {value: 308.15, provenance: given}}
DWSIM runs in Docker: build the image once with docker build --platform linux/amd64 -t
cheme-dwsim:9.0.5 -f docker/dwsim/Dockerfile . in this repository. Simulated streams get
temperature, pressure, flow and composition (energy streams their duty) as calculated;
given and assumed values are never overwritten, and a deviation from them is a warning. A
duty is written only where its sign agrees with its energy stream's direction (heat added or
power into the unit, heat removed or power produced out of it); otherwise, and for a
component separator's energy imbalance, cheme.process-sim-energy-direction warns. Run
props --fill afterwards for phase, density and enthalpy. Units: compressor, expander, pump,
heater, cooler, valve, mixer, splitter, flash drum, heat exchanger, shortcut and rigorous
column, and conversion, equilibrium and Gibbs reactors from the flowsheet's reactions (the
specs each needs: cheme schema process-simulation); recycle loops are torn
and solved iteratively. Rigorous columns take side draws, a partial or no condenser, no
reboiler and a condenser-reboiler check (examples/process-double-column, an air separation
double column). See examples/process-simulation
and examples/process-separation.
simulator: idaes in simulation.yaml (default dwsim; ADR 0039) solves the flowsheet in
the optional IDAES backend instead: the worker runs in the Python CHEME_IDAES_PYTHON names
(with idaes-pse and IPOPT; cheme-as-code never imports IDAES or Pyomo). Its scope is heater,
cooler, valve, pump, mixer, splitter, flash, conversion reactor and recycles (compressor and
expander with Peng-Robinson), property packages IDAES ideal VLE and IDAES Peng-Robinson
for benzene, toluene, hydrogen and methane; anything else is
cheme.process-sim-backend-unsupported (error) at translation, and neither package is
benchmarked (-sim-method-unbenchmarked, info). Values carry the source cheme process
simulate: IDAES <version>, <property package>; the run record also names the solver
(IPOPT). See docs/framework/idaes.md and
examples/process-hda-idaes.
--fill also records the run in process/simulation-run.json (generated, committed with the
flowsheet; schema cheme/simulation-run/v1, one entry per case): simulator and version,
property package, when, the model fingerprint and the input files' commit and fingerprints,
convergence and status, iterations (the flowsheet passes DWSIM made: one per solve without
a recycle, else the iterations of the slowest recycle), messages, per unit its
status (simulated, or excluded with the reason), convergence, results and warnings, per stream
the simulated values and which of them it holds. A run that does not converge is recorded too
(no stream value is written). --check and cheme validate report a record from other
inputs (cheme.process-sim-run-stale, warning; translating needs no simulator), --check
also a case without one (-sim-run-missing, info).
GET /api/simulation serves it. Each entry is an adapter result (cheme/adapter-result/v1)
in the file's keys: simulator (with method, the property package) is its solver.
The property package must be one the worker runs (cheme process methods; another is
cheme.process-sim-property-package, error). method_selection: {facts: {...}} records the
facts of the thermodynamic model selection; the translation re-evaluates them and flags a
package they do not recommend (cheme.process-sim-method-not-recommended, warning) unless
method_selection.substitution records why it is accepted (-sim-method-substituted,
info); incomplete facts are -sim-method-selection.
- cheme process methods [--json] prints the supported-method contract
(domains/process/thermodynamics/methods.yml): for each model the selection tree can
recommend, whether cheme process phase (thermo) and cheme process simulate (DWSIM) run
it, else the nearest model they run as an explicit substitution with what it loses, or none.
Coverage decisions: electrolytes, solid equilibrium and petroleum pseudocomponents are
unsupported on both routes (ions, unidentified components and, with water, the salts, acids
and bases electrolytes.yml lists, and any compound of an alkali or alkaline-earth metal,
are refused by the phase route and by the simulation
translation, cheme.process-sim-electrolyte; the phase route has no solid phase).
Electrolytes are limited: the electrolyte route runs Pitzer's liquid model for aqueous NaCl
only (cheme process electrolyte, below). The phase route claims no application range; the simulation
route claims one only where a test benchmarks it (today Peng-Robinson (PR) for N2/O2/Ar and their
mixtures, Soave-Redlich-Kwong (SRK) for each of them alone, NRTL for the ethanol + water
bubble points at 101.3 kPa(a) against measured VLE data, on DWSIM 9.0.5,
methods[].simulation.application_range, checked by cheme maturity).
- cheme process phase COMPONENT... (--model EOS | --liquid M --vapor V) [--z x,y]
[--temperature T --pressure P] [--accept-zero A:B] [--check] [--json] (extra props)
reports per binary pair whether the model's interaction parameter is sourced (with its
source: thermo's ChemSep PR, NRTL and Wilson tables, the fetched ChemSep SRK file, thermo's
UNIFAC group parameters), missing (the calculation uses zero) or accepted_zero
(--accept-zero); with a temperature and pressure it also runs the flash. --check exits 1
when a pair is missing. Python: thermodynamics.coverage.interaction_parameter_coverage.
- cheme process electrolyte --temperature T --pressure P (--molality M | --mass-fraction W |
--species Na+=M,Cl-=M) [--system NaCl-H2O] [--json] (extras cantera and props) runs a
qualified aqueous electrolyte system of the versioned contract
domains/process/thermodynamics/electrolytes.yml (ADR 0027): today NaCl-H2O, through Pitzer's
equations in Cantera's HMW-electrolyte phase with the Silvester and Pitzer (1977) parameters,
complete dissociation into Na+ and Cl-. It prints the mean ionic activity coefficient
(molality scale), the osmotic coefficient, the water activity and the water vapour pressure
over the solution (a_w times the IAPWS-95 saturation pressure), the relative apparent molar
enthalpy phi_L of NaCl (heats of dilution), the species, element and charge closure, and the
provenance (solver version, parameter source, tolerances, benchmark). The claim:
273.15-373.15 K, up to 1000 kPa(a) and 6 mol/kg, phi_L from 313.15 K, one liquid phase;
tests/test_electrolytes.py checks it against Hamer and Wu (1972) and Pitzer, Peiper and
Busey (1984) within 2 % in gamma+-, 0.01 in phi and 0.03 RT in phi_L. Anything else is refused
with exit code 3 and a rule id (cheme.process-electrolyte-range, -unsupported for another
ion, -charge-balance, -phase for a boiling solution, -system), never computed with a
neutral-fluid model. A brine in a mass balance with specified splits (an inventory) needs no
property model; its properties come from this route or stay unclaimed. Python:
thermodynamics.electrolytes.electrolyte_state.
A converged run is also checked for energy, independently of the duty balance DWSIM closes
itself; the record keeps the audit under energy. Exchangers: the hot and cold composite
curves from the solved temperatures and duties (counter-current, constant heat capacity flow
per stream) give the least approach; a crossing is sim-temperature-cross, an approach below
min_approach (in simulation.yaml, or per exchanger spec) sim-exchanger-approach; a
stream that changes phase is sim-exchanger-zones (info: no zone temperatures, so the
internal approach is not checked). Utility levels: a duty whose energy stream comes from (or
goes to) a terminal with category: utility and utility: {kind, supply_temperature,
return_temperature, min_approach} is checked against those temperatures
(sim-utility-level). Enthalpy closure: per unit and for the simulated plant (boundary
streams, heat, shaft work) from DWSIM's stream enthalpies, within an explicit tolerance
(sim-energy-unbalanced). Reactors (conversion, equilibrium, Gibbs, kinetic_pfr) close on a
heat-of-formation basis: DWSIM's enthalpy flows plus sum (n_out - n_in) dHf_298 of the
reacting components, on DWSIM's own formation enthalpies (its database, which its reactor
duty takes), so the closure tests the simulation's energy conservation. Verified live
(DWSIM 9.0.5): the reference state (each component's ideal gas at 298.15 K = 0, no
formation enthalpies) for Peng-Robinson, PR78 and SRK, not for NRTL; the duty reproduced
within 1e-3 kW for vapour ammonia synthesis at 10 bar (conversion, equilibrium and Gibbs
reactors under Peng-Robinson; the conversion reactor under PR78 and SRK). The project's
formation enthalpies are compared with DWSIM's separately (sim-formation-data): a warning
where values declared in materials make a reaction heat that differs by more than 1 % of it
(predictive.formation-data then fails); a smaller nonzero difference, chemicals' fallback
values and missing values are info and recorded limitations, with each component's
difference in kJ/kmol (ammonia: -45558 kJ/kmol in ATcT, -45898 in DWSIM, 0.74 % of the
synthesis heat). Another package and units
with an unestablished duty sign (a column top without condenser) are
sim-energy-unsupported, never a number. With simulator: idaes the record's
energy.reference states IDAES's basis (formation enthalpies included,
formation_enthalpy_included: true) and a reactor's closure is sim-energy-unsupported
(idaes.md). A component separator (a specified split) conserves
no energy: its imbalance is stated, not judged (status: stated, imbalance_kW = enthalpy
out minus in from the solved streams, positive: heat the unit would have to take in; DWSIM's
EnergyImb is recorded with its observed sign, simulator_convention, and not relied on),
and the plant closes with it as a stated residual (stated_kW; sim-energy-stated, info,
beyond the tolerance; not a utility; ADR 0038). Every duty
is classified once as hot or cold utility, power, or heat recovered between process streams
(process-to-process exchangers, condenser-reboilers), which is never also counted as utility.
- cheme process extract [UNIT] [--systems] [--json] (extra props) solves each extractor
of process/extraction.yaml (schema cheme/extraction/v1, cheme schema process-extraction):
a feed and a solvent feed through stages countercurrent equilibrium stages at one
temperature, NRTL from thermo with a bundled LLE system (system:, listed by --systems) or
sourced parameters for every pair (model:). It prints the raffinate and extract, solute
recovery, solvent loss, distribution coefficient and selectivity, the component closure and
qualification (qualified only for a bundled system inside its benchmarked range; the
bundled water + 1-propanol + n-butyl acetate system is benchmarked against Costa et al. 2009
at 323.15 K). Refused (exit 1, a finding with its line): no two-phase region, a missing pair
(never taken as zero), a temperature outside the parameters' range. Isothermal, no energy
balance; solvent regeneration is a separate unit; the flowsheet keeps a component separator
citing the result. Writes nothing. Python: separations.extraction.
- cheme process energy [--case C] [--json] prints the energy balances of blocks and steps
(stream calculated.enthalpy times molar flow against the energy streams crossing them,
cheme.process-energy-balance, checked by validate in every case with stream states; a
calculated enthalpy must be in kJ/kmol and a molar mass in kg/kmol, another unit is
cheme.process-unit and leaves the balance not evaluable, never converted; a block holding
a step without an energy stream whose duty is unknown is not evaluable; a reacting node
needs units.enthalpy_reference.state: elements and values on that basis, else it is
unsupported), the energy audit of the recorded
simulation run, and the boundary gaps that leave TEA and LCA inventories incomplete:
steps that exchange heat or work without an energy stream (cheme.boundary-utility-unassigned),
terminals without category (raw_material, product, by_product, utility,
wastewater, off_gas, solid_waste, emission; cheme.boundary-unclassified) and utility
terminals without levels. Exit 1 on a warning.
- cheme process pinch --dt-min X [--dt-min-source S] [--case C] [--include-constrained]
[--json|--md] [--write] takes whole-plant pinch targets (problem table,
heat_integration/pinch.py) from the case's recorded simulation run
(process/simulation-run.json; it never runs the simulator: a missing, stale or unconverged
run is an error, cheme.process-pinch-run-missing, -run-stale, -run-not-converged).
dTmin is required, its source recorded (default ASSUMED). heat_integration/streams.py
extracts one stream per thermal duty and side, each in one class: utility (heaters, coolers,
flashes, condensers, reboilers, the process side of an exchanger against a utility terminal's
stream such as a waste-heat boiler, whose steam is the utility and no second stream),
recovery (both sides of a process/process exchanger, counted once; every side of a
multi-stream exchanger; a condenser heating another column's reboiler), constrained
(reactors, with the reason; out of the targets unless --include-constrained) or excluded
(with the reason). Quality: the simulators report end temperatures only, so a stream is
linearised (one constant-CP or isothermal segment; a phase change is not split at dew or
bubble points, cheme.process-pinch-stream-linearised) or insufficient (excluded,
cheme.process-pinch-stream-excluded). The reconciliation lists every thermal duty of the
run from the units' results and checks the streams give each back within 0.1 kW + 1 %;
heat left over is cheme.process-pinch-reconciliation (warning). It prints the current
utility demand (of the targeted streams; recovered heat is not utility), the theoretical
minimum utility demand at dTmin and the theoretical energy-saving potential (their
difference; it ignores pressure, distance, fouling, control, safety and cost and is not a
saving), the hot-side, cold-side and shifted pinch or a threshold problem, utility-use pinch
violations (a utility heater below, a cooler above the pinch) apart from cross-pinch
process-to-process transfer of the existing exchangers whose sides are all known (with their
least approach), the stream table and the reconciliation. --write records settings, core
version, the simulation run (model and content fingerprints), the inputs and the results in
process/pinch-run.json (schema cheme/pinch-run/v1, one entry per case, generated) and
refuses with exit 3 while the reconciliation has a finding. Exit 1 on an error or a warning,
2 on invalid input (no or a negative --dt-min, an unknown case, --json with --md).
Python: heat_integration.report; ADR 0040.
- cheme process furnace [ID] [--json] gives the energy basis of a solid-reaction furnace
declared in process/furnaces.yaml (schema cheme/furnaces/v1; today limestone calcination
only): the element balance, the product and off-gas composition (calcination and fuel CO2
apart), the reaction enthalpy and the furnace energy closure (fuel LHV or electric power,
sensible heat of feed, product and off-gas, moisture, losses) from the NASA condensed and gas
data Cantera ships (extra cantera), never from a fluid flash. A measured fuel flow or power
is checked (cheme.furnace-closure), an absent one solved; the degree of calcination is
given, never predicted. Temperatures outside a species' data, other reactions (carbothermal
reduction, dolomite) and missing losses are refused with a finding. method: specific-energy
is a labelled conceptual fallback (a sourced GJ/t or the lime BREF's range by kiln type).
Reference case, data and tolerances: docs/framework/furnace.md.
Exit 1 on a warning or error.
- Links. Blocks, steps, streams, feeds and products link to equipment, line,
instrument or sheet (several per object, an optional sheet: scope). They resolve against
the lists and plant.yaml; unresolved ones are cheme.process-link-unresolved (info), and
the gate kind: process-links with level: warning or error reports or requires them.
- cheme process layout [IDS] --view bfd|pfd [--pin] prints the generated positions, and
--pin writes them into layout.<view> so later builds keep them.
validate, status, diff, show and render take BFD and PFD sheets like P&IDs; the
loader's warning about port reference cycles is demoted to one info line per sheet. The
template's skill process-design walks through it, and pid-draft starts the P&IDs from the PFD.
The schemas of both files: process-materials and
process-flowsheet; every cheme process
subcommand with its options: CLI reference.