Skip to content

Atmo

Atmo

Atmo(
    altitude: Optional[Union[float, Distance]] = None,
    pressure: Optional[Union[float, Pressure]] = None,
    temperature: Optional[Union[float, Temperature]] = None,
    humidity: float = 0.0,
    powder_t: Optional[Union[float, Temperature]] = None,
)

Atmospheric conditions and density calculations.

Attributes:

Name Type Description
altitude Distance

Altitude relative to sea level

pressure Pressure

Unadjusted barometric pressure, a.k.a. station pressure

temperature Temperature

Temperature

humidity float

Relative humidity [0% to 100%]

powder_temp Temperature

Temperature of powder (if different from atmosphere). (Used when Ammo.use_powder_sensitivity is True)

density_ratio float

Ratio of current density to standard atmospheric density

mach Velocity

Velocity of sound (Mach 1) for current atmosphere

Parameters:

Name Type Description Default
altitude Optional[Union[float, Distance]]

Altitude relative to sea level

None
pressure Optional[Union[float, Pressure]]

Atmospheric pressure

None
temperature Optional[Union[float, Temperature]]

Atmospheric temperature

None
humidity float

Atmospheric relative humidity [0% to 100%]

0.0
powder_t Optional[Union[float, Temperature]]

Custom temperature of powder different to atmospheric. Used when Ammo.use_powder_sensitivity is True

None
Example
from py_ballisticcalc import Atmo
atmo = Atmo(
    altitude=Unit.Meter(100),
    pressure=Unit.hPa(1000),
    temperature=Unit.Celsius(20),
    humidity=50,
    powder_t=Unit.Celsius(15)
)

Methods:

Name Description
update_density_ratio

Update the density ratio based on current conditions.

temperature_at_altitude

Temperature at altitude interpolated from zero conditions using lapse rate.

pressure_at_altitude

Pressure at altitude interpolated from zero conditions using lapse rate.

get_density_and_mach_for_altitude

Calculate density ratio and Mach 1 for the specified altitude.

standard_temperature

Calculate ICAO standard temperature for altitude.

standard_pressure

Calculate ICAO standard pressure for altitude.

icao

Create Atmo instance of standard ICAO atmosphere at given altitude.

machF

Calculate Mach 1 in fps for given Fahrenheit temperature.

machC

Calculate Mach 1 in mps for given Celsius temperature.

machK

Calculate Mach 1 in mps for given Kelvin temperature.

calculate_air_density

Calculate air density from temperature, pressure, and humidity.

Source code in py_ballisticcalc/conditions.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
def __init__(self,
             altitude: Optional[Union[float, Distance]] = None,
             pressure: Optional[Union[float, Pressure]] = None,
             temperature: Optional[Union[float, Temperature]] = None,
             humidity: float = 0.0,
             powder_t: Optional[Union[float, Temperature]] = None):
    """
    Create a new Atmo instance.

    Args:
        altitude: Altitude relative to sea level
        pressure: Atmospheric pressure
        temperature: Atmospheric temperature
        humidity: Atmospheric relative humidity [0% to 100%]
        powder_t: Custom temperature of powder different to atmospheric.
            Used when Ammo.use_powder_sensitivity is True

    Example:
        ```python
        from py_ballisticcalc import Atmo
        atmo = Atmo(
            altitude=Unit.Meter(100),
            pressure=Unit.hPa(1000),
            temperature=Unit.Celsius(20),
            humidity=50,
            powder_t=Unit.Celsius(15)
        )
        ```
    """
    self._initializing = True
    self._altitude = PreferredUnits.distance(altitude or 0)
    self._pressure = PreferredUnits.pressure(pressure or Atmo.standard_pressure(self.altitude))
    self._temperature = PreferredUnits.temperature(temperature or Atmo.standard_temperature(self.altitude))
    # If powder_temperature not provided we use atmospheric temperature:
    self._powder_temp = PreferredUnits.temperature(powder_t or self.temperature)
    self._t0 = self.temperature >> Temperature.Celsius
    self._p0 = self.pressure >> Pressure.hPa
    self._a0 = self.altitude >> Distance.Foot
    self._mach = Atmo.machF(self._temperature >> Temperature.Fahrenheit)
    self.humidity = humidity
    self._initializing = False
    self.update_density_ratio()

