## TL;DR
TECHNICAL POAST!
In literature on far UVC efficacy, experimental chambers are controlled with ventilation. Ventilation effects, along with irradiance from Far UVC, are both variables that we need to take into account for its efficacy.
Given that my house is likely to have different ventilation conditions from experimental papers - how can I model pathogen decay with far UVC given my ventilation environment?
This essay tries to answer just the very basic question, given that I only have about 1 year's worth of fluid dynamics experience : **How do I get started modelling ventilation with my computer in the first place?** This is, like my last post, in line with the spirit of [Karnofsky, Minimal-trust investigations](https://www.cold-takes.com/minimal-trust-investigations/).
I'll cover
- The point of me writing a guide document liek this
- The general Navier-Stokes equations
- What reasonable assumptions there are, and how we modify the equations for those assumptions
- How to actually build the simulation
- OpenFOAM
- Construction of the environment
- How can I make it easier for others to learn and use?
### And some key results
From this really quick investigation, I've realised that due to some sensible assumptions, I've learnt some things about what I might want to take a closer look at!
- **Far UVC modellers and installers need to pay some attention to corners and walls - because non slip boundary conditions means that the velocity of air flow in proximity to a boundary is reduced, which means that: **
- **Analysis of Far UVC on surfaces feels more relevant**
---
## Tools I built for this, and what you will need for this tutorial
- [Casual Physics Enjoyer, stl-to-openfoam-mesh](https://github.com/casualPhysics/stl-to-openfoam-mesh) to build your mesh
- [Casual Physics Enjoyer, basic-ventilation-model](https://github.com/casualPhysics/basic-ventilation-model/tree/main/system) to do a simple laminar, incompressible flow model
---
## Background reading
### Explicit models
- [C.J. Noakes, L.A. Fletcher, C.B. Beggs, P.A. Sleigh, K.G. Kerr, Journal of Aerosol Science, Volume 35, Issue 4, 2004](https://www.sciencedirect.com/science/article/abs/pii/S0021850203004488)
- This is the main paper that I'll be looking to get some sensible assumptions, and a reasonable base model
- [Yue Pan, Tongling Xia, Kangqi Guo, Yuting An, Chun Chen, Predicting spatial distribution of ultraviolet irradiance and disinfection of exhaled bioaerosols with a modified irradiance model, Building and Environment, Volume 228, 2023,](https://www.sciencedirect.com/science/article/abs/pii/S0360132322010228)
- A more recent modelling attempt that I would consider 'the target' in terms of understanding , and being able to replicate
### My Previous Writing on this Topic
- [Casual Physics Enjoyer, Calculations for Practical Heuristics in Far UVC Use](https://chillphysicsenjoyer.substack.com/p/calculations-for-practical-heuristics)
- [Casual Physics Enjoyer, Problems Trying to Model Far UVC in a Room](https://substack.com/@casualphysicsenjoyer/p-162605871)
- [Casual Physics Enjoyer, How do I achieve what I want?](https://chillphysicsenjoyer.substack.com/p/how-do-i-achieve-what-i-want)
### Does Far UVC work?
- [Blueprint Biosecurity, Blueprint for Far UVC](https://blueprintbiosecurity.org/u/2025/03/Blueprint-for-Far-UVC-PREPRINTv1.0.pdf)
- [Eadie, E., Hiwar, W., Fletcher, L. et al. Far-UVC (222 nm) efficiently inactivates an airborne pathogen in a room-sized chamber. Sci Rep 12, 4373 (2022).](https://www.nature.com/articles/s41598-022-08462-z)
### Fluid dynamics modelling
- [Versteeg, H., Malalasekera, W - An Introduction to Computational Fluid Dynamics](https://www.amazon.co.uk/Introduction-Computational-Fluid-Dynamics-Approach/dp/0582218845)
- I think that this is the most relevant book for the questions that we are trying to answer here in terms of the hard core fluid dynamics modelling.
- [12 Steps to Navier Stokes](https://nbviewer.org/github/barbagroup/CFDPython/blob/master/lessons/15_Step_12.ipynb)
- A ground up guide in modelling Navier-Stokes in 2 dimensions.
- It scratches an itch of building a toy solver in python that is readable.
- I stared at these Jupyter notebooks for a while trying to get some intuition.
- [[Magnussen, Airflow Equations1]]
- A cool model that I used as a reference at times.
---
## Things that I am trying to solve by writing this
### Pedagogy on CFD
*Problems:*
- CFD is hard to learn, and probably not accessible in any meaningful way to ground-level installers
- And so tools for ways to build easy, practical heuristics are probably are going to be important - see [Casual Physics Enjoyer, Problems Trying to Model Far UVC in a Room](https://substack.com/@casualphysicsenjoyer/p-162605871), [Blueprint Biosecurity, Blueprint for Far UVC](https://blueprintbiosecurity.org/u/2025/03/Blueprint-for-Far-UVC-PREPRINTv1.0.pdf)
- A lot of CFD tutorials are on YouTube, with all the problems that come with learning from video - like limited search functionality, going back to key points etc.
- OpenFOAM tutorials don't seem to match the standard of clarity and pedagogy in the python machine learning
- It feels like modelling ventilation in your own room should be 'easy' - just in terms of general interest purposes, and personal projects - just like how building simple machine learning models also feel easy.
*So why do I think writing a specific guide helps?*
- Well, it was painful for me to figure this stuff out, so at least 1 person (future me) might find it useful.
- Writing tutorials out specifically, and explicitly as possible in text so that it is fully replicable, feels like a solid end-state. The jump from theory to practice is hard, but seeing it once helps a lot to make that leap
- I learnt computational chemistry with the University of Nottingham a lot quicker when a researcher in the University of Nottingham sent me a super clear, crisp, step by step walkthrough of assumptions and code, one by one
### Pedagogy on fluid dynamics
- Fluid dynamics is tricky to me mainly because governing equations depend on assumptions, and its really easy to get mixed up on which is which.
- So I'm making this document to clarify my thinking, and also help others who are technical but not necessary in the weeds of fluid math.
- I thought it would be useful to explicitly list assumptions, parameters used.
- In my last post, ventilation wasn't really considered
- I thought it would be useful to familiarise myself with the Navier-Stokes equations in general.
- I wanted to model the ventilation in my room just generally.
- I wanted to familiarise myself with how far UVC might be sensitive to different ventilation regimes.
- I also wanted to figure out first principles how to toy model Navier stokes for just airflow
### As a minimal trust investigation
- In addition, there are the same points that I wanted to get out of minimal trust investigations, which I wrote about in the beginning of [Casual Physics Enjoyer, Calculations for Practical Heuristics in Far UVC Use](https://chillphysicsenjoyer.substack.com/p/calculations-for-practical-heuristics), and was originally mentioned in [Karnofsky, Minimal-trust investigations](https://www.cold-takes.com/minimal-trust-investigations/)
- Specific to ventilation modelling, I wanted to learn more about
- **What are the actual equations that we need to use?**
- I had trouble finding an explicit, reasonable presentation that goes step by step
- When incompressible vs compressible flow assumptions need to be used
- What temperature assumptions needed to be taken into account
- When turbulence assumptions need to be taken into account
- Do we actually really need to rely on people using complicated closed source CFD software or can we model this in python?
- How far might experiments deviate
---
## The General Navier-Stokes Equations
The most general model for a fluid is the Navier-Stokes equations. Given a point in space and time, they aim to model quantities associated with that fluid, given a set of conditions on the boundaries at t = 0. It's easy to get mixed up on which equations relate to which assumptions, so I've started writing them in the most 'general form'.
See [Versteeg, H., Malalasekera, W - An Introduction to Computational Fluid Dynamics](https://www.amazon.co.uk/Introduction-Computational-Fluid-Dynamics-Approach/dp/0582218845) for a more comprehensive treatment.
**Here is what the equations aim to model**
- Density (scalar) - rho
- Pressure (scalar) - p
- Velocity (vector) - **u**
- Momentum (vector) - u, v, w
- Energy (scalar) - i
- *Any other scalar quantity, which in our case would be pathogen concentration.*
**Here are the equations in their most general form, with minimal assumptions**
*The mass equation:*
- rho represents the density
- **u** represents the velocity field
$
\frac{\partial \rho}{\partial t} + \nabla \cdot (\rho \mathbf{u}) = 0
$
The momentum equations:
- The S variables represent a source term
- mu represents viscosity
$
\begin{align}
\text{x-momentum:} \quad
& \frac{\partial (\rho u)}{\partial t} + \nabla \cdot (\rho u \mathbf{u}) = -\frac{\partial p}{\partial x} + \nabla \cdot \left( \mu \nabla u \right) + S_{Mx} \\[1.5ex]
\text{y-momentum:} \quad
& \frac{\partial (\rho v)}{\partial t} + \nabla \cdot (\rho v \mathbf{u}) = -\frac{\partial p}{\partial y} + \nabla \cdot \left( \mu \nabla v \right) + S_{My} \\[1.5ex]
\text{z-momentum:} \quad
& \frac{\partial (\rho w)}{\partial t} + \nabla \cdot (\rho w \mathbf{u}) = -\frac{\partial p}{\partial z} + \nabla \cdot \left( \mu \nabla w \right) + S_{Mz}
\end{align}
$
The energy equations and state equations are
$
\begin{align}
\text{Internal Energy:} \quad
& \frac{\partial (\rho i)}{\partial t} + \nabla \cdot (\rho i \mathbf{u}) = -p \nabla \cdot \mathbf{u} + \nabla \cdot (k \nabla T) + S
\end{align}
$
$
\begin{align}
\text{Equations of State:} \quad
& p = p(\rho, T) \\
& i = i(\rho, T)
\end{align}
$
$
\begin{align}
\text{Perfect Gas:} \quad
& p = \rho RT \\
& i = C_v T
\end{align}
$
Where:
- i is the internal energy per unit mass
- ρ is the density
- p is the pressure
- T is the temperature
- k is the thermal conductivity
- S is the source term
- R is the gas constant
- Cv is the specific heat at constant volume
The equations above can also be written in the general transport equation form below
- The pressure gradient terms in the momentum equation are absorbed into the source term
$
\frac{\partial (\rho \phi)}{\partial t} + \nabla \cdot (\rho \phi \mathbf{u}) = \nabla \cdot \left( \Gamma \nabla \phi \right) + S_\phi
$
---
## Assumptions for a simple ventilation model
### Assumptions in an early paper
Let's take a look at the following paper to model ventilation in a chamber, and then critique some of the assumptions / see when they might be valid
- [C.J. Noakes, L.A. Fletcher, C.B. Beggs, P.A. Sleigh, K.G. Kerr, Journal of Aerosol Science, Volume 35, Issue 4, 2004](https://www.sciencedirect.com/science/article/abs/pii/S0021850203004488)?
I'll then use these assumptions as a starting point for modelling things myself.
#### Assumptions on equations and parameters
Ok so we need to be quite careful here
- The assumption of **incompressibility** - which means that div **u** = 0
- This assumption feels somewhat justified, and is cited in [Versteeg, H., Malalasekera, W - An Introduction to Computational Fluid Dynamics](https://www.amazon.co.uk/Introduction-Computational-Fluid-Dynamics-Approach/dp/0582218845), where slow moving gases can be modelled as such.
- That being said, taking away this assumption is likely to affect big changes on the results so it is worth checking this quite closely.
- They use constant **temperature**
- This assumption means we don't have buoyancy effects, which would probably be too strong of an assumption in the case where we are modelling natural ventilation where temperature variations matter
- ***In the case of constant temperature, equations of state imply that energy is no longer an equation that we need to model, which I will elaborate on below***
- They use constant **density**
- This implies that the fluid is incompressible, as said above.
- They use constant **viscosity**
- This makes sense given that the fluid is Newtonian.
- They use the following scalar transport equation
- The equation is
$
\frac{\partial \phi}{\partial t} + \nabla \cdot \left[ \mathbf{u} \phi \right] = \nabla \cdot \left( D \nabla \phi \right) + S_\phi
$
- In comparison, the transport equation used in [Yue Pan, Tongling Xia, Kangqi Guo, Yuting An, Chun Chen, Predicting spatial distribution of ultraviolet irradiance and disinfection of exhaled bioaerosols with a modified irradiance model, Building and Environment, Volume 228, 2023,](https://www.sciencedirect.com/science/article/abs/pii/S0360132322010228) is the same, but includes a turbulence function as well
- The equation is below
$
\frac{\partial \phi}{\partial t} + \nabla \cdot \left[ (\mathbf{u} + \mathbf{v}_s) \phi \right] = \nabla \cdot \left( (D + \varepsilon_p) \nabla \phi \right) + S_\phi
$
where:
- phi is a scalar quantity (e.g., temperature, concentration)
- u = fluid velocity vector
- v_s = settling velocity vector (if applicable)
- D = molecular diffusivity
- \( epsilon_p \) = turbulent diffusivity (if applicable)
- \( S_\phi \) = source term
#### Suspected constants For Air at Standard Conditions (20°C, 1 atm):
- Density
- ρ≈1.204 kg/m³
- Dynamic Viscosity:
- μ≈1.81×10^−5 kg/(m·s)
#### So what do the equations reduce to?
Based on these assumptions, the governing equations for a ventilation model basically just become the equations governing incompressible flow:
**Mass Conservation (Continuity):**
- Incompressible flow
$
\nabla \cdot\mathbf{u} = 0
$
**Momentum Equations (Navier-Stokes):**
- Constant density ($\rho$)
- Constant viscosity ($\mu$)
- No buoyancy effects (constant temperature)
- We also can just divide both sides by density to use kinematic viscosity instead
$
\begin{align}
\text{Mass Conservation:} \quad
& \nabla \cdot \mathbf{u} = 0 \\[1.5ex]
\text{x-momentum:} \quad
& \rho \left( \frac{\partial u}{\partial t} + \mathbf{u} \cdot \nabla u \right) = -\frac{\partial p}{\partial x} + \mu \nabla^2 u \\[1.5ex]
\text{y-momentum:} \quad
& \rho \left( \frac{\partial v}{\partial t} + \mathbf{u} \cdot \nabla v \right) = -\frac{\partial p}{\partial y} + \mu \nabla^2 v \\[1.5ex]
\text{z-momentum:} \quad
& \rho \left( \frac{\partial w}{\partial t} + \mathbf{u} \cdot \nabla w \right) = -\frac{\partial p}{\partial z} + \mu \nabla^2 w
\end{align}
$
**Scalar Transport Equation:**
- We derive this from the transport equation above given that density is constant - I'll derive this in the appendix below.
- For a passive scalar (like concentration)
- No pressure effects
- Constant diffusivity
$
\begin{align}
\text{Scalar Transport:} \quad
& \frac{\partial C}{\partial t} + \nabla \cdot (C\mathbf{u}) = D\nabla^2 C + S
\end{align}
$
Where:
- $\mathbf{u} = (u,v,w)$ is the velocity vector
- $p$ is the pressure
- $\rho$ is the constant density
- $\mu$ is the constant viscosity
- $C$ is the scalar concentration
- $D$ is the diffusivity
- $S$ is the source term
#### More points about these assumptions:
The incompressibility assumption is valid for:
- Low Mach number flows < 0.3
- Slow-moving gases
- No significant temperature variations
Constant temperature assumption means:
- No buoyancy effects
- No need for energy equation
- Density remains constant
Constant viscosity assumption:
- Valid for Newtonian fluids
- No temperature dependence
- No pressure dependence
The scalar transport equation:
- Assumes the scalar doesn't affect the flow field
- No pressure effects on transport
- Constant diffusivity
#### What are reasonable boundary conditions?
In the paper above, in the case of ventilation modelling, there is a chamber with an inlet and an outlet.
What do I think are reasonable boundary conditions on the airflow? Well, inspired by the paper above, for just ventilation on its own, I thought that these were reasonable assumptions for a ventilation model
- Kinematic viscosity
- The kinematic velocity for air
- Pressure
- On the inlet
- Zero gradient
- On the outlet
- Static pressure
- On the walls
- Zero gradient
- Velocity
- On the inlet
- A fixed vector for the air direction
- On the outlet
- Zero gradient
- Supposedly this is the usual boundary condition for ventilation models
- On the walls
- A no slip condition on the wall
- Scalar transport
---
## Ok cool - so how can I get started modelling this?
I wanted to start easy by modelling a cuboid, where one side is an inlet, and the other side is an outlet. The boundary conditions will be the same ones that I've listed above. I'm still starting out so any feedback would be appreciated!
I used
- Blender
- OpenFoam
- Paraview
### Making the geometries
- I first made my shape in blender - just a simple cuboid for now to start out.
### Separating the surfaces
- Then I select the surfaces which have distinct boundary conditions and then 'separate' those and label them. In this case, I called them 'inlet', 'outlet' and 'walls', each one for a different boundary condition. The left face I called the inlet, and the face opposite that I called the outlet. The rest is labelled as walls.
![[Screenshot 2025-05-16 at 16.54.31.png]]
### Exporting them as STLs
- In blender, export these as separate stl files and put them all into a folder ready to mesh them.
```
geometry/
└── your_model/
├── part1.stl
├── part2.stl
└── ...
```
### Parsing them ready for meshing and meshing them
- Now we need to parse the stl files for meshing
- I made a tool for this for basic meshes just to start. So just place your geometries in the required directory, git clone the tool below and then run this to get a mesh!
https://github.com/casualPhysics/stl-to-openfoam-mesh
![[Screenshot 2025-05-17 at 20.23.43.png]]
As an output, you should see three subdirectories in the constant/ directory:
- triSurface
- polyMesh
- extendedFeatureEdgeMesh
Keep those and use them for the next step.
### Running this in OpenFOAM to do a basic incompressible flow simulation
Now if you go to [Casual Physics Enjoyer, basic-ventilation-model](https://github.com/casualPhysics/basic-ventilation-model/tree/main/system), you'll see the structure that we need to run an openFOAM simulation. I suggest that you go and try it out yourself!
- the constant subdir contains the outputs of the mesh routine in the above step
- the system subdir contains files that control the parameters of your simulation
- the 0 subdir contains initial conditions.
```
ventilation_model
├── system
│ ├── fvSchemes
│ ├── fvSolution
│ ├── foamDataToFluentDict
│ └── controlDict
├── constant
│ ├── transportProperties
│ ├── triSurface
│ ├── polyMesh
│ └── extendedFeatureEdgeMesh
└── 0
├── U
└── p
```
#### Initial conditions
Let's take a look at what the initial conditions look like
- We have a separate file for both the velocity (U) and the pressure (p) initial conditions.
- The files consist of dictionaries for each separate region that you made the stl files for!
- In the section above - I outline the initial conditions that we need
Here's are what the pressure field looks like:
- each stl file has its dedicated boundary condition
- the outlet has static pressure, which is one of the assumptions that we talked about earlier
- the dimensions below denote that the units of pressure are in m^2 s ^(-2). You might notice that these aren't the units of pressure, but rather pressure / density. This is because we have divided by constant density above, and so the units of 'effective' pressure is what we are trying to solve for.
```
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2412 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object p;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 2 -2 0 0 0 0];
internalField uniform 0;
boundaryField
{
basic_boxInlet
{
type zeroGradient;
}
basic_boxWalls
{
type zeroGradient;
}
basic_boxOutlet
{
type fixedValue;
value uniform 0;
}
}
// ************************************************************************* //
```
- the velocity boundary conditions simulate the veolc
#### The control dict
The controlDict file controls which kind of solver we want to use. In this case, we use the icoSolver, which is for incompressible, laminar flows.
- We also specify the time stamps that we are doing the simulation for
```
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2412 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object controlDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
application icoFoam;
startFrom latestTime;
startTime 0;
stopAt endTime;
endTime 75;
deltaT 0.05;
writeControl timeStep;
writeInterval 20;
purgeWrite 0;
writeFormat ascii;
writePrecision 6;
writeCompression off;
timeFormat general;
timePrecision 6;
runTimeModifiable true;
// ************************************************************************* //
```
#### Running the simulation
In the directory, use the icoFoam command to run the simulation in openFOAM and you should then have a generated subdirectories that represent the state of the geometries for each timestep. You can these use paraview to visualise the results, which I'll look at in a future post.
In this case, the output of the simulation of the **velocity** field of the mesh looks like this -
![[Screenshot 2025-05-18 at 20.40.21.png]]
And then the pressure gradient looks like this:
![[Screenshot 2025-05-18 at 20.41.12.png]]
#### Other files
We also have these other files, which since I'm new, will try to explain in a next post.
│ ├── fvSchemes
│ ├── fvSolution
│ ├── foamDataToFluentDict