Quick Overview

Click the badge below to try this tutorial interactively in your browser:

Launch Binder

You can also run this tutorial inGoogle Colab. It takes a one-time setup per session: follow theColab instructions.

  • Prepared by:

  • Learning objectives. After this tutorial, you will be able to:

    • See how the main QSDsan classes (Component, WasteStream, SanUnit, System, TEA, LCA, Process) fit together

    • Preview the typical workflow from defining components to running an analysis

    • Decide which subsequent tutorials to read based on what you want to build

QSDsan is a Python platform for the quantitative sustainable design of sanitation and resource recovery systems. Built on BioSTEAM, it lets you assemble treatment trains as connected unit operations, compute techno-economic and life-cycle impact metrics, and explore uncertainty and sensitivity across designs.

This tutorial is a brief tour of the main pieces. Each class shown here has its own dedicated tutorial; pointers are at the end.

Setup

Import QSDsan and confirm the installed version.

[1]:
import qsdsan as qs, exposan
print(f'This tutorial was made with qsdsan v{qs.__version__} and exposan v{exposan.__version__}')
This tutorial was made with qsdsan v1.5.3 and exposan v1.5.3

1. The pieces of QSDsan

QSDsan groups its functionality into a small set of core classes:

QSDsan UML diagram

Thermosteam, BioSTEAM, and BW2QSD are external packages that QSDsan leverages.

Class

What it represents

Component

A chemical or biological species, with thermodynamic and biological properties

WasteStream

A flow of components, with composition and thermodynamic state

SanUnit

A unit operation, with _run, _design, and _cost methods

System

A connected set of units representing a treatment train

Process

Rate expressions and stoichiometry for dynamic biokinetic models

TEA

Techno-economic analysis on a System

LCA

Life cycle assessment on a System

Model

Parameters and metrics for uncertainty and sensitivity analyses

The rest of this tour shows each one briefly.

2. Components and Streams

A Component describes one species (a chemical, a microbial group, or an aggregate parameter such as COD). A Components registry defines the thermodynamic basis for the model and must be set as the active thermo before building streams. See the Component tutorial for more information.

[2]:
cmps = qs.Components.load_default()
qs.set_thermo(cmps)
cmps.show()
CompiledComponents([
    S_H2,      S_CH4,       S_CH3OH,     S_Ac,
    S_Prop,    S_F,         S_U_Inf,     S_U_E,
    C_B_Subst, C_B_BAP,     C_B_UAP,     C_U_Inf,
    X_B_Subst, X_OHO_PHA,   X_GAO_PHA,   X_PAO_PHA,
    X_GAO_Gly, X_PAO_Gly,   X_OHO,       X_AOO,
    X_NOO,     X_AMO,       X_PAO,       X_MEOLO,
    X_FO,      X_ACO,       X_HMO,       X_PRO,
    X_U_Inf,   X_U_OHO_E,   X_U_PAO_E,   X_Ig_ISS,
    X_MgCO3,   X_CaCO3,     X_MAP,       X_HAP,
    X_HDP,     X_FePO4,     X_AlPO4,     X_AlOH,
    X_FeOH,    X_PAO_PP_Lo, X_PAO_PP_Hi, S_NH4,
    S_NO2,     S_NO3,       S_PO4,       S_K,
    S_Ca,      S_Mg,        S_CO3,       S_N2,
    S_O2,      S_CAT,       S_AN,        H2O,
])

Each component carries a rich set of properties:

[3]:
cmps.S_CH4.show(chemical_info=True)
Component: S_CH4 (phase_ref='g')
[Names]  CAS: 74-82-8
         InChI: CH4/h1H4
         InChI_key: VNWKTOKETHGBQD-U...
         common_name: methane
         iupac_name: ('methane',)
         pubchemid: 297
         smiles: C
         formula: CH4
[Groups] Dortmund: <Empty>
         UNIFAC: <Empty>
         PSRK: <Empty>
         NIST: <Empty>
