Reference
The functions and types for setting up, running and measuring a process. A process is described in layers, from the physics to the operation. Each layer is a plain object that can be edited, saved and reused on its own:
| Layer | Objects |
|---|---|
| Gas | Gas, Feed, Component |
| Materials | Adsorbent with an isotherm by component (dual_site_langmuir, langmuir), Column, Wall |
| Flowsheet | Flowsheet, beds (add_bed!) and devices with roles (add_device!) |
| Operation | a Cycle of steps (Pressurise, Adsorb, Blowdown, Evacuate, Purge, Equalise, Idle, Concurrent, or your own), TimeStepping |
| Process | Process: simulate, cyclic_steady_state, performance |
| Design | add_variable!, performance_gradient, optimise! |
| Files | read_process, write_process, load_adsorbent; see Process files |
| Results | ProcessResult, write_results, plots; see Results and plots |
The Building a process from its parts example uses each layer in turn. To add your own kinds of step or unit, see Extending Mocca.
Units
Everything is in plain SI units: Pa, K, m, s, mol, kg and J.
Mocca.Units Module
Mocca.UnitsMocca works in plain SI units throughout: Pa, K, m, s, mol, kg and J. These constants convert other units to SI by multiplication, so 0.1bar is 1e4 (Pa) and -36.6kJ_per_mol is -36600.0 (J/mol):
using Mocca.Units
Evacuate(40.0; to = 0.1bar)Units.celsius converts a temperature in °C to K. Files read with read_process may give a number with one of these units as a string, such as "0.1 bar".
Gas and feed
Mocca.Interface.Component Type
Component(name; formula = name, cas = "", molar_mass, heat_capacity, viscosity)A chemical component: its molar mass [kg/mol], gas heat capacity [J/(kg·K)] and gas viscosity [Pa·s], both near 25 °C. Mocca knows common gases by name (see component); add others with register_component!.
Mocca.Interface.component Function
component(name) → ComponentThe component called name, for example "CO2". Known: CO2, N2, O2, Ar, H2O, CH4, H2, CO, He, and any added with register_component!.
Mocca.Interface.register_component! Function
register_component!(c::Component)Make c known by name, so that gases, compositions and isotherms can use it. Replaces a component of the same name.
Mocca.Interface.Gas Type
Gas(names; molar_masses, heat_capacities, viscosity, density, diffusivity = 1.6e-5)A gas mixture of the named components, in order. The order sets the order of compositions and loadings everywhere; by convention the first component is the one to capture (it is the key component of purity and recovery).
Properties default to those of the components (see component), and can be overridden, all at once as a vector in gas order or for some components by name, such as heat_capacities = (CO2 = 697.6,):
molar_masses[kg/mol] andheat_capacities[J/(kg·K)] per component;viscosity[Pa·s] of the mixture. By default that of the feed, the Herning–Zipperer mixture of the component viscosities;density[kg/m³] of the mixture, by default the ideal gas at the feed pressure and temperature. The built-in units do not use it;diffusivity[m²/s]: molecular diffusivity, one value or one per component. It sets the macropore diffusion of the default linear driving force and, through the first component, the axial dispersion. The default is that of CO2 in N2 at 25 °C.
Mocca.Interface.composition Function
composition(gas, spec; trace = 1e-10) → Vector{Float64}Mole fractions in gas order. spec is a vector in gas order, or fractions by name, such as (CO2 = 0.15, N2 = 0.85) or Dict("CO2" => 0.15, "N2" => 0.85). Components not named get trace, to keep every fraction positive, and the named ones are scaled down to make room. Fractions must sum to one.
Mocca.Interface.Feed Type
Feed(gas; composition, temperature = 298.15, pressure = 101325.0, light_product)The feed gas of a flowsheet: its composition (see composition), temperature [K] and pressure [Pa]. light_product is the composition of the gas leaving the top of a bed during adsorption, used where light product flows back into a bed (for example in light product pressurisation). By default it is the feed with the component captured reduced to a trace: the first component, or the key_component of the Process the feed is used in.
Isotherms, adsorbents and columns
Isotherm parameters are given by component name, so an adsorbent does not depend on the order of the components of a gas, and components can be added or removed without editing parallel arrays.
Mocca.Interface.AbstractIsothermData Type
AbstractIsothermDataIsotherm parameters given by component name, independent of the order of the components in a gas. build_isotherm turns them into an AbstractIsotherm for a particular Gas. Components of the gas without parameters do not adsorb; parameters for components the gas does not have are ignored, so one adsorbent can serve gases with different components.
Implement build_isotherm(data, gas; density) for a new kind, and register it with register_isotherm! to read and write it in files.
Mocca.Interface.dual_site_langmuir Function
dual_site_langmuir(name => parameters, ...; reference_temperature = 298.15, basis = :volume)Dual-site Langmuir isotherm data, one entry per component:
dual_site_langmuir(
"CO2" => (qsb = 3489.44, b0 = 8.65e-7, ΔUb = -36.64kJ_per_mol, qsd = 2872.35, d0 = 2.63e-8, ΔUd = -35.69kJ_per_mol),
"N2" => (qsb = 6613.55, b0 = 2.50e-6, ΔUb = -15.8kJ_per_mol))For each site, b (strong) and d (weak): saturation capacity qsb, qsd; affinity at infinite temperature b0, d0 [m³/mol], on a concentration basis; and internal energy of adsorption ΔUb, ΔUd [J/mol], negative. Omitted d-site values are zero. dUb and dUd may be written for ΔUb and ΔUd.
Capacities are in mol per m³ of adsorbent with basis = :volume, or mol/kg with basis = :mass. reference_temperature [K] is where the isosteric heat of adsorption is evaluated.
Mocca.Interface.langmuir Function
langmuir(name => (qs = ..., b0 = ..., ΔU = ...), ...; reference_temperature = 298.15, basis = :volume)Single-site Langmuir isotherm data by component: a dual_site_langmuir with an empty second site.
Mocca.Interface.build_isotherm Function
build_isotherm(data, gas; density) → AbstractIsothermThe isotherm for the components of gas, in its order. density [kg/m³] is the adsorbent density, which converts capacities given per kilogram. An AbstractIsotherm is used as it is.
Mocca.Interface.Adsorbent Type
Adsorbent(name = ""; isotherm, density, heat_capacity, particle_diameter,
particle_porosity, tortuosity = 3.0, adsorbed_heat_capacity = nothing,
mass_transfer = nothing, reference = "")An adsorbent material:
isotherm: isotherm data by component, such asdual_site_langmuir, or anAbstractIsothermin the order of the gas it is used with;density[kg/m³] andheat_capacity[J/(kg·K)] of the adsorbent;particle_diameter[m],particle_porosityandtortuosity, which set the macropore diffusion of the default linear driving force;adsorbed_heat_capacity[J/(kg·K)] of the adsorbed phase, per component (a vector in gas order or by name); by default the gas heat capacities;mass_transfer: anAbstractMassTransfer; by default aLinearDrivingForcefrom the particle properties and the gas diffusivity;reference: where the data come from.
Load one from the library with load_adsorbent.
Mocca.Interface.equilibrium_loading Function
equilibrium_loading(adsorbent, gas; pressure, temperature, composition) → VectorEquilibrium loading [mol/kg] of each component of gas on adsorbent, at a total pressure [Pa], temperature [K] and gas composition (see composition). Use it to check isotherm data and units.
Mocca.Interface.isotherm_table Function
isotherm_table(adsorbent, gas, name; pressures, temperatures = [298.15]) → MatrixPure-component loading [mol/kg] of component name on adsorbent, one row per partial pressure [Pa] in pressures and one column per temperature [K] in temperatures. The other components are at trace levels.
Mocca.Interface.Column Type
Column(; length, radius, porosity, outer_radius = radius, axial_conductivity = 0.09,
wall = Wall(), inner_heat_transfer = 8.6, outer_heat_transfer = 2.5,
ambient_temperature = 298.15)The geometry and heat transfer of a packed column:
length[m], innerradius[m] and bedporosity(void fraction between particles);outer_radius[m] of the wall, needed when the bed has a wall (the default thermal model);axial_conductivity[W/(m·K)] of the packed bed;wall, aWall;inner_heat_transferandouter_heat_transfer[W/(m²·K)], bed to wall and wall to surroundings, andambient_temperature[K].
The heat transfer defaults are those of Haghpanah et al. (2013).
sourceMocca.Interface.Wall Type
Wall(; density = 7800.0, heat_capacity = 502.0, conductivity = 16.0)Column wall material, by default stainless steel: density [kg/m³], heat capacity [J/(kg·K)] and conductivity [W/(m·K)].
sourceFlowsheets
Devices have roles. A role sets the device's default boundary condition and valve size, tells the steps which device to open, and tells performance which streams are feed and product.
Mocca.Interface.Flowsheet Type
Flowsheet(gas::Gas, feed::Feed)An empty flowsheet for gas, fed with feed. Add beds with add_bed! and devices with add_device!, and run it as a Process.
Devices have roles (feed, product, extract, ...) that give their default boundary conditions and valve sizes, tell the step templates which device to open, and tell performance which streams are feed and product. The units and connections are a UnitNetwork, so the lower-level functions, such as add_unit!, connect! and setup_flowsheet_model, work on a flowsheet too.
Mocca.Interface.add_bed! Function
add_bed!(fs, name, adsorbent, column; ncells = 30, thermal = WithWall(),
dispersion_velocity = :adsorption, dispersion = nothing, permeability = nothing)Add a fixed bed of adsorbent in column to the flowsheet under name. Its ports are :bottom and :top.
The axial dispersion depends on the gas velocity. By default (dispersion_velocity = :adsorption) it is set from the interstitial velocity of the bed's adsorption step when a Process is built, or the largest adsorption velocity in the cycle for a bed fed from another; it then stays fixed as settings change. Give a velocity [m/s], or the dispersion [m²/s] itself, to fix it. It is the bed's AxialDispersion parameter, so it can also be set through parameters of a Process or a design variable. See fixed_bed_model for the other keywords.
Mocca.Interface.add_device! Function
add_device!(fs, name, role; at, boundary = nothing)
add_device!(fs, name, :link; from, to)Add a flow device with a role (see DEVICE_ROLES), connected to a bed port at = (bed, port), such as (:Bed, :bottom). A link joins from, a bed port, to to, another.
The boundary of an inlet or outlet device defaults to the feed temperature, a role composition (the feed for feed, extract and waste; the light product for product and light inlet) and a pressure that steps override: the feed pressure, or for extract and waste the lowest pressure of the bed in its cycle. Pass boundary (a PortCondition, or a NamedTuple with any of pressure, temperature and composition) to set it.
Mocca.Interface.DEVICE_ROLES Constant
Roles of flow devices:
| Role | Connected by | Boundary | Meaning |
|---|---|---|---|
:feed | its outlet | inlet | feed gas into a bed; counts as feed |
:light_inlet | its outlet | inlet | light product into a bed, for light product pressurisation or purge |
:product | its inlet | outlet | light product (raffinate) out of a bed |
:extract | its inlet | outlet | heavy product out of a bed, through a vacuum pump; counts as product |
:waste | its inlet | outlet | other gas out of a bed, such as a water purge |
:link | both ports | none | between two beds: series feed, equalisation |
Mocca.Interface.print_flowsheet_diagram Function
print_flowsheet_diagram([io], fs)Print each unit of fs with the devices at each of its ports, their roles and boundaries.
Cycles and steps
A step acts on a bed and opens the devices it needs, found by their roles; every other device stays closed. Each step that changes the pressure starts where the step before ended, wrapping around the cycle; give from to start elsewhere.
Mocca.Interface.Cycle Type
Cycle(steps)
Cycle(label => steps, label => steps, ...; cycle_time = nothing)A cycle of steps (see AbstractStep), each run after the one before. Elements may also be Concurrent groups and plain Stages.
With several labelled sequences, such as one per bed of a two-bed process whose steps take different times, the sequences run side by side and are combined with parallel_stages: a shorter one is padded to cycle_time (by default the longest) with its devices closed. Each sequence tracks the pressures of its own beds.
Mocca.Interface.Pressurise Type
Pressurise(duration; to = feed pressure, from = nothing, source = :feed, bed = nothing,
device = nothing, ramp_rate = nothing, conductance = nothing, name = "")Raise the bed from from (by default where the step before left it) to to [Pa], through the bed's feed device (source = :feed) or its light inlet (source = :light_product, light product pressurisation). The pressure on the far side of the valve follows an exponential ramp at ramp_rate [1/s].
Mocca.Interface.Adsorb Type
Adsorb(duration; velocity = nothing, superficial_velocity = nothing, flow_rate = nothing,
pressure = feed pressure, bed = nothing, feed_device = nothing, outlet = nothing,
conductance = nothing, name = "")Feed the bed at constant flow, given as one of an interstitial velocity [m/s], a superficial_velocity [m/s] or a flow_rate [m³/s at the bed inlet], with light product leaving the other end at pressure [Pa] through its product device, or through a link into another bed. A bed without a feed device of its own is fed through a link opened by the step of the bed before it, and takes no flow setting.
Mocca.Interface.Blowdown Type
Blowdown(duration; to, from = nothing, through = :top, device = nothing, bed = nothing,
ramp_rate = nothing, conductance = nothing, name = "")Lower the bed to to [Pa] through an outlet device at its through end: the product device at the top (co-current), or a waste or extract device at the bottom (counter-current).
Mocca.Interface.Evacuate Type
Evacuate(duration; to, from = nothing, through = :bottom, device = nothing, bed = nothing,
ramp_rate = nothing, conductance = nothing, name = "")Lower the bed to to [Pa] through an outlet device at its through end, by default the extract (vacuum) device at the bottom, which takes the heavy product.
Mocca.Interface.Purge Type
Purge(duration; velocity = nothing, superficial_velocity = nothing, flow_rate = nothing,
pressure = nothing, bed = nothing, conductance = nothing, name = "")Light product enters through the bed's light inlet at a constant flow (one of an interstitial velocity, a superficial_velocity or a flow_rate) and leaves through the extract device at the other end, held at pressure [Pa], by default where the step before left the bed.
Mocca.Interface.Equalise Type
Equalise(duration; beds, device = nothing, conductance = nothing, name = "")Open the link between the two beds, such as (:A, :B), so that their pressures equalise; gas flows from the higher to the lower. Both beds are then taken to be at the mean of their start pressures, the start of the next ramp. The link is found from the beds, or given as device, which must join them.
Mocca.Interface.Concurrent Type
Concurrent(steps...; name = "")Steps on different beds that run at the same time, as one stage of the cycle. They must have the same duration and open different devices. The stage is named after its steps, such as "A adsorption, B evacuation".
Mocca.Interface.TimeStepping Type
TimeStepping(; max_dt = 1.0, first_dt = 0.05, keep_step_pattern = :auto)How each stage is split into time steps: steps of at most max_dt [s], starting with first_dt and doubling, which carries the solver smoothly through the abrupt start of a stage (and keeps the cycle map smooth for Newton's method and gradients; see setup_schedule).
keep_step_pattern keeps the number of steps of each stage fixed when its duration changes, stretching them instead, which is what the gradients with respect to durations describe. :auto does this when the process has a duration among its design variables; the pattern is then that of the durations when the first such variable was added, or when reset_step_pattern! was last called.
Processes
Mocca.Interface.Process Type
Process(fs, cycle; name = "", timestepping = TimeStepping(), key_component = 1,
ambient_pressure = 101325.0, pump_efficiency = 0.72, heat_capacity_ratio = 1.4,
ramp_rate = 0.5, initial_state = Dict(), parameters = Dict())A cyclic adsorption process: the flowsheet fs (built with Flowsheet(gas, feed), add_bed! and add_device!) operated by cycle (see Cycle).
timestepping: seeTimeStepping.key_component: the component to capture, by index or name, forperformance.ambient_pressure[Pa]: vacuum pumps discharge to it and feed compressors take gas from it;pump_efficiencyandheat_capacity_ratioare those of both.ramp_rate[1/s]: of the pressure ramps of steps that do not set their own.initial_state: per unit with holdup, a NamedTuple with any ofpressure,temperature,wall_temperatureandcomposition. By default each unit starts at the lowest pressure of its cycle (the feed pressure if no step sets it), at the feed temperature, full of light product, with the sorbent of a bed at equilibrium (seesetup_unit_state).parameters: per unit, values of its Jutul parameters to override, such asDict(:Bed => Dict(:AdsorbentDensity => 1200.0)).
Simulate it with simulate, find its cyclic steady state with cyclic_steady_state, and measure it with performance.
The process keeps its own copies of fs and cycle, so changing them afterwards does not change the process, and several processes can be built from the same ones. Change a process through its design variables (see add_variable!); p.flowsheet and p.cycle are its copies.
Jutul.simulate Method
simulate(p::Process; cycles = 1, state0 = initial_state(p), info_level = -1, kwarg...) → ProcessResultSimulate cycles cycles of the process. Extra keywords go to simulate_process.
Mocca.Interface.initial_state Function
initial_state(p) → DictThe initial state of every unit; see initial_state in Process.
Mocca.Interface.stages Function
stages(p::Process) → Vector{Stage}The stages of the process's cycle, with the device settings of each.
sourceMocca.Interface.cyclic_steady_state Function
cyclic_steady_state(p; method = :newton, tol = nothing, max_cycles = 500,
warm_start = true, handover = 3, krylov = :auto, coarse_cells = 40,
progress = isinteractive(), info_level = -1, kwarg...) → ProcessResultThe cyclic steady state of the process, with the states of its last cycle:
:newton(default):handoverplain cycles (3), each from the end of the last, then Newton's method on the cycle map (seenewton_cyclic_steady_state) totol(default1e-9). The plain cycles let the fastest modes die out, and Newton's method controls its steps, so it needs no closer start. Ahandoverbelow 1 is a cycle change instead: Anderson acceleration until the change is below it. If Newton's method fails or does not converge, Anderson acceleration goes on to a change of1e-1, and then if need be1e-3, and Newton's method tries again from each. Withwarm_start, it starts from the last steady state found for this process, and its Jacobian, which is much faster when the settings have changed a little. Withkrylov = true, each Newton step is solved by GMRES instead of from the full Jacobian, which pays off for fine grids, where the variables far outnumber the slow modes of the cycle;falsealways builds the full Jacobian. The default,:auto, decides on each grid: GMRES when the beds have more than 800 variables (a bed of about 130 cells with two components, 100 with three), the full Jacobian below;krylov = nsets that number of variables ton. On a fine grid, with a bed of more thancoarse_cellscells, the steady state is found first on beds of half as many cells (and so on, down tocoarse_cellsor fewer), each interpolated onto the next finer grid as the start of Newton's method there, which then needs only a few iterations; a coarser grid is converged to1e-6only. Starting fine grids cold is slow, and on some processes (such as the wet LPP VSA from about 80 cells) Newton's method does not converge from a few plain cycles. The coarser grids only give the start: the steady state is that ofp. If Newton's method does not converge from the coarser grid's steady state,pis started from its initial state as above. Passcoarse_cells = nothingto start from the initial state on any grid.:anderson:simulate_to_cyclic_steady_statetotol(default1e-6).:mass_balance:simulate_until_mass_balanceon the key component, between the feed and light inlet devices and the outlet devices.
With progress, on by default in an interactive session such as the REPL, each cycle is printed as it finishes: how many have run, how much the state still changes over a cycle against tol, a bar of how far it has come, and an estimate of the cycles and time left. In a terminal the line is rewritten in place, so only the end of each phase stays on screen. progress can also be an IO to print to, or a CycleProgress.
Mocca.Interface.performance Function
performance(r::ProcessResult) → NamedTuplePerformance of the process over the last cycle of r, from the streams through its devices (by their roles; see add_device!):
purity: mole fraction of the key component in the extract;recovery: fraction of the key component fed that leaves in the extract;specific_energy[kWh per tonne of key component in the extract]: vacuum pumps on every outlet device whose gas leaves below ambient pressure, and compressors on every inlet device whose gas enters a bed above it, as in Haghpanah et al. (2013);productivity[mol of key component per m³ of adsorbent per s];energy[J per cycle],cycle_time[s], the component totals [mol per cycle] of thefeed,extractand lightproduct, and theadsorbent_volume[m³] andadsorbent_mass[kg].
performance(p::Process; kwarg...) → NamedTuplePerformance at cyclic steady state; keywords go to cyclic_steady_state.
Mocca.Interface.validate Function
validate(p::Process) → Vector{MoccaInputError}Check the description of a process and return the problems found, or an empty vector. Constructors already throw the first error they find; this also reports problems that do not stop a simulation but likely give wrong results:
a cycle that does not compile;
a unit with no devices, or a flowsheet without feed or extract devices;
a bed whose axial dispersion was set for a velocity far from that of its adsorption step;
design variables whose places cannot be read.
Mocca.Interface.MoccaInputError Type
MoccaInputError(location, field, message)An error in the description of a process, found when it is set up rather than inside the solver. location says where it is, such as "step 3 (blowdown) of the cycle" or "adsorbent 13X", and field names the setting, or is empty. validate returns them as a list instead of throwing.
Design variables, gradients and optimisation
Mocca.Interface.add_variable! Function
add_variable!(p, name, places...; bounds = (-Inf, Inf), description = "")Add a design variable name to the process, setting every one of places (see StepPlace, FeedPlace, ParameterPlace, or tuples such as ("evacuation", :to), (:feed, :pressure) and (:parameter, :Bed, :AdsorbentDensity)). Since pressures carry over from step to step, one place is usually enough: the evacuation pressure is also where the next pressurisation starts. bounds are needed by optimise!.
Read and set it with p[name] and p[name] = value.
Mocca.Interface.StepPlace Type
StepPlace(step, field; sequence = "")The field (such as :to, :velocity or :duration) of a step of the cycle, picked by its position (counting through all steps of the sequence, including those in Concurrent groups) or its name, such as "evacuation", or "A evacuation" for a step of bed A. In a cycle of several sequences, sequence is the label of the sequence.
Mocca.Interface.FeedPlace Type
FeedPlace(field)A field of the flowsheet's Feed: :pressure or :temperature.
Mocca.Interface.ParameterPlace Type
ParameterPlace(unit, name)The Jutul parameter name of unit, such as ParameterPlace(:Bed, :AdsorbentDensity), set to one value throughout the unit.
Mocca.Interface.performance_gradient Function
performance_gradient(p; variables = all, steady_state = nothing, rtol = 1e-7,
progress = isinteractive()) → NamedTupleThe performance of the process at cyclic steady state and its gradient with respect to the design variables (names; see add_variable!), by adjoints and the implicit function theorem (see cyclic_steady_state_gradient). Purity, recovery, specific energy and productivity follow from cycle sums by the quotient rule.
Returns metrics, the performance; gradients, a NamedTuple per measure of NamedTuples per variable, such as g.gradients.recovery.P_L; and the steady_state used. progress goes to cyclic_steady_state. The gas properties of a bed stay as they were when the process was built, and so does its axial dispersion unless a variable sets its AxialDispersion parameter.
Mocca.Interface.optimise! Function
optimise!(p; variables = all, maximise = nothing, minimise = nothing,
at_least = (;), at_most = (;), linear_constraints = [], scale = (;),
weight = 1e3, max_it = 25, log = nothing, print_level = 0) → NamedTupleOptimise the process at cyclic steady state with gradients (BFGS in the box of the variables' bounds, Jutul.unit_box_bfgs): maximise or minimise one performance measure, such as maximise = :recovery, subject to others held above or below targets, such as at_least = (purity = 0.9,). Measure constraints are quadratic penalties with weight on the shortfall in units of scale (per measure, 1 by default), so the result may miss them slightly. linear_constraints are (Dict(name => coefficient, ...), bound) pairs, meaning Σ coefficient·variable ≤ bound, such as (Dict(:P_L => 1.0, :P_I => -1.0), -0.01bar) for P_I ≥ P_L + 0.01 bar.
Variables whose bounds are equal stay fixed. Each evaluation finds the cyclic steady state from the last one. log(values, result) is called after each. Returns the best values (a NamedTuple), its metrics, the number of evaluations and the history of every evaluation, and leaves the process set to the best values.
Mocca.Interface.reset_step_pattern! Function
reset_step_pattern!(p)Take the current stage durations as the pattern of time steps that changed durations stretch (see TimeStepping).
Files and the library
Mocca.Interface.read_process Function
read_process(path) → ProcessRead a process from a JSON file written by write_process or by hand, or from the library by name (see library_processes). Numbers are SI, or strings with a unit such as "0.1 bar". An adsorbent may be given by the name of a library adsorbent or the path of an adsorbent file, relative to the process file.
Mocca.Interface.write_process Function
write_process(path, p)Write process p to a JSON file: its gas, feed, adsorbents, columns, units, devices, cycle, time stepping, options, initial state, parameter settings and design variables. Read it back with read_process.
Mocca.Interface.load_adsorbent Function
load_adsorbent(name) → AdsorbentAn adsorbent from the library by name (see library_adsorbents), or from a JSON file at the path name.
Mocca.Interface.save_adsorbent Function
save_adsorbent(path, a; gas = nothing)Write adsorbent a to a JSON file.
Mocca.Interface.register_isotherm! Function
register_isotherm!(name, T; read)Make the isotherm data type T (an AbstractIsothermData) readable from files under "type": name: read(dict) builds it. Write it by adding a method of Mocca.isotherm_to_dict(data) that returns a Dict with "type" => name.
Ready-made processes
Mocca.Interface.four_stage_vsa_process Function
four_stage_vsa_process(; ncells = 30, durations = (15.0, 15.0, 30.0, 40.0),
P_H = 1e5, P_I = 0.2e5, P_L = 0.1e5, velocity = 1.0, ramp_rate = 0.5,
timestepping = TimeStepping(), name = "four-stage VSA")The single-bed, four-stage VSA cycle of Haghpanah et al. (2013): CO2/N2 (15/85) on zeolite 13X, with pressurisation, adsorption at interstitial velocity [m/s], co-current blowdown to P_I and counter-current evacuation to P_L [Pa].
feed ──V_feed──┐ ┌──V_product── raffinate
bottom Bed top
extract ──V_vacuum──┘As in the paper, vacuum pumps discharge at, and the feed compressor takes gas from, 1 bar.
sourceMocca.Interface.two_bed_vsa_process Function
two_bed_vsa_process(; ncells = 30, feed_time = 15.0, pressurisation_time = 15.0,
equalisation_time = 10.0, equalisation_conductance = 1e-6, P_H = 1e5, P_L = 0.1e5,
velocity = 1.0, ramp_rate = 0.5, timestepping = TimeStepping())Two beds, A and B, running the four-stage VSA cycle half a cycle apart, with a pressure equalisation valve joining their tops:
feed ──V_feed_A──┐ ┌──V_product_A── raffinate
bottom A top ┤
extract ─V_vacuum_A┘ V_eq
feed ──V_feed_B──┐ │
bottom B top ┤
extract ─V_vacuum_B┘ └──V_product_B── raffinateAfter a bed finishes adsorption it equalises with the other, evacuated bed, which saves feed gas and compression work:
| Stage | Bed A | Bed B |
|---|---|---|
| 1 | pressurisation (feed) | evacuation |
| 2 | adsorption | evacuation |
| 3 | equalisation (gives) | equalisation (takes) |
| 4 | evacuation | pressurisation (feed) |
| 5 | evacuation | adsorption |
| 6 | equalisation (takes) | equalisation (gives) |
After equalising, a bed is taken to be midway between the high and low pressures, where its pressurisation or evacuation starts. A bed evacuates through two stages, along one ramp to P_L.
Mocca.Interface.lpp_vsa_process Function
lpp_vsa_process(; ncells = 30, velocity = 0.45, t_lpp = 20.0, t_ads = 78.4, t_bd = 37.9,
t_evac = 111.6, P_H = 101325.0, P_I = 0.08e5, P_L = 0.03e5, ramp_rate = 0.5,
timestepping = TimeStepping())The single-bed VSA cycle with light product pressurisation (LPP) on zeolite 13X for wet flue gas, of section 3 of Krishnamurthy et al. (2014), at the operating conditions of its section 3.1. The light product is nitrogen.
feed ──V_feed──┐ ┌──V_product── raffinate
bottom Bed top ──┤
CO2 ◄─V_vacuum─┘ └──V_lpp────── light productMocca.Interface.dual_adsorbent_vsa_process Function
dual_adsorbent_vsa_process(; ncells = 30, silica_gel_length = 0.41, zeolite_length = 1.0,
velocity = 0.70, t_press = 20.0, t_ads = 46.20,
silica_gel = (t_bd = 42.21, t_evac = 46.02, P_I = 0.48e5, P_L = 0.30e5),
zeolite = (t_bd = 56.30, t_evac = 101.20, P_I = 0.07e5, P_L = 0.03e5),
P_H = 101325.0, ramp_rate = 0.5, timestepping = TimeStepping())The dual-adsorbent, two-bed VSA process of section 4 of Krishnamurthy et al. (2014): a silica gel bed that takes up the water, in series with a zeolite 13X bed that concentrates the CO2, at the minimum-energy conditions of its section 4.3. The beds' blowdown and evacuation times differ, so the cycle runs one sequence per bed side by side.
feed ──V_feed──┐ ┌──V_link──┐ ┌──V_product── raffinate
bottom SilicaGel top ┘ bottom Zeolite top┤
H2O ◄─V_waste──┘ │ └──V_lpp────── light product
CO2 ◄─V_vacuum┘Mocca.Interface.haghpanah_2013_gas Function
haghpanah_2013_gas()CO2/N2 with the properties used by Haghpanah et al. (2013), Ind. Eng. Chem. Res. 52, 4249.
sourceMocca.Interface.haghpanah_2013_column Function
haghpanah_2013_column(; length = 1.0)The column of Haghpanah et al. (2013).
sourceMocca.Interface.krishnamurthy_2014_gas Function
krishnamurthy_2014_gas()Wet flue gas, CO2/N2/H2O, with the properties of Table S2 of Krishnamurthy et al. (2014), Ind. Eng. Chem. Res. 53, 14462: one gas heat capacity for the mixture, and a diffusivity per component (CO2–N2 for CO2 and N2, N2–H2O for water).
sourceMocca.Interface.krishnamurthy_2014_column Function
krishnamurthy_2014_column(; length = 1.0)The column of Table S2 of Krishnamurthy et al. (2014).
sourceIndex
Mocca.UnitsMocca.Interface.DEVICE_ROLESMocca.Interface.AbstractIsothermDataMocca.Interface.AdsorbMocca.Interface.AdsorbentMocca.Interface.BlowdownMocca.Interface.ColumnMocca.Interface.ComponentMocca.Interface.ConcurrentMocca.Interface.CycleMocca.Interface.EqualiseMocca.Interface.EvacuateMocca.Interface.FeedMocca.Interface.FeedPlaceMocca.Interface.FlowsheetMocca.Interface.GasMocca.Interface.IdleMocca.Interface.MoccaInputErrorMocca.Interface.ParameterPlaceMocca.Interface.PressuriseMocca.Interface.ProcessMocca.Interface.PurgeMocca.Interface.StepPlaceMocca.Interface.TimeSteppingMocca.Interface.WallJutul.simulateMocca.Interface.add_bed!Mocca.Interface.add_device!Mocca.Interface.add_variable!Mocca.Interface.build_isothermMocca.Interface.componentMocca.Interface.compositionMocca.Interface.cyclic_steady_stateMocca.Interface.dual_adsorbent_vsa_processMocca.Interface.dual_site_langmuirMocca.Interface.equilibrium_loadingMocca.Interface.four_stage_vsa_processMocca.Interface.haghpanah_2013_columnMocca.Interface.haghpanah_2013_gasMocca.Interface.initial_stateMocca.Interface.isotherm_tableMocca.Interface.krishnamurthy_2014_columnMocca.Interface.krishnamurthy_2014_gasMocca.Interface.langmuirMocca.Interface.library_adsorbentsMocca.Interface.library_processesMocca.Interface.load_adsorbentMocca.Interface.lpp_vsa_processMocca.Interface.optimise!Mocca.Interface.performanceMocca.Interface.performance_gradientMocca.Interface.print_flowsheet_diagramMocca.Interface.read_processMocca.Interface.register_component!Mocca.Interface.register_isotherm!Mocca.Interface.reset_step_pattern!Mocca.Interface.save_adsorbentMocca.Interface.stagesMocca.Interface.two_bed_vsa_processMocca.Interface.validateMocca.Interface.write_processMocca.Units.celsiusMocca.Interface.ProcessResultMocca.Interface.write_resultsMocca.Operation.compressor_energyMocca.Operation.specific_energy_kwh_per_tonneMocca.Operation.stream_totalsMocca.Operation.vacuum_pump_energyMocca.Plotting.plot_cellMocca.Plotting.plot_cell_comparisonMocca.Plotting.plot_flowsheet_streamsMocca.Plotting.plot_profilesMocca.Plotting.plot_stateMocca.Plotting.plot_state_comparison