Attributes

altitude property
altitude: Distance

Altitude relative to sea level.

pressure property
pressure: Pressure

Unadjusted barometric pressure, a.k.a. station pressure.

temperature property
temperature: Temperature

Local air temperature.

powder_temp property
powder_temp: Temperature

Powder temperature.

mach property
mach: Velocity

Velocity of sound (Mach 1) for current atmosphere.

density_ratio property
density_ratio: float

Ratio of current density to standard atmospheric density.

humidity property writable
humidity: float

Relative humidity [0% to 100%].

density_metric property
density_metric: float

Air density in metric units (kg/m^3).

density_imperial property
density_imperial: float

Air density in imperial units (lb/ft^3).

Functions

update_density_ratio
update_density_ratio() -> None

Update the density ratio based on current conditions.

Source code in py_ballisticcalc/conditions.py
172
173
174
def update_density_ratio(self) -> None:
    """Update the density ratio based on current conditions."""
    self._density_ratio = Atmo.calculate_air_density(self._t0, self._p0, self.humidity) / cStandardDensityMetric
temperature_at_altitude
temperature_at_altitude(altitude: float) -> float

Temperature at altitude interpolated from zero conditions using lapse rate.

Parameters:

Name Type Description Default
altitude float

ASL in ft

required

Returns:

Type Description
float

temperature in °C

Source code in py_ballisticcalc/conditions.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def temperature_at_altitude(self, altitude: float) -> float:
    """Temperature at altitude interpolated from zero conditions using lapse rate.

    Args:
        altitude: ASL in ft

    Returns:
        temperature in °C
    """
    t = (altitude - self._a0) * cLapseRateKperFoot + self._t0
    if t < Atmo.cLowestTempC:
        t = Atmo.cLowestTempC
        warnings.warn(f"Temperature interpolated from altitude fell below minimum temperature limit.  "
                      f"Model not accurate here.  Temperature bounded at cLowestTempF: {cLowestTempF}°F.",
                      RuntimeWarning)
    return t
pressure_at_altitude
pressure_at_altitude(altitude: float) -> float

Pressure at altitude interpolated from zero conditions using lapse rate.

Ref: https://en.wikipedia.org/wiki/Barometric_formula#Pressure_equations

Parameters:

Name Type Description Default
altitude float

ASL in ft

required

Returns:

Type Description
float

pressure in hPa

Source code in py_ballisticcalc/conditions.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def pressure_at_altitude(self, altitude: float) -> float:
    """Pressure at altitude interpolated from zero conditions using lapse rate.

    Ref: https://en.wikipedia.org/wiki/Barometric_formula#Pressure_equations

    Args:
        altitude: ASL in ft

    Returns:
        pressure in hPa
    """
    p = self._p0 * math.pow(1 + cLapseRateKperFoot * (altitude - self._a0) / (self._t0 + cDegreesCtoK),
                            cPressureExponent)
    return p
get_density_and_mach_for_altitude
get_density_and_mach_for_altitude(
    altitude: float,
) -> Tuple[float, float]

Calculate density ratio and Mach 1 for the specified altitude.

Ref: https://en.wikipedia.org/wiki/Barometric_formula#Density_equations

Parameters:

Name Type Description Default
altitude float

ASL in units of feet. Note: Altitude above 36,000 ft not modelled this way.

required

Returns:

Type Description
Tuple[float, float]

density ratio and Mach 1 (fps) for the specified altitude

