Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion docs/scenario_workflow_translation_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down Expand Up @@ -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.
Expand Down
177 changes: 177 additions & 0 deletions lib/BOSS/scenario_measure_map.json
Original file line number Diff line number Diff line change
@@ -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
}
}
]
}
]
}
58 changes: 58 additions & 0 deletions spec/tests/unit/scenario_measure_map_spec.rb
Original file line number Diff line number Diff line change
@@ -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
Loading