Getting started
This example goes through a whole study of a process: read it from Mocca's library, change its operating settings, run it to cyclic steady state, measure its performance, look at the results, export them to a spreadsheet, save the process to a file to edit and share, and check that the cycle is steady.
The process is the four-step vacuum swing adsorption (VSA) cycle of Haghpanah et al. (2013), which captures CO₂ from a flue gas of 15% CO₂ in N₂ on zeolite 13X:
Pressurisation: the top of the bed is closed and feed enters at the bottom, bringing the bed up to the feed pressure.
Adsorption: feed enters the bottom; CO₂ is adsorbed and nitrogen leaves the top.
Blowdown: the bottom is closed and the bed is taken down to an intermediate pressure through the top, which removes most of the nitrogen.
Evacuation: the top is closed and the bed is evacuated through the bottom, which gives the CO₂ product.
import Mocca
using Mocca.Units1. Read a process from the library
The library holds the processes and adsorbents of published studies:
println(Mocca.library_processes())
println(Mocca.library_adsorbents())["haghpanah_2013_four_stage_vsa", "krishnamurthy_2014_dual_adsorbent_vsa", "krishnamurthy_2014_lpp_vsa"]
["silica_gel_krishnamurthy_2014", "zeolite_13x_haghpanah_2013", "zeolite_13x_krishnamurthy_2014"]A process holds everything needed to run it: the gas and feed, the adsorbent and column, the flowsheet of beds and devices, the cycle of steps, the time stepping and the design variables.
process = Mocca.read_process("haghpanah_2013_four_stage_vsa")Process Haghpanah et al. (2013), four-step VSA: CO2/N2 feed at 1.0 bar, 298.15 K, capturing CO2
Bed: fixed bed of zeolite 13X, 30 cells
bottom V_feed (feed), from boundary at 1.0 bar; V_vacuum (extract), to boundary at 0.1 bar
top V_product (product), to boundary at 1.0 bar
cycle of 4 stages, 100.0 s:
pressurisation 15.0 s V_feed
adsorption 15.0 s V_feed, V_product
blowdown 30.0 s V_product
evacuation 40.0 s V_vacuum
variables: t_ads, t_bd, t_evac, P_I, P_L, v_feed2. Change the operating settings
The design variables of the process are named settings. Setting one changes every place it acts: the evacuation pressure P_L is also where the next pressurisation starts. Values are in SI units; Mocca.Units has conversions such as bar.
Mocca.variable_names(process)6-element Vector{Symbol}:
:t_ads
:t_bd
:t_evac
:P_I
:P_L
:v_feedprocess[:P_L] = 0.05bar
process[:t_ads] = 30.0
process[:P_L], process[:t_ads](5000.0, 30.0)The cycle is compiled into stages, each a set of device settings:
for st in Mocca.stages(process)
println(rpad(st.name, 16), st.duration, " s, opens ", join(keys(st.settings), ", "))
endpressurisation 15.0 s, opens V_feed
adsorption 30.0 s, opens V_feed, V_product
blowdown 30.0 s, opens V_product
evacuation 40.0 s, opens V_vacuum3. Run it to cyclic steady state
cyclic_steady_state repeats the cycle until the state of the bed at the end of a cycle is the state it started from, accelerating the approach with Newton's method. The result holds the steady cycle.
result = Mocca.cyclic_steady_state(process)
println("converged: $(result.converged), cycles: $(result.iterations)")converged: true, cycles: 244. Measure its performance
Purity and recovery of CO₂ in the extract (the vacuum line), the pump and compressor energy per tonne of CO₂, and the productivity per m³ of adsorbent:
perf = Mocca.performance(result)
println("purity $(round(100perf.purity, digits = 1))%, recovery $(round(100perf.recovery, digits = 1))%, ",
"$(round(perf.specific_energy, digits = 1)) kWh/t, productivity $(round(perf.productivity, sigdigits = 3)) mol/(m³·s)")purity 88.4%, recovery 64.3%, 126.9 kWh/t, productivity 0.684 mol/(m³·s)5. Look at the results
The flow and composition through each device over the cycle, with the stages shaded. Flow is positive from a device's inlet to its outlet, so the vacuum line is positive while it draws gas out of the bed.
stages = Mocca.stages(process)
f_streams = Mocca.plot_flowsheet_streams(result.states, process.model, result.timesteps; stages = stages)
Mole fractions and loadings along the bed at the end of each stage. Most of the CO₂ is held near the feed end, at the bottom. Blowdown removes most of the nitrogen, and at the end of evacuation the gas in the bed is nearly pure CO₂.
bed = process.flowsheet[:Bed]
f_profiles = Mocca.plot_profiles(result.states, result.timesteps, bed, cumsum([st.duration for st in stages]);
labels = [st.name for st in stages], unit = :Bed)
Each state in result.states holds the state of every bed and device after a time step, in SI units, for any other quantity. For example, the range of the bed temperature over the cycle:
T_min = minimum(minimum(s[:Bed][:Temperature]) for s in result.states)
T_max = maximum(maximum(s[:Bed][:Temperature]) for s in result.states)
println("bed temperature from $(round(T_min - 273.15, digits = 1)) to $(round(T_max - 273.15, digits = 1)) °C")bed temperature from 20.0 to 28.4 °C6. Export to a spreadsheet
write_results writes the result to a folder of CSV files that open in a spreadsheet, in bara, °C, mol%, kg/h, Nm³/h and mol/kg, with the units in the column headers. It also writes the process, to rerun it later.
dir = joinpath(mktempdir(), "base_case")
files = Mocca.write_results(dir, result)
foreach(println, basename.(files))summary.csv
streams.csv
stage_streams.csv
stream_history.csv
bed_history.csv
bed_profiles.csv
process.jsonsummary.csv holds the feed, the cycle, the performance and the mass balance of each component over the cycle:
print(read(joinpath(dir, "summary.csv"), String))Section,Quantity,Value,Unit
Process,Name,"Haghpanah et al. (2013), four-step VSA",
Process,Components,CO2 / N2,
Process,Key component,CO2,
Process,Result,cyclic steady state,
Process,Converged,yes,
Process,Cycles to steady state,24,
Process,Final cycle-to-cycle change,5.4331624e-10,-
Process,Mocca version,0.2.0,
Process,Written,2026-10-20 12:50,
Feed,Pressure,1.0,bara
Feed,Temperature,25.0,degC
Feed,CO2,15.0,mol%
Feed,N2,85.0,mol%
Feed,Mean feed flow,32.053467,kg/h
Feed,Mean feed flow,23.631899,Nm3/h
Feed,Mean feed flow,0.29287176,mol/s
Cycle,Cycle time,115.0,s
Cycle,Stage 1: pressurisation,15.0,s
Cycle,Stage 2: adsorption,30.0,s
Cycle,Stage 3: blowdown,30.0,s
Cycle,Stage 4: evacuation,40.0,s
Performance,CO2 purity in extract,88.353642,mol%
Performance,CO2 recovery,64.299401,%
Performance,CO2 in extract,4.4753763,kg/h
Performance,Productivity,0.68351739,mol/(m3 adsorbent.s)
Performance,Productivity,0.00060488265,mol/(kg adsorbent.s)
Performance,Specific energy,126.94978,kWh/t CO2
Performance,Specific energy,0.45701922,GJ/t CO2
Performance,Pump and compressor work,65.337025,kJ/cycle
Performance,Mean pump and compressor power,0.56814805,kW
Mass balance,CO2 in,5.0520379,mol/cycle
Mass balance,CO2 out,5.0520379,mol/cycle
Mass balance,CO2 accumulated in beds,-5.0022209e-12,mol/cycle
Mass balance,CO2 imbalance,-6.1597138e-10,% of in
Mass balance,N2 in,28.628215,mol/cycle
Mass balance,N2 out,28.628215,mol/cycle
Mass balance,N2 accumulated in beds,-3.9783628e-13,mol/cycle
Mass balance,N2 imbalance,1.0527238e-10,% of in
Mass balance,Total in,33.680253,mol/cycle
Mass balance,Total out,33.680253,mol/cycle
Mass balance,Total accumulated in beds,-5.4000571e-12,mol/cycle
Mass balance,Total imbalance,-2.9115477e-12,% of in
Adsorbent,Volume,0.041326261,m3
Adsorbent,Mass,46.698675,kg
Beds,Bed adsorbent,zeolite 13X,
Beds,Bed length,1.0,m
Beds,Bed inner diameter,0.289,m
Beds,Bed bed porosity,0.37,-
Beds,Bed particle diameter,2.0,mm
Beds,Bed adsorbent mass,46.698675,kg
Beds,Bed cells,30,streams.csv is a stream table, one row per device, with its composition, flow, amount per cycle, temperature and pressures. Some of its columns:
rows = split.(readlines(joinpath(dir, "streams.csv")), ',')
columns = indexin(["Device", "CO2 [mol%]", "N2 [mol%]", "Mean flow [kg/h]", "Total [mol/cycle]"], rows[1])
for row in rows
println(join(rpad.(row[columns], 20)))
endDevice CO2 [mol%] N2 [mol%] Mean flow [kg/h] Total [mol/cycle]
V_feed 15.0 85.0 32.053467 33.680253
V_product 6.0112986 93.988701 27.202771 30.00363
V_vacuum 88.353642 11.646358 4.8506964 3.6766227stage_streams.csv breaks it down by stage. stream_history.csv and bed_history.csv hold the devices and both ends of each bed at every time step, and bed_profiles.csv the state along each bed at the end of each stage, with the cycle and stage of each row so that a filter or a pivot table picks out one device or stage:
foreach(println, first(eachline(joinpath(dir, "bed_history.csv")), 4))Cycle,Time [s],Time in cycle [s],Stage,Bed,End,Pressure [bara],Temperature [degC],Wall temperature [degC],y CO2 [mol%],y N2 [mol%],q CO2 [mol/kg],q N2 [mol/kg]
1,0.05,0.05,pressurisation,Bed,bottom,0.07224079,19.975102,25.005816,31.566028,68.433972,3.0596149,0.0017030768
1,0.05,0.05,pressurisation,Bed,top,0.055029402,24.202797,24.994718,88.140645,11.859355,2.7402954,0.00040639757
1,0.15,0.15,pressurisation,Bed,bottom,0.11662055,20.024435,25.005767,18.158664,81.841336,3.0589496,0.0059450968For a spreadsheet set up with a decimal comma, pass delim = ';', decimal = ','.
7. Save the process, edit it and read it back
A process file holds every layer of the process and reads back as the same process:
file = Mocca.write_process(joinpath(mktempdir(), "vsa.json"), process)
same = Mocca.read_process(file)
same[:P_L] / bar0.05Process files can be written by hand. Numbers are SI, or strings with a unit, such as "0.1 bar" or "25 °C". Compositions and per-component values are keyed by component name, steps by their type, and an adsorbent may be the name of a library adsorbent. The Process files page describes the format.
text = """
{
"name": "a short cycle",
"gas": {"components": ["CO2", "N2"], "molar_masses": {"N2": 0.028}, "heat_capacities": {"CO2": 697.5687, "N2": 1096.4}},
"feed": {"composition": {"CO2": 0.15, "N2": 0.85}, "pressure": "1 bar", "temperature": "25 °C"},
"adsorbents": {"13X": "zeolite_13x_haghpanah_2013"},
"columns": {"column": {"length": "1 m", "radius": "14.45 cm", "outer_radius": "16.2 cm", "porosity": 0.37}},
"units": [{"name": "Bed", "adsorbent": "13X", "column": "column", "ncells": 30}],
"devices": [
{"name": "V_feed", "role": "feed", "at": ["Bed", "bottom"]},
{"name": "V_product", "role": "product", "at": ["Bed", "top"]},
{"name": "V_vacuum", "role": "extract", "at": ["Bed", "bottom"]}
],
"cycle": {"steps": [
{"type": "pressurise", "duration": "10 s"},
{"type": "adsorb", "duration": "10 s", "velocity": "1 m/s"},
{"type": "blowdown", "duration": "20 s", "to": "0.2 bar"},
{"type": "evacuate", "duration": "30 s", "to": "0.1 bar"}
]},
"time_stepping": {"max_dt": 1.0, "first_dt": 0.05}
}
"""
file = joinpath(mktempdir(), "short.json")
write(file, text)
short = Mocca.read_process(file)Process a short cycle: CO2/N2 feed at 1.0 bar, 298.15 K, capturing CO2
Bed: fixed bed of zeolite 13X, 30 cells
bottom V_feed (feed), from boundary at 1.0 bar; V_vacuum (extract), to boundary at 0.1 bar
top V_product (product), to boundary at 1.0 bar
cycle of 4 stages, 70.0 s:
pressurisation 10.0 s V_feed
adsorption 10.0 s V_feed, V_product
blowdown 20.0 s V_product
evacuation 30.0 s V_vacuumMistakes are reported with where they are. A blowdown that goes up in pressure:
try
write(file, replace(text, "\"to\": \"0.2 bar\"" => "\"to\": \"2 bar\""))
Mocca.read_process(file)
catch e
println(sprint(showerror, e))
endMoccaInputError in step 3 (blowdown) of the cycle, to: the step should lower the pressure of Bed, but goes from 1.0 bar to 2.0 bar8. Check the steady state
simulate runs a given number of cycles from a given state, rather than to steady state. Started from the start of the steady cycle, result.state0, the bed comes back to the same state at the end of every cycle. The largest relative change of any variable over three cycles:
result_cycles = Mocca.simulate(process; cycles = 3, state0 = result.state0)
Mocca.cycle_change(process.model, result.state0, result_cycles.states[end])2.3119849011322536e-7The top of the bed, where the nitrogen product leaves, over the three cycles:
bed_states = [s[:Bed] for s in result_cycles.states]
f_outlet = Mocca.plot_cell(bed_states, bed, result_cycles.timesteps, 30)
Example on GitHub
If you would like to run this example yourself, it can be downloaded from the Mocca.jl GitHub repository.
This page was generated using Literate.jl.