Source code in py_ballisticcalc/conditions.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
def get_density_and_mach_for_altitude(self, altitude: float) -> Tuple[float, float]:
    """Calculate density ratio and Mach 1 for the specified altitude.

    Ref: https://en.wikipedia.org/wiki/Barometric_formula#Density_equations

    Args:
        altitude: ASL in units of feet.
            Note: Altitude above 36,000 ft not modelled this way.

    Returns:
        density ratio and Mach 1 (fps) for the specified altitude
    """
    # Within 30 ft of initial altitude use initial values to save compute
    if math.fabs(self._a0 - altitude) < 30:
        mach = self._mach
        density_ratio = self._density_ratio
    else:
        if altitude > 36089:
            warnings.warn("Density request for altitude above troposphere."
                          " Atmospheric model not valid here.", RuntimeWarning)
        t = self.temperature_at_altitude(altitude) + cDegreesCtoK
        mach = Velocity.MPS(Atmo.machK(t)) >> Velocity.FPS
        p = self.pressure_at_altitude(altitude)
        density_delta = ((self._t0 + cDegreesCtoK) * p) / (self._p0 * t)
        density_ratio = self._density_ratio * density_delta
        # # Alternative simplified model:
        # # Ref https://en.wikipedia.org/wiki/Density_of_air#Exponential_approximation
        # # see doc/'Air Density Models.svg' for comparison
        # density_ratio = self._density_ratio * math.exp(-(altitude - self._a0) / 34122)
    return density_ratio, mach
standard_temperature staticmethod
standard_temperature(altitude: Distance) -> Temperature

Calculate ICAO standard temperature for altitude.

Note: This model is only valid up to the troposphere (~36,000 ft).

Parameters:

Name Type Description Default
altitude Distance

ASL in units of feet.

required

Returns:

Type Description
Temperature

ICAO standard temperature for altitude

Source code in py_ballisticcalc/conditions.py
256
257
258
259
260
261
262
263
264
265
266
267
268
269
@staticmethod
def standard_temperature(altitude: Distance) -> Temperature:
    """Calculate ICAO standard temperature for altitude.

    Note: This model is only valid up to the troposphere (~36,000 ft).

    Args:
        altitude: ASL in units of feet.

    Returns:
        ICAO standard temperature for altitude
    """
    return Temperature.Fahrenheit(cStandardTemperatureF
                                  + (altitude >> Distance.Foot) * cLapseRateImperial)
standard_pressure staticmethod
standard_pressure(altitude: Distance) -> Pressure

Calculate ICAO standard pressure for altitude.

Note: This model only valid up to troposphere (~36,000 ft). Ref: https://en.wikipedia.org/wiki/Barometric_formula#Pressure_equations

Parameters:

Name Type Description Default
altitude Distance

Distance above sea level (ASL)

required

Returns:

Type Description
Pressure

ICAO standard pressure for altitude

Source code in py_ballisticcalc/conditions.py
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
@staticmethod
def standard_pressure(altitude: Distance) -> Pressure:
    """Calculate ICAO standard pressure for altitude.

    Note: This model only valid up to troposphere (~36,000 ft).
    Ref: https://en.wikipedia.org/wiki/Barometric_formula#Pressure_equations

    Args:
        altitude: Distance above sea level (ASL)

    Returns:
        ICAO standard pressure for altitude
    """
    return Pressure.hPa(cStandardPressureMetric
                        * math.pow(1 + cLapseRateMetric * (altitude >> Distance.Meter) /
                                       (cStandardTemperatureC + cDegreesCtoK),
                                   cPressureExponent))
icao staticmethod
icao(
    altitude: Union[float, Distance] = 0,
    temperature: Optional[Temperature] = None,
    humidity: float = cStandardHumidity,
) -> Atmo

Create Atmo instance of standard ICAO atmosphere at given altitude.

Note: This model is only valid up to the troposphere (~36,000 ft).

Parameters:

Name Type Description Default
altitude Union[float, Distance]

relative to sea level. Default is sea level (0 ft).

0
temperature Optional[Temperature]

air temperature. Default is standard temperature at altitude.

None

Returns:

Type Description
Atmo

Atmo instance of standard ICAO atmosphere at given altitude.

