diff --git a/CHANGELOG.md b/CHANGELOG.md index 95b95ecb..07a70da9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.1] - 2026-07-22 + +This release extends the controlled vocabularies with unit-type information for demand types. The model data itself is unchanged. + +### Added +- **Unit types in the demand-type vocabulary:** the new canonical `EDP_TYPES` mapping records each demand type's short acronym and the physical quantity it is measured in (`UnitType`), and the new `UNIT_TYPES` vocabulary enumerates the valid unit types: `acceleration`, `speed`, `displacement`, `unitless`, `rotation`, `force`, `force_per_length`, and `pressure`. Consumers (e.g., Pelicun 3.10+) can infer physical units for demand data from the vocabulary instead of maintaining hardcoded lists. + +### Changed +- `EDP_to_demand_type` is now derived from `EDP_TYPES`. Its content is identical to before, so existing consumers are unaffected. + +### Distribution +- Version 3.1 is published to PyPI as `simcenter-dlml`. The GitHub repository's `releases/latest` continues to point to v2.1.0 for legacy Pelicun (≤3.9) compatibility. + +--- + ## [3.0] - 2026-07-13 This release turns the library into an installable Python package and adds a programmatic API, a command-line interface, and the DLML Explorer web app. The model data itself is unchanged and remains backward compatible. diff --git a/doc/source/release_notes/index.rst b/doc/source/release_notes/index.rst index 0708d69a..20d3ac24 100644 --- a/doc/source/release_notes/index.rst +++ b/doc/source/release_notes/index.rst @@ -13,6 +13,7 @@ Version 3 .. toctree:: :maxdepth: 2 + v3.1 v3.0 Version 2 diff --git a/doc/source/release_notes/v3.1.rst b/doc/source/release_notes/v3.1.rst new file mode 100644 index 00000000..5003261b --- /dev/null +++ b/doc/source/release_notes/v3.1.rst @@ -0,0 +1,22 @@ +.. _changes_v3_1: + +=========================== +Version 3.1 (July 22, 2026) +=========================== + +This release extends the controlled vocabularies with unit-type information for demand types. The model data itself is unchanged. + +Added +----- + +- **Unit types in the demand-type vocabulary:** the new canonical ``EDP_TYPES`` mapping records each demand type's short acronym and the physical quantity it is measured in (``UnitType``), and the new ``UNIT_TYPES`` vocabulary enumerates the valid unit types: ``acceleration``, ``speed``, ``displacement``, ``unitless``, ``rotation``, ``force``, ``force_per_length``, and ``pressure``. Consumers (e.g., Pelicun 3.10+) can infer physical units for demand data from the vocabulary instead of maintaining hardcoded lists. + +Changed +------- + +- ``EDP_to_demand_type`` is now derived from ``EDP_TYPES``. Its content is identical to before, so existing consumers are unaffected. + +Distribution +------------ + +- Version 3.1 is published to PyPI as ``simcenter-dlml``. The GitHub repository's ``releases/latest`` continues to point to v2.1.0 for legacy Pelicun (≤3.9) compatibility. diff --git a/pyproject.toml b/pyproject.toml index f4c7206e..efe6bdeb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "simcenter-dlml" -version = "3.1.dev0" +version = "3.1" description = "Damage and Loss Model Library" readme = "README.md" license = { text = "BSD-3-Clause" } diff --git a/src/dlml/__init__.py b/src/dlml/__init__.py index 812f52ea..3bf02537 100644 --- a/src/dlml/__init__.py +++ b/src/dlml/__init__.py @@ -20,6 +20,8 @@ from dlml.vocabulary import ( DEMAND_TYPES, DISTRIBUTION_FAMILIES, + EDP_TYPES, + UNIT_TYPES, EDP_to_demand_type, ) @@ -32,7 +34,9 @@ 'DEMAND_TYPES', 'DISTRIBUTION_FAMILIES', 'DatasetFileNotFoundError', + 'EDP_TYPES', 'EDP_to_demand_type', + 'UNIT_TYPES', 'UnknownDatasetError', '__version__', 'available_collections', diff --git a/src/dlml/vocabulary.py b/src/dlml/vocabulary.py index 285ca65f..4b406de1 100644 --- a/src/dlml/vocabulary.py +++ b/src/dlml/vocabulary.py @@ -3,9 +3,10 @@ These enumerations are the authoritative definition of the controlled vocabularies that appear in the packaged model parameters: the demand types a -fragility or loss model may be conditioned on, and the statistical -distribution families a model parameter may use. They are owned here, in the -data library, so a single source of truth governs what the data may contain. +fragility or loss model may be conditioned on, the unit type each demand is +measured in, and the statistical distribution families a model parameter may +use. They are owned here, in the data library, so a single source of truth +governs what the data may contain. The values mirror pelicun's current definitions -- ``EDP_to_demand_type`` in ``pelicun.base`` and the ``rv_class_map`` registry in ``pelicun.uq`` -- and @@ -14,51 +15,78 @@ from __future__ import annotations -#: Mapping from a demand's full descriptive name to its short demand-type -#: code (e.g. ``'Peak Floor Acceleration' -> 'PFA'``). The keys are the -#: demand-type names a model's ``Demand``/``Type`` field may use; a spectral -#: name may additionally carry a ``|`` qualifier in the data, such as +#: The valid unit types a demand may be measured in. A demand type's unit +#: type identifies the physical quantity its values represent (so a consumer +#: can pick the appropriate unit for it); ``'unitless'`` marks dimensionless +#: demands, and ``'rotation'`` marks radian-measured demands that undergo no +#: unit conversion. +UNIT_TYPES: frozenset[str] = frozenset( + { + 'acceleration', + 'speed', + 'displacement', + 'unitless', + 'rotation', + 'force', + 'force_per_length', + 'pressure', + } +) + +#: Mapping from a demand's full descriptive name to its properties: the short +#: demand-type code under ``'Acronym'`` (e.g. ``'Peak Floor Acceleration' -> +#: 'PFA'``) and the physical quantity it is measured in under ``'UnitType'`` +#: (one of :data:`UNIT_TYPES`). The keys are the demand-type names a model's +#: ``Demand``/``Type`` field may use. Some demands carry an additional +#: qualifier, such as the period for spectral accelerations as ``|``: #: ``'Spectral Acceleration|1.0'``. -EDP_to_demand_type: dict[str, str] = { # pelicun-compatible name +EDP_TYPES: dict[str, dict[str, str]] = { # Drifts - 'Story Drift Ratio': 'PID', - 'Peak Interstory Drift Ratio': 'PID', - 'Roof Drift Ratio': 'PRD', - 'Peak Roof Drift Ratio': 'PRD', - 'Damageable Wall Drift': 'DWD', - 'Racking Drift Ratio': 'RDR', - 'Mega Drift Ratio': 'PMD', - 'Residual Drift Ratio': 'RID', - 'Residual Interstory Drift Ratio': 'RID', - 'Peak Effective Drift Ratio': 'EDR', + 'Story Drift Ratio': {'Acronym': 'PID', 'UnitType': 'unitless'}, + 'Peak Interstory Drift Ratio': {'Acronym': 'PID', 'UnitType': 'unitless'}, + 'Roof Drift Ratio': {'Acronym': 'PRD', 'UnitType': 'unitless'}, + 'Peak Roof Drift Ratio': {'Acronym': 'PRD', 'UnitType': 'unitless'}, + 'Damageable Wall Drift': {'Acronym': 'DWD', 'UnitType': 'unitless'}, + 'Racking Drift Ratio': {'Acronym': 'RDR', 'UnitType': 'unitless'}, + 'Mega Drift Ratio': {'Acronym': 'PMD', 'UnitType': 'unitless'}, + 'Residual Drift Ratio': {'Acronym': 'RID', 'UnitType': 'unitless'}, + 'Residual Interstory Drift Ratio': {'Acronym': 'RID', 'UnitType': 'unitless'}, + 'Peak Effective Drift Ratio': {'Acronym': 'EDR', 'UnitType': 'unitless'}, # Floor response - 'Peak Floor Acceleration': 'PFA', - 'Peak Floor Velocity': 'PFV', - 'Peak Floor Displacement': 'PFD', + 'Peak Floor Acceleration': {'Acronym': 'PFA', 'UnitType': 'acceleration'}, + 'Peak Floor Velocity': {'Acronym': 'PFV', 'UnitType': 'speed'}, + 'Peak Floor Displacement': {'Acronym': 'PFD', 'UnitType': 'displacement'}, # Component response - 'Peak Link Rotation Angle': 'LR', - 'Peak Link Beam Chord Rotation': 'LBR', + 'Peak Link Rotation Angle': {'Acronym': 'LR', 'UnitType': 'rotation'}, + 'Peak Link Beam Chord Rotation': {'Acronym': 'LBR', 'UnitType': 'rotation'}, # Wind Intensity - 'Peak Gust Wind Speed': 'PWS', + 'Peak Gust Wind Speed': {'Acronym': 'PWS', 'UnitType': 'speed'}, # Wind Demands - 'Peak Wind Force': 'PWF', - 'Peak Internal Force': 'PIF', - 'Peak Line Force': 'PLF', - 'Peak Wind Pressure': 'PWP', + 'Peak Wind Force': {'Acronym': 'PWF', 'UnitType': 'force'}, + 'Peak Internal Force': {'Acronym': 'PIF', 'UnitType': 'force'}, + 'Peak Line Force': {'Acronym': 'PLF', 'UnitType': 'force_per_length'}, + 'Peak Wind Pressure': {'Acronym': 'PWP', 'UnitType': 'pressure'}, # Inundation Intensity - 'Peak Inundation Height': 'PIH', + 'Peak Inundation Height': {'Acronym': 'PIH', 'UnitType': 'displacement'}, # Shaking Intensity - 'Peak Ground Acceleration': 'PGA', - 'Peak Ground Velocity': 'PGV', - 'Spectral Acceleration': 'SA', - 'Spectral Velocity': 'SV', - 'Spectral Displacement': 'SD', - 'Peak Spectral Acceleration': 'SA', - 'Peak Spectral Velocity': 'SV', - 'Peak Spectral Displacement': 'SD', - 'Permanent Ground Deformation': 'PGD', + 'Peak Ground Acceleration': {'Acronym': 'PGA', 'UnitType': 'acceleration'}, + 'Peak Ground Velocity': {'Acronym': 'PGV', 'UnitType': 'speed'}, + 'Spectral Acceleration': {'Acronym': 'SA', 'UnitType': 'acceleration'}, + 'Spectral Velocity': {'Acronym': 'SV', 'UnitType': 'speed'}, + 'Spectral Displacement': {'Acronym': 'SD', 'UnitType': 'displacement'}, + 'Peak Spectral Acceleration': {'Acronym': 'SA', 'UnitType': 'acceleration'}, + 'Peak Spectral Velocity': {'Acronym': 'SV', 'UnitType': 'speed'}, + 'Peak Spectral Displacement': {'Acronym': 'SD', 'UnitType': 'displacement'}, + 'Permanent Ground Deformation': {'Acronym': 'PGD', 'UnitType': 'displacement'}, # Placeholder for advanced calculations - 'One': 'ONE', + 'One': {'Acronym': 'ONE', 'UnitType': 'unitless'}, +} + +#: Mapping from a demand's full descriptive name to its short demand-type +#: code, derived from :data:`EDP_TYPES` (e.g. +#: ``'Peak Floor Acceleration' -> 'PFA'``). +EDP_to_demand_type: dict[str, str] = { # pelicun-compatible name + name: info['Acronym'] for name, info in EDP_TYPES.items() } #: The valid demand-type names (the keys of :data:`EDP_to_demand_type`). A diff --git a/tests/test_vocabulary.py b/tests/test_vocabulary.py index 0d54d446..0fa095fa 100644 --- a/tests/test_vocabulary.py +++ b/tests/test_vocabulary.py @@ -56,6 +56,51 @@ def test_demand_type_codes_are_nonempty_strings(): ) +def test_unit_types_are_the_expected_set(): + """UNIT_TYPES carries exactly the five valid unit types.""" + assert ( + frozenset( + { + 'acceleration', + 'speed', + 'displacement', + 'unitless', + 'rotation', + 'force', + 'force_per_length', + 'pressure', + } + ) + == vocabulary.UNIT_TYPES + ) + + +def test_edp_types_entries_are_well_formed(): + """Every EDP_TYPES entry has exactly an Acronym and a valid UnitType.""" + for name, info in vocabulary.EDP_TYPES.items(): + assert set(info) == {'Acronym', 'UnitType'}, name + assert isinstance(info['Acronym'], str) + assert info['Acronym'] + assert info['UnitType'] in vocabulary.UNIT_TYPES, name + + +def test_demand_type_mapping_is_derived_from_edp_types(): + """EDP_to_demand_type is the name-to-acronym projection of EDP_TYPES.""" + assert vocabulary.EDP_to_demand_type == { + name: info['Acronym'] for name, info in vocabulary.EDP_TYPES.items() + } + assert list(vocabulary.EDP_to_demand_type) == list(vocabulary.EDP_TYPES) + + +def test_names_sharing_an_acronym_share_a_unit_type(): + """Two names with the same acronym agree on the unit type, so a consumer + may safely look up units at the acronym level.""" + by_acronym: dict[str, str] = {} + for name, info in vocabulary.EDP_TYPES.items(): + expected = by_acronym.setdefault(info['Acronym'], info['UnitType']) + assert info['UnitType'] == expected, name + + def test_distribution_families_include_the_core_set(): """The family set carries pelicun's core distributions, incl. the ``deterministic`` default used for an absent family.""" @@ -70,7 +115,9 @@ def test_vocabulary_is_reexported_from_package(): """The canonical names are reachable directly from the package root.""" assert dlml.DEMAND_TYPES is vocabulary.DEMAND_TYPES assert dlml.DISTRIBUTION_FAMILIES is vocabulary.DISTRIBUTION_FAMILIES + assert dlml.EDP_TYPES is vocabulary.EDP_TYPES assert dlml.EDP_to_demand_type is vocabulary.EDP_to_demand_type + assert dlml.UNIT_TYPES is vocabulary.UNIT_TYPES # --------------------------------------------------------------------------- diff --git a/uv.lock b/uv.lock index 2730c621..ecaa06c9 100644 --- a/uv.lock +++ b/uv.lock @@ -4944,7 +4944,7 @@ wheels = [ [[package]] name = "simcenter-dlml" -version = "3.1.dev0" +version = "3.1" source = { editable = "." } dependencies = [ { name = "jsonschema", version = "4.25.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },