@@ -46,8 +46,9 @@ const defaultPlumeConfig: PlumeConfig = {
4646export interface Parcel {
4747 z : number ; // Height levels [m]
4848 w : number ; // Vertical velocity [m/s]
49+ thetal : number ; // Liquid-water potential temperature (conserved) [K]
4950 theta : number ; // Potential temperature [K]
50- qt : number ; // Total specific humidity [kg/kg]
51+ qt : number ; // Total specific humidity (conserved) [kg/kg]
5152 thetav : number ; // Virtual potential temperature [K]
5253 qsat : number ; // Saturation specific humidity [kg/kg]
5354 b : number ; // Buoyancy [m/s²]
@@ -71,7 +72,7 @@ function initializeFireParcel(
7172) : Parcel {
7273 // Start with parcel props from ambient air
7374 const z = background . z [ 0 ] ;
74- let theta = background . theta [ 0 ] ;
75+ let thetal = background . theta [ 0 ] ; // CLASS assumes RH_near-surf < 100%
7576 const thetavAmbient = background . thetav [ 0 ] ;
7677 let qt = background . qt [ 0 ] ;
7778 const rho = background . rho [ 0 ] ;
@@ -83,24 +84,25 @@ function initializeFireParcel(
8384 const FFire =
8485 ( ( fire . omega * fire . C * fire . spread ) / fire . d ) * ( 1 - fire . radiativeLoss ) ;
8586 const FqFire = ( fire . omega * fire . Cq * fire . spread ) / fire . d ;
86- const FvFire = FFire * ( 1 + 0.61 * theta * FqFire ) ;
87+ const FvFire = FFire * ( 1 + 0.61 * thetal * FqFire ) ;
8788
8889 // Use cube root as the root may be negative and js will yield NaN for a complex number result
8990 const fac_w =
9091 ( 3 * g * plumeConfig . aW * FvFire ) /
9192 ( 2 * rho * cp * thetavAmbient * ( 1 + plumeConfig . bW ) ) ;
9293 const w = Math . cbrt ( fac_w * fire . h0 ) ;
9394
94- // Add excess temperature/humidity and update thetav/qsat accordingly
95+ // Add excess temperature/humidity
9596 const dtheta = FFire / ( rho * cp * w ) ;
9697 const dqv = FqFire / ( rho * w ) ;
97- theta += dtheta ;
98+ thetal += dtheta ;
9899 qt += dqv ;
99100
100- // Thermodynamics
101- const T = saturationAdjustment ( theta , qt , p , exner ) ;
101+ // Update thetav/qsat accordingly
102+ const T = saturationAdjustment ( thetal , qt , p , exner ) ;
102103 const qsat = qsatLiq ( p , T ) ;
103104 const ql = Math . max ( qt - qsat , 0 ) ;
105+ const theta = thetal + ( Lv / cp / exner ) * ql ;
104106 const thetav = virtualTemperature ( theta , qt , ql ) ;
105107 const rh = ( ( qt - ql ) / qsat ) * 100 ;
106108 const Td = dewpoint ( qt , p / 100 ) ;
@@ -118,6 +120,7 @@ function initializeFireParcel(
118120 return {
119121 z,
120122 w,
123+ thetal,
121124 theta,
122125 qt,
123126 thetav,
@@ -156,13 +159,15 @@ export function calculatePlume(
156159 // Mass flux through plume
157160 const m = parcel . m + ( parcel . e - parcel . d ) * dz ;
158161 const emz = ( parcel . e / parcel . m ) * dz ;
159- const theta = parcel . theta - emz * ( parcel . theta - bg . theta [ i - 1 ] ) ;
162+ // FIXME: Line below assumes background state is unsaturated
163+ const thetal = parcel . thetal - emz * ( parcel . thetal - bg . theta [ i - 1 ] ) ;
160164 const qt = parcel . qt - emz * ( parcel . qt - bg . qt [ i - 1 ] ) ;
161165
162166 // Thermodynamics and buoyancy
163- const T = saturationAdjustment ( theta , qt , bg . p [ i ] , bg . exner [ i ] ) ;
167+ const T = saturationAdjustment ( thetal , qt , bg . p [ i ] , bg . exner [ i ] ) ;
164168 const qsat = qsatLiq ( bg . p [ i ] , T ) ;
165169 const ql = Math . max ( qt - qsat , 0 ) ;
170+ const theta = thetal + ( Lv / cp / bg . exner [ i ] ) * ql ;
166171 const thetav = virtualTemperature ( theta , qt , ql ) ;
167172 const rh = ( ( qt - ql ) / qsat ) * 100 ;
168173 const Td = dewpoint ( qt , bg . p [ i ] / 100 ) ;
@@ -191,6 +196,7 @@ export function calculatePlume(
191196 parcel = {
192197 z,
193198 w,
199+ thetal,
194200 theta,
195201 qt,
196202 thetav,
0 commit comments