From 1b27340a59eeb40bc4e0fddcef425d1c3d292b21 Mon Sep 17 00:00:00 2001 From: JieXiong9119 <69597597+JieXiong9119@users.noreply.github.com> Date: Tue, 28 Jul 2026 15:10:10 -0600 Subject: [PATCH] add initial measure mapping json --- docs/scenario_workflow_translation_plan.md | 14 +- lib/BOSS/scenario_measure_map.json | 177 +++++++++++++++++++ spec/tests/unit/scenario_measure_map_spec.rb | 58 ++++++ 3 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 lib/BOSS/scenario_measure_map.json create mode 100644 spec/tests/unit/scenario_measure_map_spec.rb diff --git a/docs/scenario_workflow_translation_plan.md b/docs/scenario_workflow_translation_plan.md index a66418b..619f367 100644 --- a/docs/scenario_workflow_translation_plan.md +++ b/docs/scenario_workflow_translation_plan.md @@ -156,7 +156,7 @@ Each PR should be independently reviewable and should not implement later PR sco | 2 | Done | Current PR-2 branch | Scenario data model and discovery | Add reader-returned structures for first-facility report-level package scenarios. Extract scenario ID, name, temporal status, report ID, package ID, reference case ID, measure IDrefs, and linked premises. | PR 1 | Unit tests prove `building_151.xml` discovers baseline plus all package scenarios and existing reader behavior remains unchanged. | Parser assumes a hardcoded namespace, reads the wrong scenario path, or breaks existing reader tests. | | 3 | Done | Current PR-3 branch | Measure index | Index first-facility measures by ID and extract category/name metadata plus linked premises, cost/savings fields, and implementation status. | PR 2 | Tests resolve package `MeasureID` references in `building_151.xml` to parsed measure metadata. | Unresolved refs are silently dropped or measures without `TechnologyCategories` crash parsing. | | 4 | Done | Current PR-4 branch | Parser warning contract | Add structured warnings for missing IDs, unresolved refs, empty packages, missing names, missing categories, and packages with no usable measures. | PR 3 | Tests cover warning cases using `BuildingEQ-1.0.0.xml`, `Golden Test File.xml`, and no-measure fixtures. | Warnings only print to stdout or malformed package data aborts all discovery. | -| 5 | Planned | - | Initial mapping JSON | Add `lib/BOSS/scenario_measure_map.json` with verified mappings for an initial supported set from `building_151.xml`. | PR 1 | Mapping JSON is valid, and every included entry has source category/name, target `measure_dir_name`, and arguments. | Legacy mappings are copied blindly without checking current OpenStudio measure dirs/args. | +| 5 | Done | Current PR-5 branch | Initial mapping JSON | Add `lib/BOSS/scenario_measure_map.json` with verified mappings for an initial supported set from `building_151.xml`. | PR 1 | Mapping JSON is valid, and every included entry has source category/name, target `measure_dir_name`, and arguments. | Legacy mappings are copied blindly without checking current OpenStudio measure dirs/args. | | 6 | Planned | - | Basic `ScenarioMeasureMapper` | Load JSON, normalize lookup keys, and map one parsed BuildingSync measure to OpenStudio step specs using `SystemCategoryAffected` plus `MeasureName`. | PRs 3, 5 | Mapper unit tests return expected steps and structured unmapped warnings. | Mapper mutates reader data, raises on unmapped measures, or hardcodes rules outside JSON. | | 7 | Planned | - | Technology category fallback | Add fallback lookup by technology category plus `MeasureName`. | PR 6 | Tests show fallback mapping works when `SystemCategoryAffected` is absent, while normal lookup priority is preserved. | Fallback changes normal category/name lookup behavior. | | 8 | Planned | - | Conditional mapping rules | Add data-driven conditional argument support for building type and principal HVAC/system context. | PR 6 | Tests prove conditions include and exclude arguments predictably. | Implementation becomes a per-measure Ruby condition chain. | @@ -248,6 +248,18 @@ Keep detailed rationale in the relevant PR description or code review thread. Ke - Verification: `C:\Ruby32-x64\bin\ruby.exe -S bundle exec rubocop lib/BOSS/buildingsync_reader/buildingsync_reader.rb spec/tests/unit/buildingsync_reader_spec.rb` - fails on existing reader/spec style debt and new-cop configuration warnings; after PR-4 cleanup, the focused run reports 132 existing offenses, 117 autocorrectable. - Follow-up: PR 5 can add mapping JSON without expecting parser warnings to identify unmapped OpenStudio measures; mapping-specific warnings remain PR 6/PR 9 scope. +### PR 5: Initial mapping JSON + +- Status: Done +- Delivered by: Current PR-5 branch +- Handoff: `lib/BOSS/scenario_measure_map.json` defines the initial system-category-plus-measure-name mapping data for a verified `building_151.xml` subset. Entries use exact reader source strings, include technology category metadata, and emit OSW-shaped step specs with `measure_dir_name` and argument hashes. +- Handoff: PR 5 intentionally defers mappings that need conditional/context support or more target research, including daylight controls, package unit replacement, burner replacement, and boiler replacement. PR 6 should load the JSON as data only; technology-category fallback, conditional rules, and structured scenario results remain PRs 7-9. +- Verification: `C:\Ruby32-x64\bin\ruby.exe -S bundle exec ruby -rjson -e "JSON.parse(File.read('lib/BOSS/scenario_measure_map.json'))"` - passed. +- Verification: Local Ruby check against installed bundled measure sources - every mapped `measure_dir_name` and non-skip argument was present. +- Verification: `C:\Ruby32-x64\bin\ruby.exe -S bundle exec rspec spec/tests/unit/scenario_measure_map_spec.rb` - 3 examples, 0 failures. +- Verification: `C:\Ruby32-x64\bin\ruby.exe -S bundle exec rspec spec/tests/unit/buildingsync_reader_spec.rb --format progress` - exited 0; the terminal emitted no RSpec summary for this command. +- Follow-up: PR 6 should add `BOSS::ScenarioMeasureMapper`, normalize lookup keys for `system_category_affected` plus `measure_name`, and return structured unmapped warnings without mutating reader data. + ## Verification Commands Use the narrowest command that verifies the current PR. Broader commands should be run before merging larger integration PRs. diff --git a/lib/BOSS/scenario_measure_map.json b/lib/BOSS/scenario_measure_map.json new file mode 100644 index 0000000..c2f68d1 --- /dev/null +++ b/lib/BOSS/scenario_measure_map.json @@ -0,0 +1,177 @@ +{ + "schema_version": "1.0", + "lookup_order": [ + "system_category_affected_and_measure_name" + ], + "mappings": [ + { + "system_category_affected": "Air Distribution", + "technology_category_element_name": "OtherHVAC", + "measure_name": "Add or repair economizer", + "steps": [ + { + "measure_dir_name": "EnableEconomizerControl", + "arguments": { + "__SKIP__": false, + "economizer_type": "FixedDryBulb" + } + } + ] + }, + { + "system_category_affected": "Air Distribution", + "technology_category_element_name": "OtherHVAC", + "measure_name": "Install demand control ventilation", + "steps": [ + { + "measure_dir_name": "EnableDemandControlledVentilation", + "arguments": { + "__SKIP__": false, + "dcv_type": "EnableDCV" + } + } + ] + }, + { + "system_category_affected": "Ceiling", + "technology_category_element_name": "BuildingEnvelopeModifications", + "measure_name": "Increase ceiling insulation", + "steps": [ + { + "measure_dir_name": "IncreaseInsulationRValueForRoofsByPercentage", + "arguments": { + "__SKIP__": false, + "r_value": 20 + } + } + ] + }, + { + "system_category_affected": "General Controls and Operations", + "technology_category_element_name": "OtherHVAC", + "measure_name": "Upgrade operating protocols, calibration, and/or sequencing", + "steps": [ + { + "measure_dir_name": "AdjustThermostatSetpointsByDegrees", + "arguments": { + "__SKIP__": false, + "cooling_adjustment": 1.0, + "heating_adjustment": -1.0 + } + } + ] + }, + { + "system_category_affected": "Lighting", + "technology_category_element_name": "LightingImprovements", + "measure_name": "Add occupancy sensors", + "steps": [ + { + "measure_dir_name": "ReduceLightingLoadsByPercentage", + "arguments": { + "__SKIP__": false, + "lighting_power_reduction_percent": 5 + } + } + ] + }, + { + "system_category_affected": "Lighting", + "technology_category_element_name": "LightingImprovements", + "measure_name": "Retrofit with light emitting diode technologies", + "steps": [ + { + "measure_dir_name": "SetLightingLoadsByLPD", + "arguments": { + "__SKIP__": false, + "lpd": 0.6 + } + } + ] + }, + { + "system_category_affected": "Plug Load", + "technology_category_element_name": "PlugLoadReductions", + "measure_name": "Install plug load controls", + "steps": [ + { + "measure_dir_name": "ReduceElectricEquipmentLoadsByPercentage", + "arguments": { + "__SKIP__": false, + "elecequip_power_reduction_percent": 20.0 + } + } + ] + }, + { + "system_category_affected": "Refrigeration", + "technology_category_element_name": "Refrigeration", + "measure_name": "Replace ice/refrigeration equipment with high efficiency units", + "steps": [ + { + "measure_dir_name": "ReduceElectricEquipmentLoadsByPercentage", + "arguments": { + "__SKIP__": false, + "elecequip_power_reduction_percent": 5 + } + } + ] + }, + { + "system_category_affected": "Roof", + "technology_category_element_name": "BuildingEnvelopeModifications", + "measure_name": "Increase roof insulation", + "steps": [ + { + "measure_dir_name": "IncreaseInsulationRValueForRoofs", + "arguments": { + "__SKIP__": false, + "r_value": 30 + } + } + ] + }, + { + "system_category_affected": "Wall", + "technology_category_element_name": "BuildingEnvelopeModifications", + "measure_name": "Air seal envelope", + "steps": [ + { + "measure_dir_name": "ReduceSpaceInfiltrationByPercentage", + "arguments": { + "__SKIP__": false, + "space_infiltration_reduction_percent": 30.0 + } + } + ] + }, + { + "system_category_affected": "Wall", + "technology_category_element_name": "BuildingEnvelopeModifications", + "measure_name": "Increase wall insulation", + "steps": [ + { + "measure_dir_name": "IncreaseInsulationRValueForExteriorWalls", + "arguments": { + "__SKIP__": false, + "r_value": 25 + } + } + ] + }, + { + "system_category_affected": "Wall", + "technology_category_element_name": "BuildingEnvelopeModifications", + "measure_name": "Insulate thermal bypasses", + "steps": [ + { + "measure_dir_name": "IncreaseInsulationRValueForExteriorWallsByPercentage", + "arguments": { + "__SKIP__": false, + "r_value": 20 + } + } + ] + } + ] +} diff --git a/spec/tests/unit/scenario_measure_map_spec.rb b/spec/tests/unit/scenario_measure_map_spec.rb new file mode 100644 index 0000000..e7ae70e --- /dev/null +++ b/spec/tests/unit/scenario_measure_map_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'json' + +SCENARIO_MEASURE_MAP_PATH = File.expand_path('../../../lib/BOSS/scenario_measure_map.json', __dir__) + +RSpec.describe 'scenario_measure_map.json' do + let(:scenario_measure_map) { JSON.parse(File.read(SCENARIO_MEASURE_MAP_PATH)) } + let(:mappings) { scenario_measure_map.fetch('mappings') } + + it 'defines required mapping fields for each entry' do + expect(scenario_measure_map.fetch('schema_version')).to eq '1.0' + expect(mappings).not_to be_empty + + mappings.each do |mapping| + expect(mapping.fetch('system_category_affected')).not_to be_empty + expect(mapping.fetch('measure_name')).not_to be_empty + expect(mapping.fetch('steps')).not_to be_empty + + mapping.fetch('steps').each do |step| + expect(step.fetch('measure_dir_name')).not_to be_empty + expect(step.fetch('arguments')).to be_a Hash + end + end + end + + it 'uses OSW-shaped argument hashes without conditional mapping rules' do + mappings.each do |mapping| + mapping.fetch('steps').each do |step| + expect(step.fetch('arguments')).to include('__SKIP__') + expect(step.fetch('arguments')).not_to include('condition') + end + end + end + + it 'covers the verified initial building_151 measure set' do + expected_sources = [ + ['Air Distribution', 'Add or repair economizer'], + ['Air Distribution', 'Install demand control ventilation'], + ['Ceiling', 'Increase ceiling insulation'], + ['General Controls and Operations', 'Upgrade operating protocols, calibration, and/or sequencing'], + ['Lighting', 'Add occupancy sensors'], + ['Lighting', 'Retrofit with light emitting diode technologies'], + ['Plug Load', 'Install plug load controls'], + ['Refrigeration', 'Replace ice/refrigeration equipment with high efficiency units'], + ['Roof', 'Increase roof insulation'], + ['Wall', 'Air seal envelope'], + ['Wall', 'Increase wall insulation'], + ['Wall', 'Insulate thermal bypasses'] + ] + + actual_sources = mappings.map do |mapping| + [mapping.fetch('system_category_affected'), mapping.fetch('measure_name')] + end + + expect(actual_sources).to eq expected_sources + end +end