[Data]   MW: 16.042 g/mol
         Tm: 90.75 K
         Tb: 111.67 K
         Tt: 90.694 K
         Tc: 190.56 K
         Pt: 11696 Pa
         Pc: 4.5992e+06 Pa
         Vc: 9.8628e-05 m^3/mol
         Hf: -74534 J/mol
         S0: 186.3 J/K/mol
         LHV: 8.0257e+05 J/mol
         HHV: 8.9059e+05 J/mol
         Hfus: 940 J/mol
         Sfus: 0
         omega: 0.01142
         dipole: 0 Debye
         similarity_variable: 0.31167
         iscyclic_aliphatic: 0
         combustion: {'CO2': 1, 'O2'...
Component-specific properties:
[Others] measured_as: COD
         description: Dissolved Methane
         particle_size: Dissolved gas
         degradability: Readily
         organic: True
         i_C: 0.18767 g C/g COD
         i_N: 0 g N/g COD
         i_P: 0 g P/g COD
         i_K: 0 g K/g COD
         i_Mg: 0 g Mg/g COD
         i_Ca: 0 g Ca/g COD
         i_mass: 0.25067 g mass/g COD
         i_charge: 0 mol +/g COD
         i_COD: 1 g COD/g COD
         i_NOD: 0 g NOD/g COD
         f_BOD5_COD: 0
         f_uBOD_COD: 0
         f_Vmass_Totmass: 1
         chem_MW: 16.042

QSDsan provides three stream classes in a hierarchy. The Stream class is from Thermosteam (BioSTEAM’s thermodynamic engine), which represents a generic material flow. SanStream adds stream-level life cycle impact functionalit, and WasteStream adds wastewater-modeling functionality on top of SanStream. See this comparison page and the WasteStream tutorial for more information.

A WasteStream holds component flows plus the thermodynamic state. Several convenience initializers are available; for example, a typical municipal influent can be built from a COD-based model:

[4]:
ww = qs.WasteStream.codbased_inf_model('ww', flow_tot=100)
ww.show()
WasteStream: ww
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (g/hr): S_F        7.5
             S_U_Inf    3.25
             C_B_Subst  4
             X_B_Subst  22.7
             X_U_Inf    5.58
             X_Ig_ISS   5.23
             S_NH4      2.5
             S_PO4      0.8
             S_K        2.8
             S_Ca       14
             S_Mg       5
             S_CO3      12
             S_N2       1.8
             S_CAT      0.3
             S_AN       1.2
             ...        9.96e+04
 WasteStream-specific properties:
  pH         : 7.0
  Alkalinity : 10.0 mmol/L
  COD        : 430.0 mg/L
  BOD        : 249.4 mg/L
  TC         : 265.0 mg/L
  TOC        : 137.6 mg/L
  TN         : 40.0 mg/L
  TP         : 10.0 mg/L
  TK         : 28.0 mg/L
  TSS        : 209.3 mg/L
 Component concentrations (mg/L):
  S_F          75.0
  S_U_Inf      32.5
  C_B_Subst    40.0
  X_B_Subst    226.7
  X_U_Inf      55.8
  X_Ig_ISS     52.3
  S_NH4        25.0
  S_PO4        8.0
  S_K          28.0
  S_Ca         140.0
  S_Mg         50.0
  S_CO3        120.0
  S_N2         18.0
  S_CAT        3.0
  S_AN         12.0
  ...

3. Unit Operations

A SanUnit is a unit operation. QSDsan have many pre-built subclasses (clarifiers, anaerobic digesters, membrane filters, etc.) that are often used for sanitation and resource recovery facilities. You can also subclass SanUnit to model your own; see the basic and advanced tutorials for more information.

[5]:
# A small sample of built-in unit operations
# See the list of unit operations in the documentation for more details:
# https://qsdsan.readthedocs.io/en/latest/api/unit_operations/index.html
ops = sorted(i for i in dir(qs.unit_operations) if not i.startswith('_'))
print(f'qsdsan ships {len(ops)} unit operations. A few examples:')
for o in ops[:10]:
    print(f'  - {o}')
qsdsan ships 132 unit operations. A few examples:
  - A1junction
  - ADM1ptomASM2d
  - ADM1toASM2d
  - ADMjunction
  - ADMtoASM
  - ASM2dtoADM1
  - ASM2dtomADM1
  - ASMtoADM
  - ActivatedSludgeProcess
  - AdiabaticMultiStageVLEColumn

4. Process Models

A Process represents a single reaction defined by a rate expression and component stoichiometry. A Processes instance groups related Process objects into a complete dynamic model such as ASM1, ASM2d, or ADM1. See the Process tutorial for more information.

Note. If your model is steady-state, i.e., flows and unit behavior do not change with time, you do not need Process. It is only needed when you need to capture time-varying behavior. The dynamic-modeling tutorials in Part III cover this in depth.

[6]:
asm1_cmps = qs.process_models.create_asm1_cmps(set_thermo=True)
asm1 = qs.process_models.ASM1()
print(asm1.rate_equations)
                                         rate_equation
aero_growth_hetero  4.0*S_NH*S_O*S_S*X_BH/((S_NH + ...
anox_growth_hetero  0.64*S_NH*S_NO*S_S*X_BH/((S_NH ...
aero_growth_auto    0.5*S_NH*S_O*X_BA/((S_NH + 1.0)...
decay_hetero                                  0.3*X_BH
decay_auto                                   0.05*X_BA
ammonification                          0.05*S_ND*X_BH
hydrolysis          3.0*X_BH*X_S*(0.16*S_NO/((S_NO ...
hydrolysis_N        3.0*X_BH*X_ND*(0.16*S_NO/((S_NO...

5. Assembling Unit Operations into a System

Units connect via influent and effluent streams to form a System. Below is an example from EXPOsan (the companion package of established systems). The bwaise study models sanitation for Bwaise, an unplanned settlement in Kampala, Uganda. See the System tutorial for more information.

[7]:
from exposan import bwaise as bw
bw.load()
bw.sysA.diagram()
../_images/tutorials_0_Quick_Overview_18_0.svg

Once simulated, you can read per-unit results:

[8]:
print(bw.A2.results())
Pit latrine                                      Units       A2
Design              Number of users per toilet               16
                    Parallel toilets                   2.85e+04
                    Emptying period                 yr      0.8
                    Single pit volume               m3     3.66
                    Single pit area                 m2      0.8
                    Single pit depth                 m     4.57
                    Cement                          kg    2e+07
                    Sand                            kg 9.05e+07
                    Gravel                          kg 3.65e+07
                    Brick                           kg 6.47e+06
                    Plastic                         kg 2.88e+05
                    Steel                           kg 9.58e+05
                    Wood                            m3 5.42e+03
                    Excavation                      m3 1.04e+05
Purchase cost       Total toilets                  USD 1.28e+07
Total purchase cost                                USD 1.28e+07
Utility cost                                    USD/hr        0
Additional OPEX                                 USD/hr     73.1

6. Techno-Economic Analysis and Life Cycle Assessment (TEA/LCA)

A TEA runs the techno-economics on a System; an LCA runs the impact assessment. Both attach to the same System and read its simulation results. See the TEA and LCA tutorials for more information.

[9]:
c = qs.currency
print('For sysA in the Bwaise study:\n')
for attr in ('NPV', 'EAC', 'CAPEX', 'AOC', 'sales', 'net_earnings'):
    uom = c if attr in ('NPV', 'CAPEX') else f'{c}/yr'
    print(f'  - {attr}: {getattr(bw.teaA, attr):,.0f} {uom}')
For sysA in the Bwaise study:

  - NPV: -42,012,131 USD
  - EAC: 6,706,210 USD/yr
  - CAPEX: 31,421,918 USD
  - AOC: 1,844,554 USD/yr
  - sales: 206,017 USD/yr
  - net_earnings: -1,638,537 USD/yr

The LCA object summarizes impacts by category and contributor:

[10]:
bw.lcaA
LCA: sysA (lifetime 8 yr)
Impacts:
                           Construction  Transportation   Stream   Others    Total
GlobalWarming (kg CO2-eq)      3.13e+07        9.57e+05 1.82e+08 5.19e+04 2.14e+08

7. Uncertainty and Sensitivity Analyses

To perform uncertainty and sensitiviy analysis, a Model object is built on a System to add uncertain distributions of the input parameters and define output metrics of interest. The Model object can be then used to run Monte Carlo (for uncertainty) and sensitivity analyses. See the Uncertainty and Sensitivity Analyses for more information.

[11]:
bw.modelA  # shows the declared uncertainty parameters

QSDsan also includes a stats module for statistical analysis (e.g., global sensitivity analysis), including and visualization helpers.

Where to go next

The tutorials are organized in three parts. You just finished Part I.

Part II. QSDsan’s core classes. Each tutorial dives into one or a few of the classes you just saw.

Part III. Integrating process models. Build dynamic, multi-component models on top of QSDsan.


↑ Back to top