La biblioteca publica las tablas de sus libros y de sus normas, y nada de lo
que imprime un fabricante. Una hoja de características se revisa sin aviso
bajo el mismo título, sus condiciones de uso rara vez permiten copiarla, y el
valor que necesita un proyecto es el de la hoja con la que se especificó, no
el de la revisión de hoy. Así que las hojas de características, las
declaraciones de prestaciones, los informes de ensayo y las medidas tuyas
viven en un archivo de catálogo tuyo, y phonometry.io lee ese archivo en
filas de las mismas clases que entregan todos los catálogos PUBLISHED_*. El
núcleo poroso de tu hoja entra en los modelos de porosos como entra un
material poroso de un libro, un espectro de tu informe de ensayo entra en un
modelo de sala, y todas las búsquedas por nombre buscan en tu catálogo, en el
publicado o en los dos a la vez.
Las filas son tuyas y se quedan en tus objetos: la biblioteca no guarda ninguna copia, no abre ningún enlace que nombre un archivo y no envía nada a ninguna parte. Por la misma razón, una tabla de un fabricante pegada en una issue o en una pull request nunca se incorpora a la biblioteca.
Empezar por una tabla publicada
Sección titulada «Empezar por una tabla publicada»La forma más rápida de ver cómo es un archivo es escribir uno.
write_catalogue escribe cualquier tabla publicada en la forma que lee
read_catalogue, como plantilla para copiar y editar: aquí la espuma de la
Tabla 13.1 de Allard & Atalla.
import jsonimport pathlib
from phonometry import io, materials
allard = { key: row for key, row in materials.PUBLISHED_POROUS.items() if row.table == "allard-2009-table-13-1"}written = io.write_catalogue(allard, "template.json", catalogue="template-allard")print([path.name for path in written]) # ['template.json']
template = json.loads(pathlib.Path("template.json").read_text(encoding="utf-8"))print(list(template))# ['schema', 'schema_version', 'catalogue', 'row_type', 'about', 'provenance',# 'phonometry_version', 'rows']print(template["provenance"]["kind"]) # publicationprint(template["rows"][0]["key"], template["rows"][0]["flow_resistivity_pa_s_m2"])# foam 12569.0Una tabla de filas de una misma clase sale entera, con el libro como
procedencia y el día de la exportación como día de consulta. Lo que la
biblioteca calcula a partir de las celdas impresas (aquí el módulo de
cizalla, a partir del módulo de Young y del coeficiente de Poisson) no se
escribe nunca: se vuelve a calcular al leer el archivo. Si ya hay un archivo
en esa ruta, se rechaza con FileExistsError salvo que pases
overwrite=True.
El documento
Sección titulada «El documento»Un archivo de catálogo es un único objeto JSON, en UTF-8, con un conjunto cerrado de claves en el primer nivel:
| Clave | Qué guarda |
|---|---|
schema, schema_version | "phonometry-catalogue" y 1. Una versión posterior se rechaza con «upgrade phonometry». |
catalogue | El nombre del catálogo, la primera mitad de cada clave: hasta 64 caracteres entre a-z, 0-9, ., _ y -. |
row_type | El nombre de la clase de fila. Se compara con la clase que pasas y nunca se importa. |
about | Qué es el documento, cómo lo leíste y en qué unidades imprime. |
provenance | El documento: son obligatorios su tipo en kind, su título en document, su versión en version (o null si no imprime ninguna) y el día en que lo consultaste en consulted, como AAAA-MM-DD. El tipo es uno de io.PROVENANCE_KINDS: datasheet, declaration_of_performance, test_report, measurement, calculation, publication u other. |
basis | Opcional: qué dice el documento que es cada valor, salvo que una fila diga otra cosa. |
attributed_to | Opcional: a quién se atribuye la tabla entera, como un solo texto. Cada fila lo guarda como su crédito "table", y ninguna vuelve a atribuir la tabla. |
conventions | Opcional: las notas y leyendas que el documento imprime para toda la tabla. |
phonometry_version | Opcional: la versión de la biblioteca que escribió el archivo, que rellena write_catalogue. No se lee nunca. |
$schema | Opcional: dónde encuentra un editor el esquema JSON del documento (véase Autocompletado en tu editor). No se lee nunca. |
rows | Las filas, cada una con una clave propia en key y un nombre en name. |
Aquí va una hoja de características ficticia con dos filas. La primera es la resistividad al flujo que la hoja declara bajo su marcado CE, como cota inferior; la segunda es la caracterización de laboratorio que cita la página 3, con su propio informe.
panel_40 = """{ "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "panel-40", "row_type": "PorousMaterial", "about": "Core of the Panel 40 absorber as its data sheet gives it. Page 2 declares the airflow resistivity class under CE marking; page 3 quotes the laboratory characterisation of a 40 mm specimen.", "provenance": { "kind": "datasheet", "document": "Panel 40 technical data sheet", "publisher": "Example Acoustics Ltd", "version": "Rev. 4", "issued": "2026-03", "consulted": "2026-09-23", "page": "2" }, "basis": "declared", "rows": [ { "key": "core-declared", "name": "Panel 40 core", "variant": "as declared", "thickness_mm": 40, "frame_density_kg_m3": 40, "ranges": {"flow_resistivity_kpa_s_m2": [5, null]}, "bounded_below": ["flow_resistivity_kpa_s_m2"], "note": "the sheet prints the designation code AFr5" }, { "key": "core-lab", "name": "Panel 40 core", "variant": "40 mm specimen", "provenance": { "page": "3", "printed_table": "Table 2", "laboratory": "Example Lab", "report": "26-014", "test_date": "2025-11-04", "test_standard": "ISO 9053-1:2018" }, "basis": {"row": "measured", "poisson_ratio": "estimated"}, "flow_resistivity_pa_s_m2": 12500, "uncertainty": {"flow_resistivity_pa_s_m2": 900}, "porosity": 0.97, "tortuosity": 1.02, "approximate": ["tortuosity"], "viscous_length_um": 95, "thermal_length_um": 190, "frame_density_kg_m3": 40, "thickness_mm": 40, "youngs_modulus_pa": 140000, "poisson_ratio": 0.0, "unquantified": {"structural_loss_factor": "n.m."}, "x-product-code": "P40-C" } ]}"""pathlib.Path("panel-40.json").write_text(panel_40, encoding="utf-8")
mine = io.read_catalogue("panel-40.json", row_type=materials.PorousMaterial)print(list(mine)) # ['panel-40/core-declared', 'panel-40/core-lab']
declared = mine["panel-40/core-declared"]lab = mine["panel-40/core-lab"]print(lab.source)# Panel 40 technical data sheet (Example Acoustics Ltd), Rev. 4, p. 3, Table 2;# report 26-014 (Example Lab); consulted 2026-09-23print(lab.basis_of("porosity"), lab.basis_of("poisson_ratio")) # measured estimatedprint(declared.basis_of("frame_density_kg_m3")) # declaredprint(declared.ranges, declared.converted)# {'flow_resistivity_pa_s_m2': (5000.0, None)}# {'flow_resistivity_pa_s_m2': ('5', 'kPa s/m2')}print(mine.extras["panel-40/core-lab"]["x-product-code"]) # P40-CCada clave es "<catalogue>/<key>", la forma que usan todos los catálogos
publicados, y el table de cada fila es el nombre del catálogo. El nombre de
un catálogo no puede tener nunca la forma del de una tabla empaquetada, un año
de cuatro cifras seguido de una palabra (allard-2009-table-13-1), así que una
clave tuya y una clave de la biblioteca no coinciden nunca, ni hoy ni después
de que una actualización añada una tabla: acme-2026-rev4 se rechaza y
acme-rev4-2026 no.
Una fila puede acotar la procedencia del documento a su propia página, tabla,
laboratorio, acreditación, informe, fecha de ensayo y norma, como hace
core-lab, y una norma dada para un solo campo (field_test_standards) se
suma a las del documento. Un documento distinto es otro archivo: una fila no
puede cambiar el documento, su tipo ni su versión. Para todo tipo salvo
publication, que se cita como se cita su documento, el source de la fila
nombra el documento, su editor y su versión, la página y la tabla, el informe
y el laboratorio, y el día de la consulta; la acreditación, la fecha de
ensayo y las normas se quedan en row.provenance. Un rechazo nombra el
documento por su tipo: «the datasheet», «the declaration of performance»,
«the test report», «the measurement record» para measurement, «the
calculation note» para calculation, y «the source» para publication u
other.
Las columnas propias van bajo un nombre que empieza por x-: se guardan, como
texto, en Catalogue.extras y no intervienen en ningún cálculo. Una fila
nunca escribe derived, table, source ni estimated: las tres primeras
las escribe la biblioteca, y una estimación es una basis.
Cuando el archivo está mal
Sección titulada «Cuando el archivo está mal»Todos los problemas de forma de un archivo se comunican a la vez, en un solo
io.CatalogueError, cada uno un io.CatalogueIssue con el archivo, su lugar
como puntero JSON, la fila y el campo. Aquí la porosidad está escrita con coma
decimal y el espesor en pulgadas, una unidad que el lector no convierte:
broken = panel_40.replace('"porosity": 0.97', '"porosity": "0,97"')broken = broken.replace('"thickness_mm": 40,\n "youngs', '"thickness_inch": 1.6,\n "youngs')try: io.parse_catalogue(broken, row_type=materials.PorousMaterial, label="panel-40.json")except io.CatalogueError as error: for issue in error.issues: print(issue)# panel-40.json: /rows/1/porosity (row 'core-lab'): expected a number, got the# text '0,97'; JSON numbers use a decimal point# panel-40.json: /rows/1/thickness_inch (row 'core-lab'): 'inch' is not a unit# this reader converts; write thickness_mm (mm), thickness_cm (cm),# thickness_m (m) or thickness_um (µm)parse_catalogue lee lo mismo de un texto o de un diccionario que ya está en
memoria, y label nombra el documento en cada problema. Un campo mal escrito
se contesta con el campo al que más se parece; una fila bien escrita que
incumple el contrato que cumple toda fila (una densidad negativa, una
porosidad mayor que 1, un valor junto a una palabra que dice que no lo hay) se
comunica con la primera regla que incumple. El lector rechaza un NaN o un
Infinity, un nombre escrito dos veces en un mismo objeto, un carácter de
control o una marca que reordena el texto, cualquier cosa que no sea un
archivo regular con el nombre de un catálogo o de su cabecera (una tubería, un
dispositivo, un socket o un directorio, rechazados antes de abrirlos), un
archivo de más de 16 MiB (comprobado antes de leer un solo byte), una cabecera
de archivo CSV de más de 64 KiB, más de 50 000 filas y un anidamiento de más de
seis niveles.
Lo que solo merece una segunda mirada no es un error. Un valor cuya base es
measured sin informe ni laboratorio que lo respalden, o una palabra en lugar
de un número tan larga que es una frase, se guarda tal como lo escribe el
archivo, se anota en Catalogue.notes y se anuncia una sola vez con un
io.CatalogueWarning. Una nota nunca cambia una fila.
Usar las filas
Sección titulada «Usar las filas»Una fila leída de un archivo es de la misma clase, y se construye de la misma manera, que una publicada: la aceptan los mismos métodos, y lo que devuelven lo aceptan las mismas funciones.
En los modelos
Sección titulada «En los modelos»printed() da un número que imprime el documento, o lo rechaza con lo que el
documento tiene en su lugar. Es la manera de pasar una celda a cualquier
función que reciba un float. PorousMaterial.medium() lee sus cinco
parámetros del mismo modo, así que la fila de laboratorio entra en el modelo
de matriz de transferencia y la fila declarada se rechaza en los términos de
la propia hoja:
import numpy as np
print(lab.printed("flow_resistivity_pa_s_m2")) # 12500.0
frequencies = np.array([250.0, 500.0, 1000.0, 2000.0])core = materials.PorousLayer(thickness=0.040, medium=lab.medium(frequencies))print(materials.layered_absorber(frequencies, [core]).absorption.round(2))# [0.15 0.4 0.73 0.98]
try: declared.medium(frequencies)except ValueError as error: print(error)# 'Panel 40 core' has no flow_resistivity_pa_s_m2, which 'johnson_champoux_allard'# needs: the datasheet prints a lower bound of 5 kPa s/m2 (5000 Pa s/m2) and no# value (Panel 40 technical data sheet (Example Acoustics Ltd), Rev. 4, p. 2;# consulted 2026-09-23).El rechazo cita primero la cifra y la unidad que imprime la hoja, y después el valor que guarda la fila: una cota inferior AFr5 no es una resistividad al flujo de 5000 Pa·s/m², y aquí nada finge que lo sea.
Banda a banda, hacia un modelo de sala
Sección titulada «Banda a banda, hacia un modelo de sala»Una fila que imprime un valor por banda se lee en las frecuencias que pide un
modelo con values_at(), que rechaza una banda que el documento no imprime en
lugar de leerla como cero. Las funciones que reciben sus coeficientes por
posición, como room.sabine_reverberation_time, reciben un array en el que
cada valor está impreso:
from phonometry import room
tiles = io.parse_catalogue( { "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "ceiling-tiles", "row_type": "AbsorptionSpectrum", "about": "Octave-band Sabine coefficients a fictitious data sheet prints.", "provenance": { "kind": "datasheet", "document": "Example tile data sheet", "publisher": "Example Acoustics Ltd", "version": "Rev. 1", "consulted": "2026-09-25", "laboratory": "Example Lab", "report": "26-031", "test_standard": "ASTM C423-17", }, "basis": "measured", "rows": [ { "key": "tile-e400", "name": "Example ceiling tile", "mounting": "E400", "absorption_coefficient_125": 0.45, "absorption_coefficient_250": 0.62, "absorption_coefficient_500": 0.78, "absorption_coefficient_1000": 0.90, "absorption_coefficient_2000": 0.94, "absorption_coefficient_4000": 0.91, } ], }, row_type=materials.AbsorptionSpectrum,)tile = tiles["ceiling-tiles/tile-e400"]bands = np.array([125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0])t60 = room.sabine_reverberation_time(180.0, [(60.0, tile.values_at(bands)), (190.0, 0.04)])print(t60.round(2)) # [0.84 0.65 0.53 0.47 0.45 0.47]
try: tile.values_at([63.0, 125.0])except ValueError as error: print(error)# 'Example ceiling tile' has no absorption_coefficient_63, which 'the 63 Hz band'# needs: the datasheet does not give it, and it does not follow from the cells# that it does (Example tile data sheet (Example Acoustics Ltd), Rev. 1;# report 26-031 (Example Lab); consulted 2026-09-25).El método de la meseta
Sección titulada «El método de la meseta»building.plateau_transmission_loss admite una fila PlateauMaterial como
material, una del libro o una tuya, y lee sus tres números a través de
printed(). Un número que pases explícitamente sustituye al de la fila, que
entonces ni siquiera se lee.
from phonometry import building, solids
boards = io.parse_catalogue( { "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "boards", "row_type": "PlateauMaterial", "about": "Plateau-method constants of a fictitious board, as its data sheet prints them.", "provenance": { "kind": "datasheet", "document": "Example board data sheet", "version": "Rev. 2", "consulted": "2026-09-25", }, "rows": [ { "key": "board", "name": "Example board", "surface_density_per_mm_kg_m2": 0.9, "coincidence_height_db": 28, "plateau_frequency_ratio": 7, } ], }, row_type=solids.PlateauMaterial,)board = boards["boards/board"]tl = building.plateau_transmission_loss(bands, material=board, thickness_mm=12.5)print(round(tl.plateau_start), round(tl.plateau_end)) # 522 3656print(tl.transmission_loss.round(1)) # [15.6 21.6 27.6 28. 28. 29.3]Un suelo flotante: s’ y s’t
Sección titulada «Un suelo flotante: s’ y s’t»La hoja de características de una capa resiliente imprime una de dos magnitudes
distintas. La rigidez dinámica de la capa instalada es la que usa la
frecuencia natural de un suelo flotante; la rigidez dinámica aparente
es la que mide la EN 29052-1 sobre una probeta, y solo se
convierte en a través de la resistividad al flujo lateral del
apartado 8.2. ResilientLayer las guarda en dos campos,
dynamic_stiffness_n_m3 y apparent_dynamic_stiffness_n_m3, y un «s’ ≤ 9 MN/m³»
declarado es una cota, no una rigidez:
layers = io.parse_catalogue( { "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "underlays", "row_type": "ResilientLayer", "about": "Two underlays of a fictitious data sheet, as it prints them.", "provenance": { "kind": "datasheet", "document": "Example underlay data sheet", "publisher": "Example Acoustics Ltd", "version": None, "consulted": "2026-09-25", }, "basis": "declared", "rows": [ { "key": "declared", "name": "Underlay 8", "thickness_mm": 8, "ranges": {"dynamic_stiffness_mn_m3": [None, 9]}, "bounded_above": ["dynamic_stiffness_mn_m3"], }, { "key": "tested", "name": "Underlay 20", "thickness_mm": 20, "apparent_dynamic_stiffness_mn_m3": 7, "basis": {"row": "measured"}, "provenance": {"laboratory": "Example Lab", "report": "26-044"}, }, ], }, row_type=materials.ResilientLayer,)try: layers["underlays/declared"].natural_frequency(mass_per_area_kg_m2=100.0)except ValueError as error: print(error)# 'Underlay 8' has no dynamic_stiffness_n_m3, which 'the natural frequency of# EN 29052-1 Formula 2' needs: the datasheet prints an upper bound of 9 MN/m3# (9000000 N/m3) and no value (Example underlay data sheet (Example Acoustics# Ltd), no version printed; consulted 2026-09-25).
tested = layers["underlays/tested"]f0 = tested.natural_frequency(mass_per_area_kg_m2=100.0, airflow_resistivity_pa_s_m2=150_000.0)print(round(float(f0), 1)) # 42.1La capa ensayada solo da , así que su frecuencia natural
necesita la resistividad al flujo lateral del apartado 8.2: a partir de
100 kPa·s/m², es , y por debajo la llamada necesita además
la rigidez del gas encerrado de la Fórmula 7, que se pasa con
gas_stiffness_n_m3. Sin la llamada se rechaza con esa explicación en
lugar de tomar una magnitud por la otra.
Un sólido en la simulación elástica
Sección titulada «Un sólido en la simulación elástica»La simulación elástica acepta la fila de un sólido allí donde acepta un
Material: simulation.scholte_speed y ElasticFDTD2D.from_regions leen
los tres números que integra el FDTD elástico: la velocidad longitudinal de
un sólido extenso, la velocidad transversal y la densidad. Una hoja que
imprime un módulo, un coeficiente de Poisson y una densidad ha impreso
también esas velocidades, y la fila las guarda marcadas como derivadas. Las
velocidades de placa y de barra que una página imprime a su lado no se leen
nunca: el FDTD elástico no integra ninguna de las dos, y una velocidad de
placa tomada por la de un sólido extenso se equivoca en torno a un diez por
ciento.
from phonometry import simulation, solids
alloys = io.parse_catalogue( { "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "alloys", "row_type": "SolidMaterial", "about": "Elastic constants of two fictitious alloys, as their data sheet prints them.", "provenance": { "kind": "datasheet", "document": "Example alloy data sheet", "version": "Rev. 1", "consulted": "2026-09-25", }, "rows": [ { "key": "plate", "name": "Example alloy", "youngs_modulus_gpa": 70, "poisson_ratio": 0.33, "density_kg_m3": 2700, }, { "key": "sheet", "name": "Example sheet", "plate_longitudinal_speed_m_s": 5400, "density_kg_m3": 2700, }, ], }, row_type=solids.SolidMaterial,)alloy = alloys["alloys/plate"]print(round(alloy.bulk_longitudinal_speed_m_s), round(alloy.transverse_speed_m_s))# 6198 3122print(round(simulation.scholte_speed(simulation.WATER, alloy), 1)) # 1476.6
try: simulation.scholte_speed(simulation.WATER, alloys["alloys/sheet"])except ValueError as error: print(error)# 'Example sheet' has no bulk_longitudinal_speed_m_s, which 'the elastic FDTD'# needs: the datasheet does not give it, and it does not follow from the cells# that it does (Example alloy data sheet, Rev. 1; consulted 2026-09-25).Un suelo en los modelos de exteriores
Sección titulada «Un suelo en los modelos de exteriores»Una fila GroundSurface, de los suelos publicados o de los tuyos, entra en
los modelos de exteriores a través de medium(): el semiespacio poroso, por
el modelo de Delany y Bazley o por el de Miki, que
environment.ground_effect recibe como impedance,
barrier_insertion_loss como ground_impedance y
atmospheric_parabolic_equation como impedance. La resistividad se lee a
través de printed(), así que el intervalo de un pastizal se rechaza en
lugar de promediarse. Una resistividad al flujo efectiva es un parámetro del
modelo con el que se ajustó, así que una fila que la página marca como
ajustada con un modelo solo entra en ese modelo:
from phonometry import environment
site = io.parse_catalogue( { "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "site-survey", "row_type": "GroundSurface", "about": "An effective flow resistivity fitted to a site's own level differences.", "provenance": { "kind": "measurement", "document": "Site survey 12", "version": None, "consulted": "2026-09-25", "laboratory": "Example Lab", "report": "26-051", }, "basis": "measured", "rows": [{"key": "pasture", "name": "Pasture", "flow_resistivity_kpa_s_m2": 180}], }, row_type=environment.GroundSurface,)pasture = site["site-survey/pasture"]octaves = np.array([125.0, 250.0, 500.0, 1000.0])ground = pasture.medium(octaves, model="miki")over_pasture = environment.ground_effect(octaves, 1.0, 1.5, 50.0, impedance=ground)print(over_pasture.excess_attenuation.round(1)) # [ 3.6 -4.2 -12.8 -2.8]
fitted = environment.PUBLISHED_GROUND["cox-2017-table-6-7/sports_field_semi_phenomenological"]try: fitted.medium(octaves)except ValueError as error: print(error)# 'Sports field' is the row the page marks “Fitted using semi-phenomenological# model” (Cox & D'Antonio 3e Table 6.7, PDF pages 257-258 (printed pp.# 200-201)): its effective flow resistivity is a parameter of that model and# not of 'delany_bazley'; neither 'delany_bazley' nor 'miki' is that model, or# take row.printed('flow_resistivity_pa_s_m2') into another model yourself.Un suelo exterior es mucho más resistivo que los materiales fibrosos con los
que se ajustaron los dos modelos, así que todas las octavas de aquí quedan por
debajo de su rango de ajuste publicado, y la llamada emite un
PorousAbsorberWarning que dice que la regresión es ahí una extrapolación;
Elección de la impedancia del suelo
explica lo que eso cuesta y por qué Miki es el modelo que conviene preferir por
debajo del rango.
Una pared en el modelo detallado de edificación
Sección titulada «Una pared en el modelo detallado de edificación»building.HomogeneousElement.from_solid construye el elemento de la
ISO 12354-1 que forma una capa de un sólido, a partir de la densidad y la
velocidad de placa de la fila, que es la velocidad de fase cuasilongitudinal
de la Tabla B.3 de la norma, y del espesor que le das:
y .
El factor de pérdidas interno lo das tú y nunca se lee de la fila, que puede
guardar cuatro (a flexión, longitudinal, in situ y uno que la página no
precisa), porque solo tú sabes cuál de ellos, si alguno, es el
de la norma:
from phonometry import building
blocks = io.parse_catalogue( { "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "blocks", "row_type": "SolidMaterial", "about": "A fictitious calcium-silicate block, as its data sheet prints it.", "provenance": { "kind": "datasheet", "document": "Example block data sheet", "version": "Rev. 3", "consulted": "2026-09-25", }, "basis": "declared", "rows": [ { "key": "block", "name": "Example block", "density_kg_m3": 1800, "plate_longitudinal_speed_m_s": 2500, "flexural_loss_factor": 0.012, } ], }, row_type=solids.SolidMaterial,)wall = building.HomogeneousElement.from_solid( blocks["blocks/block"], thickness_m=0.2, internal_loss_factor=0.01, area_m2=13.75, length1_m=5.0, length2_m=2.75,)print(round(wall.mass_per_area, 1), round(wall.critical_frequency, 1)) # 360.0 128.4print(wall.internal_loss_factor) # 0.01, no el 0.012 a flexión de la filaSon los números de las paredes interiores del edificio resuelto del
Anexo L de la ISO 12354-1, 200 mm de bloques de silicato cálcico con
360 kg/m² y 128,4 Hz, y el elemento entra en building.in_situ_element como
uno construido a mano.
Buscar en tu catálogo y en los publicados
Sección titulada «Buscar en tu catálogo y en los publicados»Todas las búsquedas *_named admiten catalogue=, las filas en las que
buscar en lugar de su propio PUBLISHED_*. PUBLISHED_POROUS | mine une los
dos en un único mapping de solo lectura que rechaza una clave que tengan los
dos, así que una sola búsqueda contesta desde ambos:
both = materials.PUBLISHED_POROUS | minefor row in materials.porous_materials_named("panel 40 core", catalogue=both): print(row.table, row.variant)# panel-40 as declared# panel-40 40 mm specimenprint(len(materials.porous_materials_named("Foam", catalogue=both))) # 5Cada búsqueda conserva su propia forma de casar un nombre, contesta con todas
las coincidencias en el orden en que las guarda el catálogo y nunca elige una.
Casa sin distinguir mayúsculas ni la forma en que se guarda una letra
acentuada: un nombre pegado de un documento que guarda «é» como «e» seguida
de un acento combinante encuentra la fila escrita con «é», y al revés. Una
fila de otra clase dentro del catálogo se rechaza con TypeError, que nombra
su clave.
Escribir lo que imprime el documento
Sección titulada «Escribir lo que imprime el documento»Un documento imprime más que números, y un archivo escribe cada cosa como lo que es, con los mismos matices que usan las tablas publicadas. El sujeto de cada frase de abajo es el tipo de documento: «the datasheet», «the test report».
| El documento imprime | Se escribe en el archivo como | why_missing contesta |
|---|---|---|
| un número | el campo, en su unidad o en otra de la misma magnitud | nada: el valor está |
| nada | el campo omitido, o null | «the datasheet does not give it, and it does not follow from the cells that it does» |
~0,85 | el valor, y el campo en "approximate" | nada |
≤ 30 o < 30 | "ranges": {"f": [null, 30]} y "bounded_above": ["f"] | «the datasheet prints an upper bound of 30 and no value» |
≥ 5 o > 5 | "ranges": {"f": [5, null]} y "bounded_below": ["f"] | «… a lower bound of 5 and no value» |
0,30 a 0,50 | "ranges": {"f": [0.30, 0.50]} | «the datasheet prints 0.3 to 0.5 and no value» |
| varias lecturas y ningún valor único | "reported": {"f": [0.30, 0.35, [0.30, 0.40]]} | «the datasheet lists 0.3, 0.35, 0.3 to 0.4 and no single value» |
0,85 ± 0,05 | el valor, y "uncertainty": {"f": 0.05} | nada |
| una palabra o un código donde iría el número | "unquantified": {"f": "AFr5"} | «the datasheet prints “AFr5” where the number would be» |
| un valor que sabes que está mal | "misprinted": {"f": "por qué"}, sin valor | tu motivo, tal como lo escribiste |
| un valor al que llega la aritmética pero que no debe calcularse | "not_derivable": {"f": "por qué"} | tu motivo |
| una celda en blanco que el documento da por referencia a otra fila | el valor, y "carried": {"f": "de la fila de arriba"} | nada |
| una cifra en una unidad que no está en ninguna familia | el valor en la unidad del propio campo, y "converted": {"f": ["3e5", "psi"]} | nada, y una cota cita primero la cifra |
| el crédito a un laboratorio o a un autor | "attributed_to": {"f": "..."}, o "row", o "table"; o el de la tabla una sola vez, como attributed_to del propio documento | no se pregunta |
| un sí o un no | true o false, en un campo que guarde uno | no se pregunta |
Una cota declarada bajo un marcado CE, AFr5 para una resistividad al flujo
de al menos 5 kPa·s/m², se escribe como la cota, con el código en note, como
hace core-declared. Escrita como el número 5, afirmaría una medida que la
hoja no hace.
Unidades
Sección titulada «Unidades»Una celda puede escribirse en la unidad que lleva el nombre de su campo, o en
otra unidad de la misma magnitud, con el mismo nombre y el sufijo de la otra
unidad: thickness_cm en lugar de thickness_mm. La cifra se convierte sobre
los dígitos que escribe el archivo, con el factor exacto, y se redondea una
sola vez, así que 0,067 GPa son 67 000 000 Pa, cosa que no da el producto de
dos flotantes. converted guarda la cifra y la unidad exactamente como las
escribe el archivo.
| Magnitud | Sufijos |
|---|---|
| Longitud | _um, _mm, _cm, _m |
| Masa por unidad de superficie | _g_m2, _kg_m2 |
| Densidad | _g_cm3, _kg_m3 |
| Presión y módulo | _pa, _kpa, _mpa (megapascales), _n_mm2, _gpa |
| Rigidez por unidad de superficie | _n_m3, _mn_m3 |
| Resistividad al flujo | _pa_s_m2, _kpa_s_m2 |
| Temperatura | _c, _k, desplazada por su origen en un valor, un intervalo y una lectura, y solo por el factor en un más-menos |
| Área de absorción | _m2, _ft2, _ft2_per_1000_ft3, solo en AbsorptionAreaSpectrum, y la última solo con per al lado |
La unidad se reconoce por la coincidencia más larga con todas las unidades que usan los campos de la biblioteca, así que una unidad compuesta nunca se toma por una longitud: una resistencia específica al flujo en Pa·s/m o una tasa por metro no admiten otra unidad, y un nombre que podrían tomar dos campos de la misma magnitud se rechaza por ambiguo. Una unidad que no está en la tabla se rechaza con las grafías que admite el campo. Todos los números de una misma celda se escriben en una sola unidad.
Desde una hoja de cálculo: un archivo CSV
Sección titulada «Desde una hoja de cálculo: un archivo CSV»Una hoja de características se teclea más a menudo en una hoja de cálculo que
en un archivo JSON, y una hoja de cálculo guarda CSV. Un catálogo puede ser
uno: las filas van en el archivo CSV, cada una empezando en una línea propia
bajo una primera línea que nombra las columnas, y el documento sin sus filas
va en una cabecera JSON al lado, con el nombre del archivo CSV seguido de
.phonometry.json. La cabecera declara cómo escribe el archivo sus celdas,
"csv": {"delimiter": ";", "decimal": ","} para una hoja de cálculo
configurada con coma decimal, y nada del dialecto se adivina a partir del
archivo. write_catalogue escribe el par cuando el nombre termina en .csv,
así que una tabla publicada vuelve a ser la plantilla más rápida, aquí los
coeficientes de absorción de la Tabla 6.1 de Arau-Puchades (1999):
arau = { key: row for key, row in materials.PUBLISHED_ABSORPTION.items() if row.table == "arau-1999-table-6-1"}written = io.write_catalogue( arau, "template.csv", catalogue="template-arau", delimiter=";", decimal=",")print([path.name for path in written])# ['template.csv', 'template.csv.phonometry.json']
lines = pathlib.Path("template.csv").read_text(encoding="utf-8-sig").splitlines()print(lines[0].split(";")[:5])# ['key', 'name', 'variant', 'note', 'absorption_coefficient_125']print(lines[1].split(";")[:5])# ['01_pared_de_ladrillo', 'Pared de ladrillo', '', '', '0,025']header = json.loads(pathlib.Path("template.csv.phonometry.json").read_text("utf-8"))print(header["csv"]) # {'delimiter': ';', 'decimal': ','}Abre el archivo en una hoja de cálculo, edítalo y guárdalo como «CSV UTF-8».
El lector admite UTF-8 con o sin la marca de orden de bytes que escribe ahí la
hoja de cálculo, y rechaza cualquier otra codificación con ese consejo.
Conserva la cabecera al lado del archivo, con el delimitador y la marca
decimal con los que guardó la hoja de cálculo. El escritor pone un apóstrofo
delante de un texto que una hoja de cálculo ejecutaría como fórmula (el que
empieza por =, +, -, @, un tabulador o un retorno de carro), y el
lector lo vuelve a quitar. Un archivo CSV y su cabecera son dos archivos: cada
uno se escribe entero, pero no los dos como uno solo.
Cada celda numérica guarda una sola cosa, con una gramática cerrada que escribe los mismos matices que el documento JSON:
| La celda | Lo que dice |
|---|---|
| vacía | el documento no imprime nada ahí |
0,85 | un valor, con la marca decimal que declara la cabecera |
~0,85 | un valor aproximado |
<=30, <30 o ≤30 | una cota superior y ningún valor |
>=5, >5 o ≥5 | una cota inferior y ningún valor |
0,30..0,50 | un intervalo y ningún valor; ~0,30..0,50 si es aproximado |
0,85±0,05 o 0,85+/-0,05 | un valor y su más-menos |
[AFr5] | lo que imprime el documento donde iría el número |
true o false | un sí o un no, en una columna que guarde uno |
Un número nunca lleva separador de miles, y su signo menos puede ser el
tipográfico. Las columnas son key, los campos de la clase de fila (en la
unidad que lleva el nombre de cada uno, o en otra de la misma magnitud, como
flow_resistivity_kpa_s_m2), basis para la fila entera (una celda vacía
toma la del documento), la página, la tabla, el laboratorio, la acreditación,
el informe, la fecha de ensayo y la norma de la fila como provenance.page,
provenance.printed_table, provenance.laboratory,
provenance.accreditation, provenance.report, provenance.test_date y
provenance.test_standard, el crédito de la fila entera como
attributed_to.row, y columnas propias con nombre x-.... El crédito de la
tabla entera va una sola vez en la cabecera, como "attributed_to": "...",
igual que en un documento JSON. Una gama ficticia de placas tecleada en una
hoja:
header = { "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "tile-range", "row_type": "AbsorptionSpectrum", "about": "Octave-band Sabine coefficients of a fictitious tile range, typed from its data sheet.", "provenance": { "kind": "datasheet", "document": "Example tile range data sheet", "publisher": "Example Acoustics Ltd", "version": "Rev. 2", "consulted": "2026-09-25", "laboratory": "Example Lab", }, "basis": "measured", "csv": {"delimiter": ";", "decimal": ","},}pathlib.Path("tiles.csv.phonometry.json").write_text(json.dumps(header), encoding="utf-8")pathlib.Path("tiles.csv").write_text( "key;name;mounting;absorption_coefficient_125;absorption_coefficient_250;" "absorption_coefficient_500;absorption_coefficient_1000;" "absorption_coefficient_2000;absorption_coefficient_4000;provenance.report;x-code\n" "e400;Tile 15;E400;0,45;~0,62;0,78±0,03;0,90;0,94;0,91;26-031;T15\n" "a;Tile 15;A;[n.m.];0,25..0,30;0,55;0,80;>=0,85;0,80;26-032;T15\n", encoding="utf-8",)sheet = io.read_catalogue("tiles.csv", row_type=materials.AbsorptionSpectrum)e400, mounted_a = sheet["tile-range/e400"], sheet["tile-range/a"]print(e400.absorption_coefficient_1000, e400.approximate, e400.uncertainty)# 0.9 frozenset({'absorption_coefficient_250'}) {'absorption_coefficient_500': 0.03}print(mounted_a.ranges)# {'absorption_coefficient_250': (0.25, 0.3), 'absorption_coefficient_2000': (0.85, None)}print(mounted_a.why_missing("absorption_coefficient_2000"))# the datasheet prints a lower bound of 0.85 and no valueprint(mounted_a.source)# Example tile range data sheet (Example Acoustics Ltd), Rev. 2; report 26-032# (Example Lab); consulted 2026-09-25print(sheet.extras["tile-range/a"]["x-code"]) # T15Las filas pasan las mismas comprobaciones que las de un documento JSON, y
todos los problemas se comunican a la vez en su línea y su columna, con las
letras con que las nombra una hoja de cálculo. Una columna que el lector no
conoce se contesta con el nombre más parecido, y un texto donde va un número
nunca se lee como una palabra, un NaN o un cero:
pathlib.Path("tiles.csv").write_text( "key;name;absorption_coefficent_500;absorption_coefficient_1000\n" "e400;Tile 15;0,78;0.^G\n", encoding="utf-8",)try: io.read_catalogue("tiles.csv", row_type=materials.AbsorptionSpectrum)except io.CatalogueError as error: for issue in error.issues: print(issue)# tiles.csv, line 1, column C: no field 'absorption_coefficent_500' on# AbsorptionSpectrum; did you mean 'absorption_coefficient_500'?# tiles.csv, line 2, column D (absorption_coefficient_1000), row 'e400': '0.^G'# is not a number; if the datasheet prints this text where the number would# be, write it as [0.^G]0.^G es lo que puede dejar la capa de texto rota de un PDF: si la página
imprime ahí una palabra, va entre corchetes; si imprime un número, teclea el
número. Un intervalo tecleado con un guion, un más-menos tecleado como +-,
una unidad tecleada detrás del número y un número agrupado en miles reciben
cada uno la forma de escribirlos. Cuando todos los números que fallan están
escritos con la otra marca decimal (un número agrupado en miles, que podría
ser cualquiera de las dos, no cuenta), o la primera línea se parte en
columnas con otro delimitador, el rechazo dice además cuál hay que
declarar.
El crédito también viaja en el archivo: write_catalogue escribe el
crédito de una fila en su columna attributed_to.row, y el que todas las
filas dan a la tabla entera en la cabecera. La Tabla 6.2 de Bies atribuye
su primera fila a Beranek e Hidaka (1998):
bies = { key: row for key, row in materials.PUBLISHED_ABSORPTION.items() if row.table == "bies-2017-table-6-2"}io.write_catalogue(bies, "bies.csv", catalogue="bies")lines = pathlib.Path("bies.csv").read_text(encoding="utf-8-sig").splitlines()print(lines[0].split(",")[-1]) # attributed_to.rowback = io.read_catalogue("bies.csv", row_type=materials.AbsorptionSpectrum)print(back["bies/unoccupied_heavily_upholstered_seats"].attributed_to)# {'row': 'Beranek and Hidaka (1998)'}Lo que no cabe en una celda se escribe en un documento JSON: varias lecturas
sin un valor único, una errata, un valor al que llega la aritmética pero que
no debe calcularse, una celda arrastrada de otra fila, una cifra en una unidad
que no está en ninguna familia, un crédito, una base o una norma para una sola
celda, una cota o un intervalo con más-menos, una cota aproximada, un texto
vacío donde el valor por defecto del campo dice algo y una tabla cuyas filas
la atribuyen por separado. write_catalogue se niega a escribir una tabla
así como CSV, nombra cada celda que no puede guardar con el puntero con que
la escribiría el documento JSON y no escribe nada. La Tabla A2 de Hopkins
imprime la fuente de tres celdas de su fila del acero dentro de la propia
celda, como «5270 (Fahy, 1985)» para la velocidad de placa: un crédito para
una sola celda, así que esa fila solo va a JSON:
from phonometry import solids
steel = "hopkins-2007-table-a2/steel"try: io.write_catalogue( {steel: solids.PUBLISHED_SOLIDS[steel]}, "steel.csv", catalogue="hopkins" )except io.CatalogueError as error: print(error.issues[0].location) # /rows/0/attributed_to/plate_longitudinal_speed_m_sprint(pathlib.Path("steel.csv").exists()) # FalseQué clase de fila guarda cada magnitud
Sección titulada «Qué clase de fila guarda cada magnitud»Una hoja de características y un libro pueden imprimir magnitudes que se
parecen y no son la misma. Las clases de fila las separan, y el archivo no
puede impedir que escribas una en el campo de otra, así que lee esto antes de
elegir un row_type.
AbsorptionSpectrumguarda coeficientes de absorción de Sabine por octavas de 63 Hz a 8 kHz, tal como los imprime la tabla de un libro o tal como una hoja de características imprime por octavas el resultado de un ensayo en cámara reverberante. El coeficiente práctico de absorción sonora de la ISO 11654 no es uno de ellos: es una valoración, redondeada a pasos de 0,05 y limitada a 1,00, y la fila por octavas de una hoja de características europea suele ser eso.PracticalAbsorptionSpectrumguarda yThirdOctaveAbsorptionSpectrumel por tercios de octava de un informe de ensayo, cada uno con la valoración que imprime la hoja; no escribas ninguno de los dos enAbsorptionSpectrum. Cómo leer la hoja de características de un material distingue los tres en la página.TransmissionLossSpectrumguarda la pérdida por transmisión de una construcción tal como la recopila un libro, por bandas de octava, a partir de ensayos que el libro rara vez nombra: Bies califica sus valores de incidencia de campo, las filas de ASHRAE salen de ensayos de laboratorio y Rossing no nombra ninguna fuente. El índice de reducción acústica de un informe de ensayo según la ISO 10140-2 se mide por tercios de octava sobre una probeta de un producto, en un laboratorio donde se ha comprobado que el sonido transmitido por flancos es despreciable, y se valora con la ISO 717-1. Es otro registro: lo guardaSoundReductionSpectrum, con los índices globales que imprime el informe, eImpactImprovementSpectrumguarda la reducción del nivel de impactos de un revestimiento; no escribas ninguno de los dos enTransmissionLossSpectrum.ResilientLayerguarda endynamic_stiffness_n_m3y enapparent_dynamic_stiffness_n_m3. Un informe de ensayo según la EN 29052-1 imprime y, cuando puede, ; una hoja de características a menudo solo imprime «s’». Escribe cada una en su campo.PorousMaterialguarda los parámetros de un esqueleto poroso,PlateauMateriallos tres números del método de la meseta ySolidMateriallas constantes elásticas de un sólido. La página de catálogos publicados muestra cada clase con sus columnas.
Qué es un valor: basis, converted y carried
Sección titulada «Qué es un valor: basis, converted y carried»basis dice qué afirma el documento que es un valor, uno de
io.CATALOGUE_BASES, y basis_of(field) lo contesta para cualquier celda:
| Palabra | Significado |
|---|---|
measured | un resultado de ensayo que da el documento |
declared | un valor, una cota o una clase declarados bajo una norma de producto o un marcado CE |
calculated | una cifra que calculó el propio documento, con el modelo de una norma, un programa o una fórmula |
estimated | una estimación del propio documento |
extended | el resultado de una aplicación extendida de un ensayo |
Una celda sin palabra es una que el documento no califica, que es la respuesta
honesta en la mayoría de las hojas de características. La basis del
documento es la de todas sus filas, la entrada "row" de una fila es la de
todas sus celdas, y la entrada propia de una celda manda sobre las dos.
basis es lo que dice el documento; derived es lo que calculó esta
biblioteca, a partir de las celdas impresas, al construir la fila. Son
independientes: el módulo de cizalla de core-lab es derivado, y su texto
en derived nombra las bases en las que se apoya, un módulo de Young medido y
un coeficiente de Poisson estimado. converted guarda la cifra y la unidad de
un valor convertido desde otra unidad, y carried dice de dónde da el
documento una celda en blanco. Un archivo puede escribir converted y
carried junto a un valor; derived, nunca.
Editar una fila
Sección titulada «Editar una fila»Una fila es inmutable. Para cambiar una celda, toma las celdas que imprime el
documento con printed_fields(), cambia una y vuelve a construir la fila con
from_printed, que vuelve a calcular lo que se sigue de ellas.
dataclasses.replace copia los valores derivados tal como estaban, junto a
una celda de la que ya no se siguen:
cells = lab.printed_fields()cells["youngs_modulus_pa"] = 200_000.0edited = materials.PorousMaterial.from_printed(**cells)print(lab.shear_modulus_pa, edited.shear_modulus_pa) # 70000.0 100000.0Una clase de fila propia
Sección titulada «Una clase de fila propia»Una magnitud que no guarda ninguna clase de la biblioteca puede vivir igual en
un archivo de catálogo, en una clase de fila propia: una dataclass inmutable y
keyword-only construida sobre io.CatalogueRow, con cada campo anotado como
float | None, int | None, bool, str, frozenset[str] o un Mapping
de ellos. El lector la lee como cualquier otra, y las demás unidades de una
magnitud se aplican también a tus campos cuando el nombre no deja lugar a
dudas, salvo una temperatura, que se escribe en la unidad que nombra su
campo:
import dataclasses
@dataclasses.dataclass(frozen=True, kw_only=True)class Plasterboard(io.CatalogueRow): surface_density_kg_m2: float | None = None reaction_to_fire: str = ""
boards_of_mine = io.parse_catalogue( { "schema": "phonometry-catalogue", "schema_version": 1, "catalogue": "plasterboards", "row_type": "Plasterboard", "about": "One board of a fictitious data sheet.", "provenance": { "kind": "datasheet", "document": "Example board range", "version": None, "consulted": "2026-09-25", }, "rows": [ { "key": "board-12", "name": "Board 12.5", "surface_density_g_m2": 8600, "reaction_to_fire": "A2-s1,d0", } ], }, row_type=Plasterboard,)row = boards_of_mine["plasterboards/board-12"]print(row.surface_density_kg_m2, row.converted)# 8.6 {'surface_density_kg_m2': ('8600', 'g/m2')}No pongas slots=True en una clase así: en Python 3.12, y en las versiones de
3.13 anteriores a la corrección, falla un método suyo que llame a super() sin
argumentos, que es como un __post_init__ tuyo
llega al contrato de fila. Una anotación float o int a secas se rechaza
con TypeError la primera vez que se construye la clase, porque cualquier
celda de una fila puede faltar. Una clase construida sobre BandedRow no está
soportada: sus bandas se declaran en variables de clase privadas.
Autocompletado en tu editor: el esquema JSON
Sección titulada «Autocompletado en tu editor: el esquema JSON»Un archivo de catálogo se escribe a mano, y un editor que conoce su forma
completa el nombre de un campo mientras lo escribes, ofrece las palabras que
admiten basis y kind y marca un texto donde va un número antes de que el
lector vea el archivo. JSON Schema es la manera en que un editor aprende una
forma, y io.catalogue_schema escribe una, en el dialecto 2020-12, a partir
de las clases de fila que le das: cada campo con su propio nombre y con cada
otra unidad que convierte el lector, los campos que puede nombrar cada
matiz, la procedencia y sus tipos, los límites que impone la unidad de un
campo (una densidad nunca es negativa, una porosidad va de 0 a 1) y la
cabecera de un archivo CSV con su dialecto. Una clase de fila propia se
describe con sus campos:
schema = io.catalogue_schema(materials.PorousMaterial, Plasterboard)print(schema["$id"]) # urn:phonometry:schema:catalogue:1print(schema["properties"]["row_type"]["enum"]) # ['Plasterboard', 'PorousMaterial']board = schema["$defs"]["Plasterboard"]["properties"]print(sorted(name for name in board if name.startswith("surface_density")))# ['surface_density_g_m2', 'surface_density_kg_m2']pathlib.Path("catalogue.schema.json").write_text(json.dumps(schema, indent=2), encoding="utf-8")
pointed = json.loads(panel_40)pointed["$schema"] = "./catalogue.schema.json"print(io.parse_catalogue(pointed, row_type=materials.PorousMaterial) == mine) # TrueUn documento nombra su esquema en una clave $schema de primer nivel, que
el lector admite y no lee nunca: un archivo a su lado, como arriba, o el
esquema de todas las clases de fila que publica la biblioteca, que sirve el
sitio de la documentación en
https://docs.jmrp.io/phonometry/schemas/phonometry-catalogue-1.json.
También se le puede decir a un editor qué archivos son catálogos, y dejarlos
sin esa clave; en Visual Studio Code basta un ajuste:
{ "json.schemas": [ { "fileMatch": ["*.catalogue.json", "*.csv.phonometry.json"], "url": "https://docs.jmrp.io/phonometry/schemas/phonometry-catalogue-1.json" } ]}El esquema exige a un archivo su forma: qué claves pueden tener un
documento, una procedencia y una fila, qué clase de valor admite cada una,
el patrón de un nombre y de una clave. Lo que depende de dos celdas a la
vez, un valor junto a una palabra que dice que no lo hay o una cota en un
campo sin intervalo, lo rechaza el lector, así que un archivo que el editor
acepta aún puede rechazarse, y el rechazo dice dónde. Un archivo que el
lector lee nunca aparece marcado en un editor cuyo validador lee las
expresiones regulares como JavaScript o Python, como hace el de Visual
Studio Code. El nombre del esquema, urn:phonometry:schema:catalogue:1,
sigue a la versión del formato, así que un formato posterior será un esquema
nuevo al lado de este.
Lo que no pasa nunca
Sección titulada «Lo que no pasa nunca»El lector y las filas guardan lo que imprime el documento y nada más.
- Ninguna banda que no imprima el documento se extrapola, se interpola o se rellena con sus vecinas, y ninguna se lee como cero.
- Nada se recorta ni se redondea: un coeficiente de 1,05 sigue siendo 1,05, y una valoración nunca se lleva a una rejilla.
- Ninguna cifra se adivina por su tamaño, y ninguna unidad se lee de dentro de una celda.
- Nada se sube, se guarda en caché ni se copia, y no se abre ningún enlace que nombre un archivo.
- Dos filas con el mismo nombre se guardan las dos y se contestan las dos, y ninguna búsqueda elige entre ellas por ti.
- Nada de lo que nombra el archivo se importa: solo lo leen los lectores de JSON y de CSV de la biblioteca estándar, y el nombre de clase que escribe solo se compara con la clase que pasas.
- Ningún dialecto se adivina: el delimitador y la marca decimal de un archivo CSV son los que declara su cabecera, y nunca se lee un separador de miles.
Qué cubre esta guía
Sección titulada «Qué cubre esta guía»Cubierto
Leer un catálogo propio de un archivo JSON o de un archivo CSV con su cabecera JSON (
io.read_catalogue), o de un texto o un mapping (io.parse_catalogue), en las filas que entregan todos los catálogos publicados, con suio.Provenance, sus notas y todos los problemas de forma comunicados a la vez con un puntero JSON o con una línea y una columna; escribir filas, entre ellas una tabla publicada, de vuelta a un archivo JSON o CSV (io.write_catalogue); buscar en el tuyo y en los publicados con todas las búsquedas*_named; pasar las filas a los modelos de porosos, a un modelo de sala, al método de la meseta, a un suelo flotante, a la simulación elástica, a los modelos de suelo de exteriores y al modelo detallado de la ISO 12354; y el esquema JSON de un archivo de catálogo, para que un editor lo complete (io.catalogue_schema).No cubierto
El formato propio de una hoja de cálculo (XLSX, ODS) no se lee: guarda la hoja como «CSV UTF-8», y escribe en JSON lo que no cabe en una celda. Una valoración que ninguna función de esta biblioteca calcula (un NRC, un STC, un IIC) se guarda tal como se imprime y no se comprueba nunca. La biblioteca no trae datos de ningún fabricante, y una tabla pegada en una issue o en una pull request nunca se incorpora.
Referencias
Sección titulada «Referencias»- International Organization for Standardization. (1989). Acoustics — Determination of dynamic stiffness — Part 1: Materials used under floating floors in dwellings (EN 29052-1:1992 (ISO 9052-1:1989)). La rigidez dinámica s' de una capa instalada y la rigidez dinámica aparente s't de una probeta, dos campos de ResilientLayer, y el apartado 8.2, que convierte la segunda en la primera con la resistividad al flujo lateral.
- International Organization for Standardization. (1997). Acoustics — Sound absorbers for use in buildings — Rating of sound absorption (ISO 11654:1997). El coeficiente práctico de absorción sonora que imprime una hoja de características europea, redondeado a pasos de 0,05 y limitado a 1,00: una valoración de una medida en cámara reverberante y no un coeficiente de Sabine, y por eso no tiene sitio en AbsorptionSpectrum.
- International Organization for Standardization. (2010). Acoustics — Laboratory measurement of sound insulation of building elements — Part 2: Measurement of airborne sound insulation (ISO 10140-2:2010). El índice de reducción acústica R de laboratorio que imprime la hoja de características de un muro o de una puerta: una probeta de un producto, medida por tercios de octava en una instalación donde la transmisión por flancos es despreciable y valorada con la ISO 717-1. No es la pérdida por transmisión que recopila un libro, que es la que guarda TransmissionLossSpectrum.
- International Organization for Standardization. (2017). Building acoustics — Estimation of acoustic performance of buildings from the performance of elements — Part 1: Airborne sound insulation between rooms (ISO 12354-1:2017). El elemento homogéneo que HomogeneousElement.from_solid construye con la fila de un sólido: m' = ρt, y la frecuencia crítica de la Fórmula (B.2) con la velocidad de fase cuasilongitudinal de la Tabla B.3, que es la velocidad de placa de una fila. El ejemplo son las paredes interiores del edificio resuelto del Anexo L.
- Internet Engineering Task Force. (2005). Common Format and MIME Type for Comma-Separated Values (CSV) Files (RFC 4180). El archivo CSV que guarda una hoja de cálculo: un registro por línea terminada en CRLF, el mismo número de campos en todas las líneas, y un campo que contiene un salto de línea, unas comillas dobles o el delimitador entre comillas dobles, con las comillas de dentro duplicadas. El lector admite también el punto y coma o el tabulador como delimitador, según declare la cabecera.
- Internet Engineering Task Force. (2013). JavaScript Object Notation (JSON) Pointer (RFC 6901). Cómo se sitúa cada problema de un archivo: /rows/1/porosity es la porosidad de la segunda fila.
- Internet Engineering Task Force. (2017). The JavaScript Object Notation (JSON) Data Interchange Format (RFC 8259). El texto que es un archivo de catálogo. El lector lo exige al pie de la letra: rechaza un NaN o un Infinity, que solo aceptan algunos lectores, y un nombre escrito dos veces en un mismo objeto, cuyo significado la RFC 8259 deja al lector.
- Wright, A., Andrews, H. y Hutton, B. (2022). JSON Schema Validation: A Vocabulary for Structural Validation of JSON (Internet-Draft draft-bhutton-json-schema-validation-01). Las palabras clave con las que el esquema declara la forma de un archivo de catálogo: los tipos, las palabras que admite una clave, los patrones de un nombre, las longitudes de un texto y los límites que fija la unidad de un campo.
- Wright, A., Andrews, H., Hutton, B. y Dennis, G. (2022). JSON Schema: A Media Type for Describing JSON Documents (Internet-Draft draft-bhutton-json-schema-01). El dialecto 2020-12 en el que io.catalogue_schema escribe el esquema de un archivo de catálogo, y en el que está escrito el esquema publicado.