Skip to content

Commit cf75d07

Browse files
Copilotoyve
andcommitted
Simplify to use only linear snowToLiquidRatio function
- Removed step-wise snowToLiquidRatio() in favor of linear interpolation - Removed snowToLiquidRatioContinuous() and snowfallEquivalentContinuous() (now redundant) - Updated snowToLiquidRatio() to use linear interpolation (5:1 at 0°C to 30:1 at -20°C) - Updated all tests to work with continuous ratios while maintaining coverage - All 146 tests passing (15 snow-related tests) Co-authored-by: oyve <16225018+oyve@users.noreply.github.com>
1 parent 6e899d8 commit cf75d07

File tree

2 files changed

+70
-164
lines changed

2 files changed

+70
-164
lines changed

src/phenomena/snow.ts

Lines changed: 7 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,12 @@
11
/**
22
* Calculate the snow-to-liquid ratio based on temperature.
33
* The ratio varies with temperature, with colder temperatures producing fluffier, less dense snow.
4-
* Based on empirical relationships from meteorological studies.
4+
* Uses linear interpolation for smooth transition between temperature regimes.
55
*
66
* @param {number} temperature - Air temperature in Kelvin
77
* @returns {number} Snow-to-liquid ratio (dimensionless)
88
*/
99
export function snowToLiquidRatio(temperature: number): number {
10-
// Convert to Celsius for easier calculation
11-
const tempC = temperature - 273.15;
12-
13-
// Based on empirical data:
14-
// - Very warm (near 0°C): ~5:1 (wet, heavy snow)
15-
// - Cold (-5 to -10°C): ~15:1 (typical snow)
16-
// - Very cold (< -15°C): ~20-30:1 (dry, fluffy snow)
17-
18-
if (tempC >= 0) {
19-
// Above freezing - minimal snow, mostly sleet/rain
20-
return 5;
21-
} else if (tempC >= -5) {
22-
// Wet snow
23-
return 10;
24-
} else if (tempC >= -10) {
25-
// Typical snow
26-
return 15;
27-
} else if (tempC >= -15) {
28-
// Dry snow
29-
return 20;
30-
} else {
31-
// Very dry, fluffy snow
32-
return 30;
33-
}
34-
}
35-
36-
/**
37-
* Calculate snow depth from liquid precipitation based on temperature.
38-
* Converts liquid precipitation (e.g., rainfall equivalent) to estimated snow depth.
39-
*
40-
* @param {number} liquidPrecipitation - Liquid precipitation depth in millimeters
41-
* @param {number} temperature - Air temperature in Kelvin
42-
* @returns {number} Estimated snow depth in millimeters
43-
*/
44-
export function snowfallEquivalent(liquidPrecipitation: number, temperature: number): number {
45-
const ratio = snowToLiquidRatio(temperature);
46-
return liquidPrecipitation * ratio;
47-
}
48-
49-
/**
50-
* Calculate snow-to-liquid ratio using a continuous formula (Roebber et al. 2003).
51-
* This provides a more gradual transition between temperature regimes.
52-
*
53-
* @param {number} temperature - Air temperature in Kelvin
54-
* @returns {number} Snow-to-liquid ratio (dimensionless)
55-
*/
56-
export function snowToLiquidRatioContinuous(temperature: number): number {
5710
// Convert to Celsius
5811
const tempC = temperature - 273.15;
5912

@@ -75,18 +28,20 @@ export function snowToLiquidRatioContinuous(temperature: number): number {
7528
}
7629

7730
/**
78-
* Calculate snow depth from liquid precipitation using continuous ratio.
79-
* Provides a smoother estimate across temperature ranges.
31+
* Calculate snow depth from liquid precipitation based on temperature.
32+
* Converts liquid precipitation (e.g., rainfall equivalent) to estimated snow depth.
8033
*
8134
* @param {number} liquidPrecipitation - Liquid precipitation depth in millimeters
8235
* @param {number} temperature - Air temperature in Kelvin
8336
* @returns {number} Estimated snow depth in millimeters
8437
*/
85-
export function snowfallEquivalentContinuous(liquidPrecipitation: number, temperature: number): number {
86-
const ratio = snowToLiquidRatioContinuous(temperature);
38+
export function snowfallEquivalent(liquidPrecipitation: number, temperature: number): number {
39+
const ratio = snowToLiquidRatio(temperature);
8740
return liquidPrecipitation * ratio;
8841
}
8942

43+
44+
9045
/**
9146
* Calculate liquid precipitation from snow depth (reverse calculation).
9247
* Useful for estimating water content of snowfall.

tests/phenomena/snow.test.ts

Lines changed: 63 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import {
22
snowToLiquidRatio,
33
snowfallEquivalent,
4-
snowToLiquidRatioContinuous,
5-
snowfallEquivalentContinuous,
64
snowToLiquidEquivalent
75
} from '../../src/phenomena/snow';
86

@@ -15,40 +13,34 @@ describe('snowToLiquidRatio', () => {
1513
expect(snowToLiquidRatio(280)).toBe(5);
1614
});
1715

18-
it('returns 10 for wet snow temperatures (-5°C to 0°C)', () => {
19-
// -1°C
20-
expect(snowToLiquidRatio(272.15)).toBe(10);
21-
// -3°C
22-
expect(snowToLiquidRatio(270.15)).toBe(10);
23-
// -5°C
24-
expect(snowToLiquidRatio(268.15)).toBe(10);
25-
});
26-
27-
it('returns 15 for typical snow temperatures (-10°C to -5°C)', () => {
28-
// -6°C
29-
expect(snowToLiquidRatio(267.15)).toBe(15);
30-
// -8°C
31-
expect(snowToLiquidRatio(265.15)).toBe(15);
32-
// -10°C
33-
expect(snowToLiquidRatio(263.15)).toBe(15);
16+
it('returns 30 for very cold temperatures', () => {
17+
expect(snowToLiquidRatio(253.15)).toBeCloseTo(30, 2); // -20°C exactly
18+
expect(snowToLiquidRatio(243.15)).toBe(30); // -30°C
3419
});
3520

36-
it('returns 20 for dry snow temperatures (-15°C to -10°C)', () => {
37-
// -11°C
38-
expect(snowToLiquidRatio(262.15)).toBe(20);
39-
// -13°C
40-
expect(snowToLiquidRatio(260.15)).toBe(20);
41-
// -15°C
42-
expect(snowToLiquidRatio(258.15)).toBe(20);
21+
it('provides continuous values in the transition range', () => {
22+
// -2°C should be 7.5
23+
const ratio1 = snowToLiquidRatio(271.15);
24+
expect(ratio1).toBeCloseTo(7.5, 1);
25+
26+
// -10°C should be 17.5
27+
const ratio2 = snowToLiquidRatio(263.15);
28+
expect(ratio2).toBeCloseTo(17.5, 1);
29+
30+
// -20°C boundary (exactly 30, with floating point tolerance)
31+
const ratio3 = snowToLiquidRatio(253.15);
32+
expect(ratio3).toBeCloseTo(30, 2);
4333
});
4434

45-
it('returns 30 for very cold temperatures (below -15°C)', () => {
46-
// -16°C
47-
expect(snowToLiquidRatio(257.15)).toBe(30);
48-
// -20°C
49-
expect(snowToLiquidRatio(253.15)).toBe(30);
50-
// -30°C
51-
expect(snowToLiquidRatio(243.15)).toBe(30);
35+
it('increases ratio as temperature decreases', () => {
36+
const ratio1 = snowToLiquidRatio(271.15); // -2°C
37+
const ratio2 = snowToLiquidRatio(268.15); // -5°C
38+
const ratio3 = snowToLiquidRatio(263.15); // -10°C
39+
const ratio4 = snowToLiquidRatio(258.15); // -15°C
40+
41+
expect(ratio2).toBeGreaterThan(ratio1);
42+
expect(ratio3).toBeGreaterThan(ratio2);
43+
expect(ratio4).toBeGreaterThan(ratio3);
5244
});
5345
});
5446

@@ -57,17 +49,21 @@ describe('snowfallEquivalent', () => {
5749
// 10mm liquid at 0°C (ratio 5:1) = 50mm snow
5850
expect(snowfallEquivalent(10, 273.15)).toBe(50);
5951

60-
// 10mm liquid at -3°C (ratio 10:1) = 100mm snow
61-
expect(snowfallEquivalent(10, 270.15)).toBe(100);
52+
// 10mm liquid at -3°C (ratio ~8.75:1)
53+
const snow1 = snowfallEquivalent(10, 270.15);
54+
expect(snow1).toBeCloseTo(87.5, 1);
6255

63-
// 10mm liquid at -8°C (ratio 15:1) = 150mm snow
64-
expect(snowfallEquivalent(10, 265.15)).toBe(150);
56+
// 10mm liquid at -8°C (ratio ~15:1)
57+
const snow2 = snowfallEquivalent(10, 265.15);
58+
expect(snow2).toBeCloseTo(150, 1);
6559

66-
// 10mm liquid at -12°C (ratio 20:1) = 200mm snow
67-
expect(snowfallEquivalent(10, 261.15)).toBe(200);
60+
// 10mm liquid at -12°C (ratio ~20:1)
61+
const snow3 = snowfallEquivalent(10, 261.15);
62+
expect(snow3).toBeCloseTo(200, 1);
6863

6964
// 10mm liquid at -20°C (ratio 30:1) = 300mm snow
70-
expect(snowfallEquivalent(10, 253.15)).toBe(300);
65+
const snow4 = snowfallEquivalent(10, 253.15);
66+
expect(snow4).toBeCloseTo(300, 1);
7167
});
7268

7369
it('returns 0 for 0mm liquid precipitation', () => {
@@ -76,92 +72,45 @@ describe('snowfallEquivalent', () => {
7672
});
7773

7874
it('handles small amounts of precipitation', () => {
79-
// 1mm liquid at -10°C (ratio 15:1) = 15mm snow
80-
expect(snowfallEquivalent(1, 263.15)).toBe(15);
75+
// 1mm liquid at -10°C (ratio ~17.5:1)
76+
const snow1 = snowfallEquivalent(1, 263.15);
77+
expect(snow1).toBeCloseTo(17.5, 1);
8178

82-
// 0.5mm liquid at -8°C (ratio 15:1) = 7.5mm snow
83-
expect(snowfallEquivalent(0.5, 265.15)).toBe(7.5);
79+
// 0.5mm liquid at -8°C (ratio ~15:1)
80+
const snow2 = snowfallEquivalent(0.5, 265.15);
81+
expect(snow2).toBeCloseTo(7.5, 1);
8482
});
8583

8684
it('handles large amounts of precipitation', () => {
87-
// 100mm liquid at -10°C (ratio 15:1) = 1500mm snow
88-
expect(snowfallEquivalent(100, 263.15)).toBe(1500);
89-
});
90-
});
91-
92-
describe('snowToLiquidRatioContinuous', () => {
93-
it('returns 5 for temperatures at or above freezing', () => {
94-
expect(snowToLiquidRatioContinuous(273.15)).toBe(5);
95-
expect(snowToLiquidRatioContinuous(275)).toBe(5);
96-
});
97-
98-
it('returns 30 for very cold temperatures', () => {
99-
expect(snowToLiquidRatioContinuous(253.15)).toBeCloseTo(30, 2); // -20°C exactly
100-
expect(snowToLiquidRatioContinuous(243.15)).toBe(30); // -30°C
101-
});
102-
103-
it('provides continuous values in the transition range', () => {
104-
// -2°C should be 7.5
105-
const ratio1 = snowToLiquidRatioContinuous(271.15);
106-
expect(ratio1).toBeCloseTo(7.5, 1);
107-
108-
// -10°C should be 17.5
109-
const ratio2 = snowToLiquidRatioContinuous(263.15);
110-
expect(ratio2).toBeCloseTo(17.5, 1);
111-
112-
// -20°C boundary (exactly 30, with floating point tolerance)
113-
const ratio3 = snowToLiquidRatioContinuous(253.15);
114-
expect(ratio3).toBeCloseTo(30, 2);
85+
// 100mm liquid at -10°C (ratio ~17.5:1)
86+
const snow = snowfallEquivalent(100, 263.15);
87+
expect(snow).toBeCloseTo(1750, 1);
11588
});
116-
117-
it('increases ratio as temperature decreases', () => {
118-
const ratio1 = snowToLiquidRatioContinuous(271.15); // -2°C
119-
const ratio2 = snowToLiquidRatioContinuous(268.15); // -5°C
120-
const ratio3 = snowToLiquidRatioContinuous(263.15); // -10°C
121-
const ratio4 = snowToLiquidRatioContinuous(258.15); // -15°C
122-
123-
expect(ratio2).toBeGreaterThan(ratio1);
124-
expect(ratio3).toBeGreaterThan(ratio2);
125-
expect(ratio4).toBeGreaterThan(ratio3);
126-
});
127-
});
128-
129-
describe('snowfallEquivalentContinuous', () => {
130-
it('calculates snow depth using continuous ratio', () => {
131-
// Test at various temperatures
132-
const snow1 = snowfallEquivalentContinuous(10, 273.15); // 0°C
133-
expect(snow1).toBe(50); // ratio is 5
134-
135-
const snow2 = snowfallEquivalentContinuous(10, 253.15); // -20°C
136-
expect(snow2).toBeCloseTo(300, 2); // ratio is 30 (with floating point tolerance)
137-
});
138-
89+
13990
it('provides smoother transitions than step function', () => {
14091
// The continuous version should give different values for close temperatures
141-
const snow1 = snowfallEquivalentContinuous(10, 267.15); // -6°C
142-
const snow2 = snowfallEquivalentContinuous(10, 266.15); // -7°C
92+
const snow1 = snowfallEquivalent(10, 267.15); // -6°C
93+
const snow2 = snowfallEquivalent(10, 266.15); // -7°C
14394

14495
// Both should be positive and snow2 should be >= snow1
14596
expect(snow1).toBeGreaterThan(0);
14697
expect(snow2).toBeGreaterThanOrEqual(snow1);
14798
});
148-
149-
it('returns 0 for 0mm liquid precipitation', () => {
150-
expect(snowfallEquivalentContinuous(0, 270)).toBe(0);
151-
expect(snowfallEquivalentContinuous(0, 260)).toBe(0);
152-
});
15399
});
154100

101+
155102
describe('snowToLiquidEquivalent', () => {
156103
it('converts snow depth to liquid precipitation', () => {
157104
// 50mm snow at 0°C (ratio 5:1) = 10mm liquid
158105
expect(snowToLiquidEquivalent(50, 273.15)).toBe(10);
159106

160-
// 150mm snow at -8°C (ratio 15:1) = 10mm liquid
161-
expect(snowToLiquidEquivalent(150, 265.15)).toBe(10);
107+
// 150mm snow at -8°C (ratio ~15:1) = ~10mm liquid
108+
const liquid1 = snowToLiquidEquivalent(150, 265.15);
109+
expect(liquid1).toBeCloseTo(10, 1);
162110

163111
// 300mm snow at -20°C (ratio 30:1) = 10mm liquid
164-
expect(snowToLiquidEquivalent(300, 253.15)).toBe(10);
112+
const liquid2 = snowToLiquidEquivalent(300, 253.15);
113+
expect(liquid2).toBeCloseTo(10, 1);
165114
});
166115

167116
it('is the inverse of snowfallEquivalent', () => {
@@ -181,8 +130,10 @@ describe('snowToLiquidEquivalent', () => {
181130
});
182131

183132
it('handles fractional values', () => {
184-
// 75mm snow at -10°C (ratio 15:1) = 5mm liquid
185-
expect(snowToLiquidEquivalent(75, 263.15)).toBe(5);
133+
// At -10°C, ratio is ~17.5:1
134+
// So 87.5mm snow should give ~5mm liquid
135+
const liquid = snowToLiquidEquivalent(87.5, 263.15);
136+
expect(liquid).toBeCloseTo(5, 1);
186137
});
187138
});
188139

@@ -201,16 +152,16 @@ describe('Integration tests', () => {
201152
});
202153

203154
it('handles realistic weather scenarios', () => {
204-
// Light snow: 2mm liquid at -5°C
155+
// Light snow: 2mm liquid at -5°C (ratio ~11.25:1)
205156
const lightSnow = snowfallEquivalent(2, 268.15);
206-
expect(lightSnow).toBe(20); // 10:1 ratio
157+
expect(lightSnow).toBeCloseTo(22.5, 1);
207158

208-
// Moderate snow: 10mm liquid at -10°C
159+
// Moderate snow: 10mm liquid at -10°C (ratio ~17.5:1)
209160
const moderateSnow = snowfallEquivalent(10, 263.15);
210-
expect(moderateSnow).toBe(150); // 15:1 ratio
161+
expect(moderateSnow).toBeCloseTo(175, 1);
211162

212-
// Heavy snow: 25mm liquid at -15°C
163+
// Heavy snow: 25mm liquid at -15°C (ratio ~23.75:1)
213164
const heavySnow = snowfallEquivalent(25, 258.15);
214-
expect(heavySnow).toBe(500); // 20:1 ratio
165+
expect(heavySnow).toBeCloseTo(593.75, 1);
215166
});
216167
});

0 commit comments

Comments
 (0)