The density of a gas¶
The density of a gas from its pressure, temperature and molar mass, by the ideal gas law as a density, rho = p M / (R T).
R is lusine.constants', a constant of nature. Source: Cengel & Boles, Thermodynamics, ch. 3. Checked against a reference in numpy, which has its own tests.
- Source
- Cengel & Boles, Thermodynamics, ch. 3 (the ideal gas equation of state).
- Proof
- reference, limit, invariant.
gas_density.trait
namespace lusine.properties;
float<kg/m3> gas_density(float<Pa> pressure, float<K> temperature, float<kg/mol> molar_mass)
{
return pressure * molar_mass / (lusine.constants.R * temperature);
}
The ideal gas law as a density, rho = p M / (R T). R is lusine.constants.R, N_A k exactly: the kernel names the constant's row and declares no number.
import os, sys
root = os.getcwd()
while not os.path.isfile(os.path.join(root, 'docsite.json')):
root = os.path.dirname(root)
sys.path.insert(0, os.path.join(root, 'tools'))
import numpy as np
import matplotlib.pyplot as plt
from lusine import trait_run
EPS = float(np.finfo(np.float32).eps)
The kernel¶
The law as the library states it, and as this page runs it.
print(trait_run.source('properties/gas_density.trait'))
// lusine.properties.gas_density: the density of a gas from its pressure, temperature and molar mass, by the ideal gas
// law as a density, rho = p M / (R T). R is lusine.constants', a constant of nature.
// Source: Cengel & Boles, Thermodynamics, ch. 3.
// Checked against a reference in numpy, which has its own tests.
namespace lusine.properties;
float<kg/m3> gas_density(float<Pa> pressure, float<K> temperature, float<kg/mol> molar_mass)
{
return pressure * molar_mass / (lusine.constants.R * temperature);
}
The program¶
Each section runs the kernel over a lattice of items, one a case: the host sets every parameter, and main() binds the kernel over the items.
print(trait_run.law_program('properties/gas_density.trait', 'gas_density'))
namespace probe;
lusine.version version = { { number = 1 } };
struct item {
host float<Pa> pressure;
host float<K> temperature;
host float<kg/mol> molar_mass;
float<kg/m3> result_0;
};
lattice<item, 1> items(spacing = 1 m, boundary = wall);
void main() { items.result_0 = lusine.properties.gas_density(items.pressure, items.temperature, items.molar_mass); }
Reference: air at sea level¶
The International Standard Atmosphere's air at 288.15 K and 101,325 Pa, of dry air's 28.9645 g/mol, is 1.225 kg/m³.
rho = trait_run.law('properties/gas_density.trait', 'gas_density', pressure=101325.0, temperature=288.15,
molar_mass=0.0289645)[0]
print(f'{rho:.4f} kg/m3')
assert abs(rho - 1.225) < 0.001
1.2250 kg/m3
Limit: one gas alone¶
Nitrogen alone at 300 K and 10⁵ Pa is p M / (R T) with nitrogen's 28.0134 g/mol, R = N_A k, within binary32's rounding.
R = 6.02214076e23 * 1.380649e-23
rho = trait_run.law('properties/gas_density.trait', 'gas_density', pressure=1e5, temperature=300.0,
molar_mass=0.0280134)[0]
assert abs(rho / (1e5 * 0.0280134 / (R * 300.0)) - 1) < 4 * EPS
print(rho, 'kg/m3')
1.1230791807174683 kg/m3
Invariant: 1/T at fixed pressure, p at fixed temperature¶
Charles's law and Boyle's, in the density: rho T holds across temperatures, and twice the pressure is twice the density, bit for bit.
T = np.linspace(230, 330, 11)
rho = trait_run.law('properties/gas_density.trait', 'gas_density', pressure=1e5, temperature=T, molar_mass=0.0289645)
assert np.allclose(rho * np.float32(T), rho[0] * np.float32(T[0]), rtol=4 * EPS)
pair = trait_run.law('properties/gas_density.trait', 'gas_density', pressure=[1e5, 2e5], temperature=300.0,
molar_mass=0.0289645)
assert pair[1] == 2 * pair[0]
fig, ax = plt.subplots(figsize=(7, 3))
ax.plot(T, rho, 'o-')
ax.set_xlabel('T (K)')
ax.set_ylabel('density (kg/m3)')
plt.show()