5. Calculting wastewater quality¶
The core functionality of pySIMDEUM is to deliver consumption, and if required, discharge water profiles for houses. However, some optional post-processing analysis is available for the user to calculate wastewater quality. This includes:
- Nutrient concentrations
- n
- p
- cod
- bod5
- ss
- amm
- Discharge temperature
- Note that this is at the point when it leaves the end use. In reality, temperature changes over time so this figure may not represent the exact temperatures.
Nutrient concentrations¶
import pysimdeum
import matplotlib.pyplot as plt
import os
import toml
from pysimdeum.data import DATA_DIR
import pysimdeum.utils.wastewater_quality as wq
Some default statistics for nutrient concentrations are provided in the NL and UK data folders in ww_nutrients.toml. The statistics are provided in g/use. This is an important point as pysimdeum allow for the inclusion of different enduse subtypes - an example being a more water efficient toilet. The amount of urine deposited will not change as a result but due to the water usage, nutrient concentration will. As a result, it makes sense to input data as per use, with the actualy concentration in the water being calculated by pysimdeum.
The nutrient concentration config file is structured as below, listed by enduse with nested data by substype (if subtypes are included for that enduse)
nutrient_data = toml.load(os.path.join(DATA_DIR, "NL", 'ww_nutrients.toml'))
nutrient_data
{'Shower': {'shower': {'n': 0.49,
'p': 0.07,
'cod': 13.93,
'bod5': 7.43,
'ss': 13.93,
'amm': 0.06}},
'Bathtub': {'bathtub': {'n': 0.85,
'p': 0.15,
'cod': 26.05,
'bod5': 15.07,
'ss': 7.79,
'amm': 0.12}},
'Wc': {'urine': {'n': 2.0,
'p': 0.23,
'cod': 2.0,
'bod5': 2.46,
'ss': 0.0,
'amm': 0.92},
'faeces': {'n': 3.09,
'p': 0.48,
'cod': 30.23,
'bod5': 12.03,
'ss': 3.04,
'amm': 1.1}},
'KitchenTap': {'consumption': {'n': 0,
'p': 0,
'cod': 0,
'bod5': 0,
'ss': 0,
'amm': 0},
'dishes': {'n': 0.35,
'p': 0.28,
'cod': 7.51,
'bod5': 4.48,
'ss': 4.68,
'amm': 0.03},
'washing_hands': {'n': 0.35,
'p': 0.28,
'cod': 7.51,
'bod5': 4.48,
'ss': 4.68,
'amm': 0.03},
'other': {'n': 0.35,
'p': 0.28,
'cod': 7.51,
'bod5': 4.48,
'ss': 4.68,
'amm': 0.03}},
'BathroomTap': {'washing_shaving': {'n': 0.04,
'p': 0.14,
'cod': 1.5,
'bod5': 0.81,
'ss': 0.35,
'amm': 0.002},
'brushing_teeth': {'n': 0.04,
'p': 0.14,
'cod': 1.5,
'bod5': 0.81,
'ss': 0.35,
'amm': 0.002}},
'WashingMachine': {'washingmachine': {'n': 1.49,
'p': 7.29,
'cod': 65.25,
'bod5': 27.25,
'ss': 14.1,
'amm': 0.82}},
'Dishwasher': {'dishwasher': {'n': 1.35,
'p': 2.04,
'cod': 30,
'bod5': 18,
'ss': 9.7,
'amm': 0.82}}}
As always, initialise your household.
# Build a house (two-person household)
house = pysimdeum.built_house(house_type='one_person', duration ='1 day', simulate_discharge=True)
Nutrient concentration is calculated post-simulation and can be thought of as a stage of post-processing analysis. The calculation only requires the discharge xarray.Dataset that is generated when building a house and specifying to simulate_discharge. The country argument defaults to NL, can be optionally switched to UK. time_agg is the level of temporal aggregation you want your resulting nutrient concentration profile to be aggregated to, defaults to hourly. Options include:
s: secondsm: minutes15min: 15-minute intervals30min: 30-minute intervalsh: hourly (default)
The calculation is simple. For each discharge_event the sampled nutrient concentration for the enduse and nutrient is divided by the total discharge volume for that event.
$$ \text{Nutrient Concentration (g/L)} = \frac{\text{Nutrient per Use}}{\text{Total Flow}} $$
When temporally aggregating to household level, its effectively a weighted average as the nutrient concentration (g/L) is multiplied by the flow at that timestamp, for each enduse. These are summated and then weighted against the total discharge flow of the household at that timestamp. If temporal aggregation greater than a second is chosen, values for each of the timestamps will be summated:
$$ \text{Weighted Average} = \frac{\sum (\text{Flow} \cdot \text{Nutrient Concentration})}{\sum \text{Flow}} $$
nutrients_df = wq.hh_discharge_nutrients(house.discharge, country='NL', time_agg='h')
nutrients_df
| time | flow | n | p | cod | bod5 | ss | amm | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2025-05-02 00:00:00 | 8.654921 | 0.494830 | 0.067083 | 3.811737 | 0.253480 | 0.390826 | 0.158951 |
| 1 | 2025-05-02 01:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 2 | 2025-05-02 02:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 3 | 2025-05-02 03:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 4 | 2025-05-02 04:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 5 | 2025-05-02 05:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 6 | 2025-05-02 06:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 7 | 2025-05-02 07:00:00 | 9.192220 | 0.078943 | 0.031911 | 0.299729 | 0.499543 | 0.230307 | 0.095784 |
| 8 | 2025-05-02 08:00:00 | 11.155878 | 0.519082 | 0.072037 | 4.863274 | 2.104241 | 1.139102 | 0.100817 |
| 9 | 2025-05-02 09:00:00 | 46.390759 | 0.110924 | 0.470963 | 4.264267 | 2.573510 | 0.763051 | 0.071490 |
| 10 | 2025-05-02 10:00:00 | 16.868600 | 0.223197 | 0.402607 | 4.680069 | 1.944211 | 0.862351 | 0.152916 |
| 11 | 2025-05-02 11:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 12 | 2025-05-02 12:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 13 | 2025-05-02 13:00:00 | 7.200000 | 0.207421 | 0.027544 | 0.267178 | 0.320876 | 0.000000 | 0.109582 |
| 14 | 2025-05-02 14:00:00 | 14.400000 | 0.401183 | 0.032569 | 0.307362 | 0.342404 | 0.000000 | 0.117011 |
| 15 | 2025-05-02 15:00:00 | 30.323616 | 0.004865 | 0.003584 | 0.090305 | 0.088255 | 0.128577 | 0.000680 |
| 16 | 2025-05-02 16:00:00 | 38.309607 | 0.036118 | 0.069355 | 1.440679 | 0.807622 | 0.604678 | 0.027420 |
| 17 | 2025-05-02 17:00:00 | 34.532268 | 0.059063 | 0.107519 | 1.757749 | 0.972469 | 0.575691 | 0.050698 |
| 18 | 2025-05-02 18:00:00 | 19.500112 | 0.061057 | 0.093575 | 2.081822 | 1.086653 | 0.555723 | 0.047253 |
| 19 | 2025-05-02 19:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 20 | 2025-05-02 20:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 21 | 2025-05-02 21:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 22 | 2025-05-02 22:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 23 | 2025-05-02 23:00:00 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
Discharge temperature¶
The process is very similar for calculating the temperature of wastewater. Each enduse has a discharge_temperature provided within the enduse statistics config file. The calculation when aggregating to household level is quite simple and essentially involves weighting the discharge_temperature based on the ratio of wastewater flow that enduse is contributing to the total wastewater flow at that given timestamp across all enduses.
The formula for temperature calculation is:
$$ T = \frac{(F_1 \cdot T_1) + (F_2 \cdot T_2)}{F_1 + F_2} $$
- $T$ is the calculated temperature.
- $F_1$ and $F_2$ are the flows for enduse1 and enduse2.
- $T_1$ and $T_2$ are the
discharge_temperaturefor enduse1 and enduse2.
temp_df = wq.hh_discharge_temperature(house.discharge, time_agg='h')
temp_df
| time | date | flow | discharge_temperature | |
|---|---|---|---|---|
| 0 | 2025-05-02 00:00:00 | 2025-05-02 | 8.654921 | 23.362067 |
| 1 | 2025-05-02 01:00:00 | 0 | 0.000000 | 0.000000 |
| 2 | 2025-05-02 02:00:00 | 0 | 0.000000 | 0.000000 |
| 3 | 2025-05-02 03:00:00 | 0 | 0.000000 | 0.000000 |
| 4 | 2025-05-02 04:00:00 | 0 | 0.000000 | 0.000000 |
| 5 | 2025-05-02 05:00:00 | 0 | 0.000000 | 0.000000 |
| 6 | 2025-05-02 06:00:00 | 0 | 0.000000 | 0.000000 |
| 7 | 2025-05-02 07:00:00 | 2025-05-02 | 9.192220 | 17.832711 |
| 8 | 2025-05-02 08:00:00 | 2025-05-02 | 11.155878 | 16.453997 |
| 9 | 2025-05-02 09:00:00 | 2025-05-02 | 46.390759 | 45.916159 |
| 10 | 2025-05-02 10:00:00 | 2025-05-02 | 16.868600 | 34.295567 |
| 11 | 2025-05-02 11:00:00 | 0 | 0.000000 | 0.000000 |
| 12 | 2025-05-02 12:00:00 | 0 | 0.000000 | 0.000000 |
| 13 | 2025-05-02 13:00:00 | 2025-05-02 | 7.200000 | 20.000000 |
| 14 | 2025-05-02 14:00:00 | 2025-05-02 | 14.400000 | 20.000000 |
| 15 | 2025-05-02 15:00:00 | 2025-05-02 | 30.323616 | 36.186812 |
| 16 | 2025-05-02 16:00:00 | 2025-05-02 | 38.309607 | 34.629598 |
| 17 | 2025-05-02 17:00:00 | 2025-05-02 | 34.532268 | 38.186868 |
| 18 | 2025-05-02 18:00:00 | 2025-05-02 | 19.500112 | 36.082311 |
| 19 | 2025-05-02 19:00:00 | 0 | 0.000000 | 0.000000 |
| 20 | 2025-05-02 20:00:00 | 0 | 0.000000 | 0.000000 |
| 21 | 2025-05-02 21:00:00 | 0 | 0.000000 | 0.000000 |
| 22 | 2025-05-02 22:00:00 | 0 | 0.000000 | 0.000000 |
| 23 | 2025-05-02 23:00:00 | 0 | 0.000000 | 0.000000 |