Atmo

If temperature not specified uses standard temperature.

Source code in py_ballisticcalc/conditions.py
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
@staticmethod
def icao(altitude: Union[float, Distance] = 0, temperature: Optional[Temperature] = None,
         humidity: float = cStandardHumidity) -> Atmo:
    """Create Atmo instance of standard ICAO atmosphere at given altitude.

    Note: This model is only valid up to the troposphere (~36,000 ft).

    Args:
        altitude: relative to sea level.  Default is sea level (0 ft).
        temperature: air temperature.  Default is standard temperature at altitude.

    Returns:
        Atmo instance of standard ICAO atmosphere at given altitude.
        If temperature not specified uses standard temperature.
    """
    altitude = PreferredUnits.distance(altitude)
    if temperature is None:
        temperature = Atmo.standard_temperature(altitude)
    pressure = Atmo.standard_pressure(altitude)

    return Atmo(altitude, pressure, temperature, humidity)
machF staticmethod
machF(fahrenheit: float) -> float

Calculate Mach 1 in fps for given Fahrenheit temperature.

Parameters:

Name Type Description Default
fahrenheit float

Fahrenheit temperature

required

Returns:

Type Description
float

Mach 1 in fps for given temperature

Source code in py_ballisticcalc/conditions.py
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
@staticmethod
def machF(fahrenheit: float) -> float:
    """Calculate Mach 1 in fps for given Fahrenheit temperature.

    Args:
        fahrenheit: Fahrenheit temperature

    Returns:
        Mach 1 in fps for given temperature
    """
    if fahrenheit < -cDegreesFtoR:
        bad_temp = fahrenheit
        fahrenheit = cLowestTempF
        warnings.warn(f"Invalid temperature: {bad_temp}°F. Adjusted to ({cLowestTempF}°F).", RuntimeWarning)
    return math.sqrt(fahrenheit + cDegreesFtoR) * cSpeedOfSoundImperial
machC staticmethod
machC(celsius: float) -> float

Calculate Mach 1 in mps for given Celsius temperature.

Parameters:

Name Type Description Default
celsius float

Celsius temperature

required

Returns:

Type Description
float

Mach 1 in mps for given temperature

Source code in py_ballisticcalc/conditions.py
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
@staticmethod
def machC(celsius: float) -> float:
    """Calculate Mach 1 in mps for given Celsius temperature.

    Args:
        celsius: Celsius temperature

    Returns:
        Mach 1 in mps for given temperature
    """
    if celsius < -cDegreesCtoK:
        bad_temp = celsius
        celsius = Atmo.cLowestTempC
        warnings.warn(f"Invalid temperature: {bad_temp}°C. Adjusted to ({celsius}°C).", RuntimeWarning)
    return Atmo.machK(celsius + cDegreesCtoK)
machK staticmethod
machK(kelvin: float) -> float

Calculate Mach 1 in mps for given Kelvin temperature.

Parameters:

Name Type Description Default
kelvin float

Kelvin temperature

required

Returns:

Type Description
float

Mach 1 in mps for given temperature

Source code in py_ballisticcalc/conditions.py
346
347
348
349
350
351
352
353
354
355
356
@staticmethod
def machK(kelvin: float) -> float:
    """Calculate Mach 1 in mps for given Kelvin temperature.

    Args:
        kelvin: Kelvin temperature

    Returns:
        Mach 1 in mps for given temperature
    """
    return math.sqrt(kelvin) * cSpeedOfSoundMetric
calculate_air_density staticmethod
calculate_air_density(
    t: float, p_hpa: float, humidity: float
) -> float

Calculate air density from temperature, pressure, and humidity.

Parameters:

Name Type Description Default
t float

Temperature in degrees Celsius.

required
p_hpa float

Pressure in hPa (hectopascals). Internally converted to Pa.

required
humidity float

Relative humidity. Accepts either fraction [0..1] or percent [0..100].

required

Returns:

Type Description
float

Air density in kg/m³.

