Aire húmedo
Normas aplicables: IEC 61094
Una calibración por reciprocidad necesita conocer el aire en el que se hace con cinco o seis cifras, y por eso la norma que define esa calibración lleva también el modelo de aire húmedo mejor enunciado de la literatura acústica. El Anexo F es ese modelo, y esta página es lo que la biblioteca hace con él.
La llamada útil más corta
Sección titulada «La llamada útil más corta»from phonometry import fluids
f = fluids.air( temperature_c=23.0, static_pressure_pa=101325.0, relative_humidity_percent=50.0,)print(f.density) # 1.1860847889882964 kg/m3print(f.speed_of_sound) # 345.86651725321 m/sprint(f.characteristic_impedance) # 410.2270151343906 Pa.s/mÉsas son las condiciones de la primera fila de la Tabla F.1, y los números son los que imprime el anexo. Cada una de las diez cifras que tabula vuelve dentro del redondeo de su última posición impresa.
Las tres condiciones no pesan igual
Sección titulada «Las tres condiciones no pesan igual»La biblioteca pide la temperatura y supone las otras dos. No es pereza en una dirección y rigor en la otra: se sigue de cuánto vale cada una. Barriendo cada condición por su rango plausible, con las propias ecuaciones del anexo:
| Condición | Barrida entre | Mueve la densidad un |
|---|---|---|
| Presión estática | 80 y 105 kPa | 25 % |
| Temperatura | 15 y 27 °C | 4,5 % |
| Humedad relativa | 0 y 100 % | 1,05 % |
La presión domina, y además es la que la gente cree saber ya. Un emplazamiento de ensayo a 1000 metros está a unos 90 kPa. Eso es un 11 % por debajo de la atmósfera estándar que la biblioteca supondría, y deja la densidad un 13 % alta, que en un nivel de potencia sonora es medio decibelio sin nada en la página que lo diga.
La humedad es el caso contrario. Es la que menos mueve la densidad, y casi nadie conoce la suya sin medirla.
Así que las dos se suponen y las dos se anuncian, una sola vez, en el mismo aviso:
import warnings
from phonometry import fluids
with warnings.catch_warnings(record=True) as caught: warnings.simplefilter("always") fluids.air(temperature_c=20.0)
print(caught[0].category.__name__) # FluidAssumptionWarningprint(str(caught[0].message))# air() assumed 101325 Pa and 50 % relative humidity. The pressure is the one# worth measuring: a site 1000 m up sits near 90 kPa, and taking it for one# standard atmosphere puts the density about 13 % high. The whole span of# humidity, 0 % to 100 %, is worth about 1 % of the density. Pass the# conditions to silence this.Un aviso y no dos, porque quien no pasó ninguna de las dos tiene una cosa que arreglar, no dos.
Pasar las dos es silencioso, que es la propiedad que debe recibir quien midió su
aire. Python muestra el aviso una vez por sitio de llamada, así que explorar en
un intérprete no queda ahogado en él, y cuelga de PhonometryWarning, de modo
que una sola regla lo convierte en error para quien prefiera que sus ejecuciones
fallen en vez de avisar:
import warnings
from phonometry import PhonometryWarning
warnings.filterwarnings("error", category=PhonometryWarning)La temperatura no tiene valor por defecto en absoluto. Es la única condición que quien llama ha medido de verdad, y no hay ningún valor defendible que inventarle.
La fracción de dióxido de carbono sí se pone por defecto, en silencio, a 0,000 4. Eso no es una suposición sobre el aire de nadie: el apartado F.2 la nombra como el valor a usar en condiciones de laboratorio a falta de una medida, así que la biblioteca está citando el anexo y no rellenando un hueco.
Qué fija el modelo y qué no se inventa
Sección titulada «Qué fija el modelo y qué no se inventa»La Tabla F.1 imprime cinco magnitudes. El apartado F.6 da expresiones para dos más, y tres se siguen por identidad de las anteriores:
f.density # impresa en la Tabla F.1f.speed_of_sound # impresa, a frecuencia cerof.heat_capacity_ratio # impresaf.viscosity # impresaf.thermal_diffusivity # impresaf.thermal_conductivity # expresión del apartado F.6f.specific_heat_capacity # expresión del apartado F.6f.characteristic_impedance # rho cf.prandtl_number # eta / (rho alpha_t)f.kinematic_viscosity # eta / rhoUna magnitud que este modelo no determinó lanza en vez de devolver un número que nadie imprimió. El error nombra el modelo, lo que sí determina y las dos salidas:
from phonometry.fluids import Fluid, FluidPropertyUnavailable
medido = Fluid( temperature_c=10.0, static_pressure_pa=1.1e6, composition={"salinity_psu": 35.0}, model="a density and a sound speed, measured", validity="", properties={"density": 1027.0, "speed_of_sound": 1491.0},)try: medido.heat_capacity_ratioexcept FluidPropertyUnavailable as exc: print(exc)# 'a density and a sound speed, measured' does not determine# 'heat_capacity_ratio'; it determines density, speed_of_sound. Supply the# quantity yourself, or use a model that prints it.Con un solo fluido en la biblioteca eso parece pedantería, y deja de parecerlo
con dos: el agua de mar no tiene relación de calores específicos que dar, y un
Fluid que se la inventara estaría entregando un número con la forma de una
medida y nada de su sustancia.
Tres atributos no son magnitudes y nunca lanzan. Son el registro de dónde salieron los números:
f.properties # exactamente lo que este modelo determinóf.model # 'IEC 61094-2:2009 Annex F (CIPM-2007)'f.composition # la humedad y el CO2 con los que se calculóEl dominio impreso, y la diferencia entre un ajuste y un hecho
Sección titulada «El dominio impreso, y la diferencia entre un ajuste y un hecho»El Anexo F enuncia, en su propia página, dónde se validaron sus ecuaciones: 15 °C a 27 °C, 60 kPa a 110 kPa y 10 % a 90 % de humedad relativa. No enuncia ningún rango para la fracción de dióxido de carbono.
Fuera de esa caja la biblioteca avisa y sigue respondiendo:
import warnings
from phonometry import fluids
with warnings.catch_warnings(record=True) as caught: warnings.simplefilter("always") hot = fluids.air(temperature_c=60.0, static_pressure_pa=101325.0, relative_humidity_percent=50.0)
print(caught[0].category.__name__) # FluidWarningprint("temperature 60 degC (stated 15 to 27)" in str(caught[0].message)) # Trueprint(round(hot.speed_of_sound, 2)) # 371.52La distinción importa y es la regla que sigue toda la biblioteca. El aire a 60 °C
dentro de un conducto existe; un ajuste más allá de su rango validado sigue
siendo aritmética, y rechazarlo sería rechazar una medida real. Lo que sí se
rechaza es un estado que no puede darse, y esa es la lista completa: una
temperatura igual o por debajo del cero absoluto, una presión no positiva, una
humedad relativa fuera del 0 % al 100 %, una fracción molar de dióxido de
carbono fuera de 0 a 1, y un valor no finito en cualquiera de las cuatro. Cada
una lanza ValueError nombrando el argumento.
Cómo se fijan los números
Sección titulada «Cómo se fijan los números»El oráculo es la Tabla F.1: cinco magnitudes en dos conjuntos de condiciones,
diez cifras en total. La tolerancia no se elige. Un valor está enunciado con
media unidad de la última posición hasta la que se imprimió, así que eso es lo
que significa reproducirlo, y el test deriva esa cota de la cadena impresa en
vez de llevar una constante que alguien escogió. Lo que fija la cota es esa
posición, no un recuento de cifras significativas: 1,186 084 8 tiene ocho y
2,115 317 x 10^-5 siete, y la misma regla lee las dos:
La tolerancia, derivada de lo impreso
from decimal import Decimal
def half_of_the_last_printed_figure(printed: str) -> float: """Media unidad de la última posición que imprimió el anexo.""" exponent = int(Decimal(printed).as_tuple().exponent) return float(Decimal(5) * Decimal(10) ** (exponent - 1))
half_of_the_last_printed_figure("1.1860848") # 5e-08half_of_the_last_printed_figure("2.115317e-5") # 5e-12Nadie puede aflojarla sin darse cuenta, y una edición futura que cite más cifras la aprieta sola.
Tres filas de conformidad registran cuánto de ese margen consume cada conjunto: un 55 % el primero, un 96 % el segundo y nada la identidad que ata la conductividad térmica y el calor específico a la difusividad impresa entre las dos. El 96 % es la densidad del segundo conjunto, y es el redondeo del propio anexo y no holgura de la biblioteca. Publicarlo como porcentaje significa que si algún cambio lo empuja fuera, se verá venir.
Dónde se usa este aire, y dónde no
Sección titulada «Dónde se usa este aire, y dónde no»Pasar un Fluid a una función de medida es asunto de los paquetes que lo
consumen, y viene con una regla que conviene enunciar aquí: donde una norma
fija el aire de su propio procedimiento, usar un aire mejor es apartarse de la
norma, y la biblioteca lo dice en vez de dejar que una medida deje de reproducir
en silencio el documento que cita.
Por eso las fórmulas de aire simplificadas de ISO 10534-2, ASTM E2611 e ISO 17497-1 se quedan en sus módulos con sus cláusulas, y por eso el número de Prandtl 0,71 de Johnson-Champoux-Allard sigue siendo 0,71 aunque el aire en ese estado tenga 0,728.
Dos estados de ese aire se publican como constantes, porque no tiene sentido
que quien llama los escriba dos veces. materials.ANNEX_A_AIR es el estado de
referencia que fija el anexo A.3 de la ISO 9053-2:2020 para una medida de
resistencia al flujo, 23 °C y 50 % de humedad relativa a una atmósfera
estándar, evaluado con el mismo modelo CIPM-2007 que cualquier otro aire.
simulation.SIMULATION_AIR es el medio por defecto del solver, y a propósito no
es eso: 343 m/s y 1,2 kg/m³ planos, el par redondo con el que se monta una
malla de diferencias finitas, llevado como Fluid para que una simulación diga
en qué aire se hizo en lugar de dejar dos floats sueltos en una llamada.
Referencias
Sección titulada «Referencias»- International Electrotechnical Commission. (2009). Electroacoustics — Measurement microphones — Part 2: Primary method for pressure calibration of laboratory standard microphones by the reciprocity technique (IEC 61094-2:2009). El Anexo F lleva el modelo de aire húmedo implementado aquí, sus coeficientes en la Tabla F.2 y los dos conjuntos de condiciones de la Tabla F.1 que lo fijan. La edición leída es su adopción europea BS EN 61094-2:2009.