Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a few new WeatherHelper functions #1941

Merged
merged 3 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/devices/Common/Iot/Device/Common/WeatherHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -331,5 +331,84 @@ public static Pressure CalculateBarometricPressure(Pressure measuredPressure, Te
}

#endregion

/// <summary>
/// Simplified air density (not taking humidity into account)
/// </summary>
/// <param name="airPressure">Measured air pressure</param>
/// <param name="temperature">Measured temperature</param>
/// <returns>Approximate standard air density</returns>
public static Density CalculateAirDensity(Pressure airPressure, Temperature temperature)
{
double gasConstant = 287.058; // J / (kg * K), for dry air
krwq marked this conversation as resolved.
Show resolved Hide resolved
var result = airPressure.Pascals / (gasConstant * temperature.Kelvins);
return Density.FromKilogramsPerCubicMeter(result);
}

/// <summary>
/// Calculates the air density
/// </summary>
/// <param name="airPressure">Measured air pressure</param>
/// <param name="temperature">Measured temperature</param>
/// <param name="humidity">Measured relative humidity</param>
/// <returns>Approximate standard air density at sea level</returns>
/// <remarks>From https://de.wikipedia.org/wiki/Luftdichte </remarks>
public static Density CalculateAirDensity(Pressure airPressure, Temperature temperature, RelativeHumidity humidity)
{
double rs = 287.058;
double rd = 461.523;
var pd = CalculateSaturatedVaporPressureOverWater(temperature);
// It's still called "constant" even though it's not constant
double gasConstant = rs / (1 - (humidity.Percent / 100) * (pd.Pascals / airPressure.Pascals) * (1 - (rs / rd)));
var result = airPressure.Pascals / (gasConstant * temperature.Kelvins);
return Density.FromKilogramsPerCubicMeter(result);
}

/// <summary>
/// Calculates the wind chill temperature - this is the perceived temperature in (heavy) winds at cold temperatures.
/// This is only useful at temperatures below about 20°C, above use <see cref="CalculateHeatIndex"/> instead.
/// Not suitable for wind speeds &lt; 5 km/h.
/// </summary>
/// <param name="temperature">The measured air temperature</param>
/// <param name="windSpeed">The wind speed (measured at 10m above ground)</param>
/// <returns>The perceived temperature. Note that this is not a real temperature, and the skin will never really reach
/// this temperature. This is more an indication on how fast the skin will reach the air temperature. If the skin
/// reaches a temperature of about -5°C, frostbite might occur.</returns>
/// <remarks>From https://de.wikipedia.org/wiki/Windchill </remarks>
/// <exception cref="ArgumentOutOfRangeException">The wind speed is less than zero</exception>
public static Temperature CalculateWindchill(Temperature temperature, Speed windSpeed)
{
if (windSpeed < Speed.Zero)
{
throw new ArgumentOutOfRangeException(nameof(windSpeed), "The wind speed cannot be negative");
}

double va = temperature.DegreesCelsius;
if (windSpeed < Speed.FromKilometersPerHour(1))
{
// otherwise, the result is complete crap, because the second and third terms of the equation are 0
pgrawehr marked this conversation as resolved.
Show resolved Hide resolved
windSpeed = Speed.FromKilometersPerHour(1);
Copy link
Member

@krwq krwq Oct 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense to just return temperature here instead? (might make function non-continous, you might want to check math)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because if the wind speed is low, the returned temperature is above the input temperature. This is expected and explained in the documentation. Only if the wind speed is zero or almost zero, the formula gets completely off. So this condition basically limits the output temperature to be at most about 2 degrees over the input temperature.

}

double wct = 13.12 + 0.6215 * va + (0.3965 * va - 11.37) * Math.Pow(windSpeed.KilometersPerHour, 0.16);
return Temperature.FromDegreesCelsius(wct);
}

/// <summary>
/// Calculates the wind force on an object.
/// </summary>
/// <param name="densityOfAir">The denisty of the air, calculated using one of the overloads of <see cref="CalculateAirDensity(UnitsNet.Pressure,UnitsNet.Temperature)"/></param>
/// <param name="windSpeed">The speed of the wind</param>
/// <param name="pressureCoefficient">Pressure coefficient for the shape of the object. Use 1 for a rectangular object directly facing the wind</param>
/// <returns>The Pressure the wind applies on the object</returns>
/// <remarks>From https://de.wikipedia.org/wiki/Winddruck </remarks>
public static Pressure CalculateWindForce(Density densityOfAir, Speed windSpeed, double pressureCoefficient = 1.0)
{
double v = windSpeed.MetersPerSecond;
double rho = densityOfAir.KilogramsPerCubicMeter;

double wd = pressureCoefficient * rho / 2 * (v * v);
return Pressure.FromNewtonsPerSquareMeter(wd);
}
}
}
48 changes: 48 additions & 0 deletions src/devices/Common/tests/WeatherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,5 +185,53 @@ public void GetRelativeHumidityFromActualAirTemperature(double inTemp, double in

Assert.Equal(outHumidityExpected, result.Percent, 3);
}

[Theory]
[InlineData(1013.25, 35, 1.1455)]
[InlineData(1013.25, 0, 1.2922)]
[InlineData(1013.25, -25, 1.4224)]
public void CalculateAirDensitySimple(double inPressure, double inTemp, double expected)
{
var result = WeatherHelper.CalculateAirDensity(Pressure.FromMillibars(inPressure),
Temperature.FromDegreesCelsius(inTemp));

Assert.Equal(expected, result.KilogramsPerCubicMeter, 4);
}

[Theory]
[InlineData(1013.25, 35, 0, 1.1455)]
[InlineData(1013.25, 35, 50, 1.1335)]
[InlineData(1013.25, 35, 100, 1.1214)]
public void CalculateAirDensity(double inPressure, double inTemp, double inHumidity, double expected)
{
var result = WeatherHelper.CalculateAirDensity(Pressure.FromMillibars(inPressure),
Temperature.FromDegreesCelsius(inTemp), RelativeHumidity.FromPercent(inHumidity));

Assert.Equal(expected, result.KilogramsPerCubicMeter, 4);
}

[Theory]
[InlineData(0, 0, 1.75)] // This is to be expected, see WP article
pgrawehr marked this conversation as resolved.
Show resolved Hide resolved
[InlineData(10, 5, 9.8)]
[InlineData(-10, 20, -17.9)]
[InlineData(-15, 40, -27.4)]
public void CalculateWindchill(double temperature, double windSpeed, double expected)
{
var result = WeatherHelper.CalculateWindchill(Temperature.FromDegreesCelsius(temperature),
Speed.FromKilometersPerHour(windSpeed));

Assert.Equal(expected, result.DegreesCelsius, 1);
}

[Theory]
[InlineData(20, 38.5, 1013, 68.83)]
[InlineData(20, 88.9, 1013, 367.0)]
public void CalculateWindForce(double temperature, double windSpeed, double pressure, double expected)
{
Density airDensity = WeatherHelper.CalculateAirDensity(Pressure.FromHectopascals(pressure),
Temperature.FromDegreesCelsius(temperature));
var density = WeatherHelper.CalculateWindForce(airDensity, Speed.FromKilometersPerHour(windSpeed), 1.0);
Assert.Equal(expected, density.NewtonsPerSquareMeter, 1);
}
}
}