Notes
  • Divide result by cDensityImperialToMetric to get density in lb/ft³
  • Source: https://www.nist.gov/system/files/documents/calibrations/CIPM-2007.pdf
Source code in py_ballisticcalc/conditions.py
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
@staticmethod
def calculate_air_density(t: float, p_hpa: float, humidity: float) -> float:
    """Calculate air density from temperature, pressure, and humidity.

    Args:
        t: Temperature in degrees Celsius.
        p_hpa: Pressure in hPa (hectopascals). Internally converted to Pa.
        humidity: Relative humidity. Accepts either fraction [0..1] or percent [0..100].

    Returns:
        Air density in kg/m³.

    Notes:
        - Divide result by cDensityImperialToMetric to get density in lb/ft³
        - Source: https://www.nist.gov/system/files/documents/calibrations/CIPM-2007.pdf
    """
    R = 8.314472  # J/(mol·K), universal gas constant
    M_a = 28.96546e-3  # kg/mol, molar mass of dry air
    M_v = 18.01528e-3  # kg/mol, molar mass of water vapor

    def saturation_vapor_pressure(T):
        # Calculation of saturated vapor pressure according to CIPM 2007
        A = [1.2378847e-5, -1.9121316e-2, 33.93711047, -6.3431645e3]
        return math.exp(A[0] * T ** 2 + A[1] * T + A[2] + A[3] / T)

    def enhancement_factor(p, T):
        # Calculation of enhancement factor according to CIPM 2007
        alpha = 1.00062
        beta = 3.14e-8
        gamma = 5.6e-7
        return alpha + beta * p + gamma * T ** 2

    def compressibility_factor(p, T, x_v):
        # Calculation of compressibility factor according to CIPM 2007
        a0 = 1.58123e-6
        a1 = -2.9331e-8
        a2 = 1.1043e-10
        b0 = 5.707e-6
        b1 = -2.051e-8
        c0 = 1.9898e-4
        c1 = -2.376e-6
        d = 1.83e-11
        e = -0.765e-8

        t = T - cDegreesCtoK
        Z = 1 - (p / T) * (a0 + a1 * t + a2 * t ** 2 + (b0 + b1 * t) * x_v + (c0 + c1 * t) * x_v ** 2) \
            + (p / T) ** 2 * (d + e * x_v ** 2)
        return Z

    # Normalize humidity to fraction [0..1]
    rh = float(humidity)
    rh_frac = rh / 100.0 if rh > 1.0 else rh
    rh_frac = max(0.0, min(1.0, rh_frac))

    # Convert inputs for CIPM equations
    T_K = t + cDegreesCtoK           # Kelvin
    p = float(p_hpa) * 100.0         # hPa -> Pa

    # Calculation of saturated vapor pressure and enhancement factor
    p_sv = saturation_vapor_pressure(T_K)  # Pa (saturated vapor pressure)
    f = enhancement_factor(p, t)           # Enhancement factor (p in Pa, t in °C)

    # Partial pressure of water vapor and mole fraction
    p_v = rh_frac * f * p_sv               # Pa
    x_v = p_v / p                          # Mole fraction of water vapor

    # Calculation of compressibility factor
    Z = compressibility_factor(p, T_K, x_v)

    # Density (kg/m^3) using moist air composition and compressibility factor
    density = (p * M_a) / (Z * R * T_K) * (1.0 - x_v * (1.0 - M_v / M_a))
    return density

Vacuum

Vacuum(
    altitude: Optional[Union[float, Distance]] = None,
    temperature: Optional[Union[float, Temperature]] = None,
)

Bases: Atmo

Vacuum atmosphere (zero drag).

Source code in py_ballisticcalc/conditions.py
437
438
439
440
441
442
def __init__(self,
             altitude: Optional[Union[float, Distance]] = None,
             temperature: Optional[Union[float, Temperature]] = None):
    super().__init__(altitude, 0, temperature, 0)
    self._pressure = PreferredUnits.pressure(0)
    self._density_ratio = 0