# A Cheaper Way to Test Ventilation Rates?

*Disclaimer: Please be safe guys.* *High concentrations of CO2 can be dangerous, so I didn’t work with anything more than 2000ppm in this post. This is also work in progress, and I am trying to improve it. Views subject to change. *

Naturally ventilated London flats might have bad air quality, which could damage our productivity. But to know if we have enough ventilation, we need to **measure**. So, I’m trying to make a cheaper and easier way to measure natural ventilation rates at home. In this post, I use baking soda and vinegar to make CO2 as a cheap tracer gas. Whilst it’s not professional standard, I think it does the job for basic measurements that I want. Let me explain.

Ventilation is measured with Air changes per hour (ACH). This is how often the air volume in a space is replaced with fresh air. ACH is one metric for air quality, alongside CO2, PM2.5 and PM10 concentrations. We like fresh air replacement because it flushes out bad stuff like viruses and CO2. [One guideline for ACH is the CDC’s recommendation of 5 air changes per hour.](https://www.cdc.gov/niosh/ventilation/prevention/Aim-for-5.html)

The usual way to calculate ACH is by knowing the airflow rate into a space. This is easy with mechanical ventilation because you **set** the airflow. However, a lot of flats in London are naturally ventilated, so calculating ACH is **hard**. My own flat is naturally ventilated, and I wanted to figure out a way to test its ACH.

One rough idea is to use a ‘*tracer gas*’. A tracer gas is a non toxic gas that you can measure the dispersion of, to test how fast air moves out.

The experiment goes like this. First, seal the room. Then, put a (non-dangerous) increased concentration of the tracer gas in it. Then, open the windows like how you would naturally. The tracer will then decay as the fresh air mixes in. Start measuring the concentration in real time, and you can estimate how much fresh air is coming inside and mixing with a time regression plot.

One widely available, potential tracer gas is CO2. Ideal levels of CO2 concentration in a space are less than 700ppm. So for this experiment, I decided to try and increase the concentration of CO2 in my living room to 1800ppm somehow, and then measure the decay.

But how can I get my room’s concentration to 1800ppm? This was the challenge. Dry ice is solid CO2, but ordering this is annoying and expensive. With delivery costs, I found dry ice to be around 50GBP for most suppliers for 5kg worth of pellets, which is usually the minimal ordering amount. 5kg is also way too much for what you need. You could also invite friends over and get some CO2 via respiration, but that’s not repeatable and pretty tedious as well.

But then I thought, why not use the oldest science fair experiment of all time?

**The baking soda volcano.**

[![](https://substackcdn.com/image/fetch/$s_!nf3b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F620b5f87-78bd-4b4e-9d2f-c060da482341_358x30.png)](https://substackcdn.com/image/fetch/$s_!nf3b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F620b5f87-78bd-4b4e-9d2f-c060da482341_358x30.png)

Baking soda and vinegar react to make CO2 as a by-product. After some chemistry and math, I estimated the amount of baking soda and vinegar needed to produce an extra 1000ppm for my living room (8.5m x 3.5 x 2.5m).

I realised that I needed:

*   5L of vinegar - you can get this as a big tub from home stores and,
*   400g baking soda - just go to the baking section at Tesco

**This costs 17 GBP in total and is much easier to get. I went to the shops and within 15 minutes I got what I needed.**

So, I closed the door, closed my window, put the baking soda and vinegar in my recycling bin, let it fizzle, and then measured the CO2 concentration. I used my Temtop m2000 that I got a couple of days ago.

[![](https://substackcdn.com/image/fetch/$s_!sRXo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aaa4bd-5932-4ff8-8a58-86a32f28e045_3024x1856.jpeg)](https://substackcdn.com/image/fetch/$s_!sRXo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd8aaa4bd-5932-4ff8-8a58-86a32f28e045_3024x1856.jpeg)

[![](https://substackcdn.com/image/fetch/$s_!Yb5u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd059c9f0-65a0-4221-9622-e2b9bdd6e9fc_828x783.jpeg)](https://substackcdn.com/image/fetch/$s_!Yb5u!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd059c9f0-65a0-4221-9622-e2b9bdd6e9fc_828x783.jpeg)

After a few minutes, I managed to get the concentration up to 1700ppm before it flatlined! You can see how it rises in the beginning as the baking soda and vinegar reaction add CO2 to the space.

[![](https://substackcdn.com/image/fetch/$s_!ol1C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7fc92f-3fb4-4e23-abdd-5d5e28406f69_3024x3415.jpeg)](https://substackcdn.com/image/fetch/$s_!ol1C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f7fc92f-3fb4-4e23-abdd-5d5e28406f69_3024x3415.jpeg)

Next, I opened the windows and then observed the decay pattern as the CO2 dispersed from the fresh air intake. This was around the 12min mark, and you can see it reduce in an exponential decay type pattern.

[![](https://substackcdn.com/image/fetch/$s_!E_eK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcf97e04-4899-4b08-8aff-d19646bfebaa_4032x3024.jpeg)](https://substackcdn.com/image/fetch/$s_!E_eK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdcf97e04-4899-4b08-8aff-d19646bfebaa_4032x3024.jpeg)

[![](https://substackcdn.com/image/fetch/$s_!TIwZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F175d4027-e3d3-4922-9504-75f75a57d40c_828x1159.jpeg)](https://substackcdn.com/image/fetch/$s_!TIwZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F175d4027-e3d3-4922-9504-75f75a57d40c_828x1159.jpeg)

In theory, we would expect CO2 to disperse in a pattern that looks like this, where C\_bg is the background CO2 concentration (around 400ppm) you would find outside. C\_0 is the initial CO2 concentration (in our case 1800ppm), C(t) is the current concentration, and n is the air changes per hour

[![](https://substackcdn.com/image/fetch/$s_!Yd4M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb6cdadd-8fc9-4c3b-8174-fc2fea04898b_844x64.png)](https://substackcdn.com/image/fetch/$s_!Yd4M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb6cdadd-8fc9-4c3b-8174-fc2fea04898b_844x64.png)

The m2000 records by the minute, and also allows export the data to csv via a usb connection. So I took the data, log transformed it, [and used a regression in python to get the coefficient ACH](https://www.aivc.org/sites/default/files/airbase_10530.pdf). With a background value set at 400, I got an ACH coefficient of 3.9 changes per hour. Not bad!

[![](https://substackcdn.com/image/fetch/$s_!OMmT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe66bc386-8b0d-47a7-a385-c794857eea85_733x979.png)](https://substackcdn.com/image/fetch/$s_!OMmT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe66bc386-8b0d-47a7-a385-c794857eea85_733x979.png)

An important point: the calculation is sensitive to the choice of ambient CO2 levels, and this varies between 400 and 500ppm when I have measured it outdoors. So, I am not taking this value of 3.9 ACH too seriously on its **absolute level.** Rather, I want to use this value as the **control value** for other experiments in which I try to increase my ACH.

For my next experiment, I want to see how much my fan is increases my ACH, and resolve a burning question that I had - should I point my fan inwards or outwards on my window for maximum airflow?!  
 

    def calculate_ach_ln_method(time_hours, co2_ppm, co2_baseline=400):
    	"""
    	Calculate ACH using the ln method from CO2 decay.
    	ACH = -ln((C(t) - C_baseline) / (C(0) - C_baseline)) / t
    	Where:
    	- C(t) is CO2 concentration at time t
    	- C_outdoor is outdoor CO2 concentration (typically 400 ppm)
    	- t is time in hours
    	"""
    	# Calculate normalized CO2 concentration
    	co2_normalized = (co2_ppm - co2_baseline) / (co2_ppm.iloc[0] - co2_baseline)
    	# Remove any negative or zero values (can't take ln)
    	valid_mask = co2_normalized > 0
    	time_valid = time_hours[valid_mask]
    	co2_norm_valid = co2_normalized[valid_mask]
    	if len(time_valid) < 2:
    	print("Warning: Not enough valid data points for ACH calculation")
    	return None, None
    	# Calculate ln of normalized concentration
    	ln_co2 = np.log(co2_norm_valid)
    	# Fit linear regression to ln(CO2) vs time
    	# ln(C(t)) = ln(C(0)) - ACH * t
    	# So ACH = -slope of ln(C(t)) vs t
    	coeffs = np.polyfit(time_valid, ln_co2, 1)
    	ach = -coeffs[0] # Negative of slope
    	# Calculate R-squared for goodness of fit
    	ln_co2_pred = np.polyval(coeffs, time_valid)
    	ss_res = np.sum((ln_co2 - ln_co2_pred) ** 2)
    	ss_tot = np.sum((ln_co2 - np.mean(ln_co2)) ** 2)
    	r_squared = 1 - (ss_res / ss_tot) if ss_tot != 0 else 0
    	return ach, r_squared, time_valid, ln_co2, ln_co2_pred