From f876d5a61c5af200c0e84ce5ee2f23fb94791815 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Tue, 21 Jul 2026 15:08:46 +0200 Subject: [PATCH 01/11] =?UTF-8?q?B0=20=E2=80=94=20benchmark=20methodology?= =?UTF-8?q?=20&=20contracts=20(#4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: add approved B0-B6 benchmark programme implementation plan Engine-neutral TAS/OpenStudio benchmark plan (Revision 2, stakeholder-approved) placed under docs/benchmark/ as the umbrella for milestones B0-B6. B0's methodology/metrics/schema/tolerances/glossary docs will be added alongside it. Co-Authored-By: Claude Fable 5 * docs: require commit+push after every stage (multi-laptop continuity) Adds a Multi-laptop continuity rule to the mandatory Git rules (S13) and session discipline (S12): commit and push each feature branch at stage end, and resume by fetching/checking out the existing branch rather than recreating it. Michal works across several laptops. Co-Authored-By: Claude Fable 5 * docs: define benchmark methodology and glossary Co-Authored-By: Codex * docs: define benchmark metrics and units Co-Authored-By: Codex * docs: specify benchmark schema and tolerances Co-Authored-By: Codex --------- Co-authored-by: Claude Fable 5 Co-authored-by: Codex --- docs/benchmark/GLOSSARY.md | 41 ++ docs/benchmark/IMPLEMENTATION-PLAN.md | 526 ++++++++++++++++++++++++++ docs/benchmark/METHODOLOGY.md | 96 +++++ docs/benchmark/METRICS.md | 90 +++++ docs/benchmark/SCHEMA.md | 202 ++++++++++ docs/benchmark/TOLERANCES.md | 106 ++++++ 6 files changed, 1061 insertions(+) create mode 100644 docs/benchmark/GLOSSARY.md create mode 100644 docs/benchmark/IMPLEMENTATION-PLAN.md create mode 100644 docs/benchmark/METHODOLOGY.md create mode 100644 docs/benchmark/METRICS.md create mode 100644 docs/benchmark/SCHEMA.md create mode 100644 docs/benchmark/TOLERANCES.md diff --git a/docs/benchmark/GLOSSARY.md b/docs/benchmark/GLOSSARY.md new file mode 100644 index 0000000..ae25787 --- /dev/null +++ b/docs/benchmark/GLOSSARY.md @@ -0,0 +1,41 @@ +# Benchmark glossary + +## Canonicalization + +The deterministic process that converts a SAM model into the representation hashed by `canonicalModelHash`. Its rules are identified by `canonicalizationVersion` so a changed algorithm cannot masquerade as a changed model. + +## Canonical unit + +The unit required by the benchmark contract for a metric. Producers convert values at emission time and include the unit token on every metric; consumers never infer a unit from a field name. + +## Conditioning pairing + +The two engine configurations intended to represent comparable space conditioning. The B0 candidate is OpenStudio Ideal Loads versus Tas thermostats, IZAMs, and TBD sizing. It remains provisional until the B2 `SingleBox` checkpoint and energy-modeller review. + +## Engine + +The independent simulation implementation that produces physical results. In this programme the engine families are OpenStudio/EnergyPlus and EDSL Tas. Engine identity and version belong in provenance. + +## Gate status + +The overall comparator outcome derived from contract validity, coverage diagnostics, and metric bands. A gate status is not a claim that one engine is correct. `N/A` metrics do not create a numerical failure. + +## Metric availability + +Whether a producer obtained a real metric from its route. Available means `available: true` with a non-null value, including a measured zero. Unavailable means `available: false` with `value: null`. + +## Provenance + +The evidence describing what was run and how: model and weather identities and hashes, canonicalization version, code revisions, engine versions, route, design-day source, run time, state, warnings, and notes. + +## Route + +The complete model-translation and simulation path used to produce a benchmark document. A route is distinct from an engine because one engine may be exercised through native SAM translation or a shared interchange model. + +## Tolerance band + +A configurable reporting classification based on the difference between two available, unit-compatible values. B0 defines provisional pass/warn/fail bands; it does not validate them as acceptance thresholds. + +## Whole-model reconciliation + +A diagnostic comparison between a reported model total and an aggregation of matched space results where the metric semantics permit aggregation. A reconciliation result is kept separate from the direct cross-engine comparison. diff --git a/docs/benchmark/IMPLEMENTATION-PLAN.md b/docs/benchmark/IMPLEMENTATION-PLAN.md new file mode 100644 index 0000000..240d83c --- /dev/null +++ b/docs/benchmark/IMPLEMENTATION-PLAN.md @@ -0,0 +1,526 @@ +# SAM TAS ↔ OpenStudio Benchmark Programme — Implementation Plan (B0–B6) + +> **Revision 3** — incorporates stakeholder corrections: (1) `feature/benchmark-*` branch names; (2) unavailable metrics carry `value: null`; (3) dual model hashes; (4) build-order verified to live in per-repo CI, not a SAM PR; (5) conditioning equivalence is a **candidate** in B0, frozen only after the first TAS SingleBox validation; (6) B5 GH host deferred to B5 kickoff; (7) **commit and push the feature branch after every stage** for multi-laptop continuity; plus a per-milestone **model-allocation** plan and a **mandatory Git rules** block for every implementation prompt. + +## Context + +We need a **defensible, repeatable benchmark** comparing two independent building-simulation engines — **TAS** (EDSL Tas; licensed, installed only on a separate laptop) and **OpenStudio/EnergyPlus** — over the same SAM `AnalyticalModel`s. Because TAS runs only on a licensed machine, the workflow is **two-stage and file-mediated**: each engine runs independently and emits the *same* engine-neutral benchmark JSON; a separate **headless comparator** later aligns model/space results, computes differences, assigns tolerance bands, and writes Markdown + CSV + summary-JSON. **No direct runtime coupling** between TAS and OpenStudio. + +The **primary gate** harmonises *conditioning semantics* — OpenStudio **Ideal Loads** vs the equivalent idealised TAS route (thermostats + IZAMs + TBD sizing). This pairing is a **candidate** until the first TAS SingleBox run confirms meaningful conditioning, annual energy and loads (see D10). Production HVAC (TAS **TPD** plant) is deferred to a later, separately-labelled *informational* comparison. All work branches off **`sow/2026-Q3`** in each repo and merges back into `sow/2026-Q3`. + +This plan is grounded in direct repository inspection (findings in §2). Three architecture forks were resolved with the stakeholder (§3). + +--- + +## 1. Architecture & dependency diagram + +```text + ┌───────────────────────────────┐ + │ SAM (core repo) │ + │ SAM.Core (System.Text.Json, │ + │ ToJsonObject, TryGetDateTime),│ + │ SAM.Analytical (result bags), │ + │ SAM.Geometry, SAM.Weather │ + └───────────────┬───────────────┘ + built DLLs via HintPath (..\..\SAM\build\*.dll) + ┌──────────────────┬───────────────┬──────────────────┬───────────────────────┐ + │ │ │ │ │ +┌──▼───────────┐ ┌────▼────────┐ ┌────▼──────┐ ┌────────▼────────┐ ┌──────────▼─────────────┐ +│SAM_OpenStudio│ │ SAM_Tas │ │ SAM_gbXML │ │ SAM_Validation │ │ SAM.Analytical.Benchmark│ +│SAM.Analytical│ │SAM.Analytical│ │gbXML.ToSAM│ │ (MSTest exes) │ │ (NEW, in SAM_Validation│ +│ .OpenStudio │ │ .Tas │ │ + export │ │ │ │ repo). netstandard2.0,│ +│(netstd2.0) │ │(Interop.T*) │ │ │ │ │ │ STJ only, ZERO deps. │ +└──┬───────────┘ └────┬────────┘ └───────────┘ └────────┬────────┘ │ → SAM_Validation\build\ │ + │ ref (HintPath) │ ref (HintPath) │ ProjectRef └──────────┬─────────────┘ + │ ..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll │ (built FIRST) + └───────────┬──────┴───────────────────────┬──────────┴───────────┬───────────┘ + producer writes DTO producer writes DTO comparator + reports read DTO + (SAM_OpenStudio CLI) (SAM_Tas CLI, TAS laptop) (SAM_Validation CLI, any laptop) +``` + +**Invariant:** `SAM.Analytical.Benchmark` has **no upstream SAM or engine dependencies** → builds first, referenced by all three producers/consumers with **no cycle**. Producers depend on it one-directionally; the comparator depends only on it, never on an engine runtime. + +### Data flow (two-stage, file-mediated) + +```text +[OpenStudio laptop] SAM model ─native SAM→OS→ EnergyPlus ─eplusout.sql→ SAM results ─→ benchmark-OpenStudio.json +[TAS laptop] SAM model ─WorkflowCalculator(gbXML)→ TAS (TBD→TSD) ─→ SAM results ─→ benchmark-TAS.json +[B2b optional] shared gbXML ─OS GbXMLReverseTranslator→ EnergyPlus ─→ benchmark-OpenStudio-gbXML.json +[any laptop] benchmark-*.json ─→ Comparator ─→ comparison.md / comparison.csv / comparison-summary.json +``` + +--- + +## 2. Findings from repository inspection (verified directly) + +### 2.1 Dependency & build model +- **Nothing references `SAM_Validation`; SAM core references neither engine repo.** A schema lib in SAM_Validation creates **no cycle**. +- Cross-repo refs use committed built DLLs via `..\..\\build\*.dll`. No absolute paths; portability rests on the sibling-repo checkout under `SAM-BIM\`. +- **Build orchestration (verified):** the `SAM-BIM` root is **not** a git repo — it is a container of sibling repos. The root `BuildAll*.csproj`/`BuildAlls*.bat` files are **untracked, local-only** convenience. Real, tracked build order lives in **each repo's `.github/workflows/build.yml`** via a `$buildOrder` array that clones sibling deps at the matching branch and rebuilds them **before** the current repo. SAM_OpenStudio uses `@('SAM','SAM_SQLite', )`; SAM_Validation uses `@('SAM','SAM_Mollier','SAM_IAPWS', )`. SAM_Validation also has `test.yml` (`dotnet test`). **SAM_Validation currently produces no `build\` folder** (nothing references it yet). +- `SAM_Validation` today = 3 net8.0 **MSTest-runner exes** (`OutputType=Exe`, `EnableMSTestRunner`), referencing only `SAM.Core`/`SAM.Core.Mollier`. No JSON, no benchmark code, no `SAM.Analytical` ref. Folder idiom `Classes/ Modify/ Variables/ Tests/`. Already has `Modify.Report_RelativeError/AbsoluteError` (near-zero handled) + `Error.Relative=0.01`. + +### 2.2 SAM core result model (metric source of truth) +- Results are **enum-keyed parameter bags** (`Result : SAMObject` with `Source`/`Reference`/`DateTime`) on the `AdjacencyCluster` graph. Read via `analyticalModel.GetAnalyticalModelSimulationResults()` and `adjacencyCluster.GetResults(space)`. +- **Whole-model `AnalyticalModelSimulationResult`** (`AnalyticalModelSimulationResultParameter`): `ConsumptionHeating`, `ConsumptionCooling` (label kWh), `PeakHeatingLoad`, `PeakCoolingLoad` (label kW), `PeakHeatingHour`, `PeakCoolingHour` (int hr), `FloorArea` (m²), `Volume` (m³). +- **Per-space `SpaceSimulationResult`** (one per `LoadType`): `Area`, `Volume`, `DesignLoad` (W), `Load` (W) + `LoadIndex` (peak hour-of-year 0–8760), `UnmetHours`/`OccupiedUnmetHours`/`UnmetHourFirstIndex`. +- **NOT available from either route** → explicit non-metrics: **per-space annual energy (kWh)** and **monthly profiles** (only hourly *temperatures* in the TM comfort family; Monthly exists only on the TAS **TPD** plant side). + +### 2.3 Units & sign (critical) +- **Load magnitude is W** — confirmed: OS `ToSAM_SpaceSimulationResults` stores `Load = peakLoadKilowatts * 1000.0`. +- **Consumption magnitude ambiguous:** enum labels say kWh; OS `ToSAM` copies the result-set value (reported kWh) into `ConsumptionHeating`; but the legacy `Convert\ToDesignExplorer\File.cs` divides `ConsumptionHeating` by 1000 (implying Wh). **Pin empirically in B1**; store explicit unit tokens; never infer from names. +- **Sign:** no ± convention. Heating/cooling separated by distinct params (model) or `LoadType {Undefined, Cooling, Heating}` (space); magnitudes positive. + +### 2.4 Space identity & alignment +- `Space : SAMObject` has stable, read-only `Guid` (round-trips through JSON) + settable `Name`. +- **OpenStudio** keys space results by `space.Guid.ToString("N")`; zone key = `OpenStudioName("IdealLoads", space.Name, space.Guid)`. **TAS** matches TSD `ZoneData`→SAM `Space` **by Name** but attaches to the same SAM `Space` objects (which carry Guids). +- **Both producers start from the same source SAM model** → each emits **Guid + Name** per space; comparator aligns **Guid-first, Name-fallback** (mirrors `AddResults.LookupSpace`). B2b (gbXML round-trip) may not preserve Guids → documented Name/geometry fallback. + +### 2.5 Serialization & determinism primitives +- SAM uses **System.Text.Json** (`ToJsonObject()`/`FromJsonObject(JsonObject)` + `_type` discriminator; base in `ParameterizedSAMObject`). **No Newtonsoft.** Numeric wire-format helpers `FormatDecimal.cs`/`FormatFloatingPoint.cs`. TAS timing CSV already uses `CultureInfo.InvariantCulture`. +- `SAM.Core.Query.TryGetDateTime(...)` + `IntervalHourOfYear(DateTime)→0..8759` (EnergyPlus-verified) exist for peak-hour handling. +- `Variables\` const-holder convention established (`SAM.Analytical.HourOfYear.SummerStartIndex=2880`; `SAM.Validation.Error.Relative`). + +### 2.6 OpenStudio route (native SAM→OS) +- Main lib **`SAM.Analytical.OpenStudio`** (netstandard2.0; OpenStudio 3.10.0, System.Data.SQLite.Core 1.0.119). **No CLI project.** +- **Headless + cancellable** `OpenStudioSimulationRunner.RunAsync(context, epwPath, outputDirectory, runOptions, execute, IProgress, CancellationToken)`; convenience `AnalyticalModel.ToOpenStudio(...)`/`ToOpenStudioAsync(...)`. Cancellation tree-kills via a Windows Job Object. +- **Results:** `eplusout.sql` → `OpenStudioSimulationResultSet` → `Convert\ToSAM\SimulationResults.cs`: `ToSAM(model)` → `AnalyticalModelSimulationResult` (Reference = model Guid "N"; `TotalAnnualHeating/Cooling`, `PeakHeating/CoolingLoadTotal`, `PeakHeating/CoolingHourTotal`, FloorArea/Volume summed); `ToSAM_SpaceSimulationResults(...)` → per-space keyed by `space.Guid("N")`, `Load = kW×1000` (W), `LoadIndex`, `UnmetHours`. `Modify\AddResults.cs`: `adjacencyCluster.AddResults(sqlPath, out diagnostics)`. `Query.Source()` = provenance string. +- **Ideal Loads** (`Convert\ToOpenStudio\IdealLoads.cs`) gated by `options.AssignIdealLoads` + `space.IsConditioned()`. +- **Metadata:** `Core.OpenStudio.Query.OpenStudioVersion()`/`EnergyPlusVersion()`; weather path; design-day source diagnostics (DDY/embedded/none); `RuntimeSeconds`; warning/severe/fatal counts from `eplusout.err`. +- **Tests:** NUnit 4.2.2, folders `M0–M7`/`C1–C8` + `Fixtures\AnalyticalModelFixtures.cs` (`SingleBox()`). Live E+ tests `[Category("Simulation")]`. Weather repo-relative. **Offline synthetic-SQLite tests** (`C5\AddResultsBySqlTests.cs`) exercise the full mapping without EnergyPlus. + +### 2.7 TAS route (native SAM→gbXML→TAS) +- Main lib **`SAM.Analytical.Tas`** (netstandard2.0). TAS interop = COM PIAs **committed** in `references_buildonly\Interop.T*.dll` (`EmbedInteropTypes`), found via registry `HKCU\Software\EDSL\TasManager`. → **Builds anywhere; runs only where TAS COM servers are registered.** Already refs `SAM.Analytical.gbXML`. +- **Reusable headless core = `WorkflowCalculator.Calculate(AnalyticalModel)`** (synchronous, GUI-free; events opt-in; **no CancellationToken**). Config via `WorkflowSettings { Path_gbXML, Path_TBD, WeatherData, DesignDays_*, AddIZAMs, Sizing, Simulate, UnmetHours, … }`. The GH `SAMAnalyticalWorkflowgbXML` and WinForms `Modify.RunWorkflow` are UI wrappers to **bypass**. +- Sequence: import gbXML→T3D → build TBD (weather, design days, IZAMs, adiabatic, zones) → `Sizing` → `Simulate`→TSD → `AddResults(path_TSD, adjacencyCluster)` → `UnmetHours` → `UpdateDesignLoads` (from `zone.maxHeating/CoolingLoad`). +- **Mapping:** `Modify.AddResults`→`Convert.ToSAM_Results` builds `SpaceSimulationResult`/`SurfaceSimulationResult`/`ZoneSimulationResult`; space matched by **Name**; design loads onto `Space` via `SpaceParameter.DesignHeating/CoolingLoad`. +- **⚠️ Model annual energy (`ConsumptionHeating/Cooling`) is NOT populated by the gbXML workflow** — only by `Convert\ToSAM\AnalyticalModelSimulationResult.cs` (invoked by GH `TasTSDAddBuildingResults`). **The producer must invoke this building-results read explicitly.** +- **Conditioning:** idealised via thermostats + **IZAMs** (`Modify.UpdateIZAMs`) + **TBD sizing** (`Query.Sizing`). Production HVAC = separate **TPD** path (later informational only). +- **Metadata:** duration via `Timings`/`.timing.csv`; weather/design-days from settings. **TAS engine version NOT available in code** → needs a helper (read TAS exe FileVersion from install dir) or a documented manual field. **Warnings minimal** (GH messages only) → capture `successful` + exceptions. +- **Tests: NONE exist** in SAM_Tas. Greenfield. Reading a `.tsd` needs the TAS COM server → TSD-based tests are TAS-laptop-only; only **pure-managed** tests (hand-built SAM results → DTO) are portable. + +### 2.8 gbXML / B2b reality +- **`GbXMLReverseTranslator` is NOT used anywhere in SAM_OpenStudio** (only `EnergyPlusReverseTranslator` for DDY). B2b is **greenfield**. +- **OpenStudio 3.10 SDK exposes `GbXMLReverseTranslator` + `GbXMLForwardTranslator`** (confirmed via DLL symbols) — feasible but unproven here. +- **`SAM_gbXML`** has `gbXMLSerializer.gbXML.ToSAM() → AnalyticalModel` (reverse) + forward export; SAM_Tas already uses it. → the **same gbXML** can feed both engines, isolating pure engine differences. + +### 2.9 Corpus assets +- **`SAM_Samples`** repo (own git, `sow/2026-Q3`): `Tas/Tas.zip`, `Revit/000000_SAM_Live.zip`. OS `AnalyticalModelFixtures` + repo-relative TMYx weather exist → reuse first. + +### 2.10 Metric availability matrix + +| Metric | OpenStudio native | TAS native (gbXML) | Neutral schema | +|---|---|---|---| +| Model annual heating/cooling | ✅ `ToSAM` | ⚠️ needs explicit building-results read | ✅ nullable | +| Model peak heating/cooling load | ✅ coincident-total | ✅ building-profile max | ✅ (semantic caveat) | +| Model peak heating/cooling hour | ✅ 0–8759 | ✅ 0–8759 | ✅ circular compare | +| Model floor area / volume | ✅ | ✅ | ✅ | +| Space area / volume | ✅ | ✅ | ✅ | +| Space peak load (W) + hour | ✅ `Load`/`LoadIndex` | ✅ | ✅ | +| Space design load (sizing, W) | ⚠️ peak proxy only | ✅ `DesignLoad` (sizing) | ✅ nullable (caveat) | +| Space unmet hours | ✅ | ✅ | ✅ nullable | +| **Per-space annual energy** | ❌ | ❌ | ❌ EXCLUDED | +| **Monthly profiles** | ❌ | ❌ (TPD only) | ❌ EXCLUDED | + +--- + +## 3. Resolved architecture decisions + +| # | Decision | Resolution | Rationale | +|---|----------|------------|-----------| +| D1 | Schema shape | **Independent, versioned DTOs** (`SAM.Analytical.Benchmark`, netstandard2.0, STJ-only, zero deps). | Result types are enum-keyed graph bags with misleading unit labels; comparator must not drag in `SAM.Analytical`/engines. Independent DTOs = clean isolation + explicit versioned wire contract. | +| D2 | Schema location | **Standalone lib in the `SAM_Validation` repo** (stakeholder-confirmed), output to `SAM_Validation\build\`. | Honours programme decision #1; versioned alongside its comparator. Build-order handled in per-repo CI (see D3-build below), **not** a SAM PR. | +| D3 | Invocation | **Three thin console CLIs** (`benchmark-openstudio`, `benchmark-tas`, `benchmark-compare`) over **one shared CLI host** in the schema lib; extensible. | A single cross-engine exe is impossible under isolation rules. Shared host = arg parsing, invariant-culture JSON I/O, stable exit codes, written once. | +| D3-build | Build order | B1a: schema project outputs to `SAM_Validation\build\` + is added to SAM_Validation's solution (its CI already builds the solution). B1b/B2: each engine repo's **own** `.github/workflows/build.yml` gains a step to clone `SAM_Validation` @ matching branch and build **only** `SAM.Analytical.Benchmark.csproj` → `SAM_Validation\build\`. Root `BuildAlls*` (untracked) updated locally only. | Verified: build order is tracked per-repo in CI, not in SAM nor the untracked root. Keeps each change inside the repo that needs it; no implicit cross-repo edits. | +| D4 | Canonical units | Schema stores **explicit unit tokens**; producers convert at emit; **pin consumption magnitude empirically in B1**. | Enum labels contradict the legacy consumer; requirement forbids name-based inference. | +| D5 | Space alignment | **Guid-first, Name-fallback**; explicit diagnostics for missing/duplicate/unmatched. | Matches `AddResults.LookupSpace`; producers share source-model Guids. | +| D6 | Primary metric set | Model: annual htg/clg, peak htg/clg load + hour, floor area, volume. Space: area, volume, design/peak load + hour, unmet hours. **Exclude** per-space annual energy & monthly profiles. | Only these present from both routes. | +| D7 | B2b | **Optional milestone, spike-gated** (SDK `GbXMLReverseTranslator`), after B3, results tagged `route=SharedGbXML-OpenStudio`. | Only this route isolates engine-vs-translator differences; greenfield → prove before build. | +| D8 | Tolerances | `±5%` (warn)/`±15%` (fail) as **configurable, clearly-labelled provisional bands** + near-zero absolute floor. Never presented as validated. | Requirement forbids scientific claims pre-corpus. | +| D9 | TAS cancellation | **Do not** add async/cancellation to SAM_Tas. Producer is synchronous; hard process timeout only. | Adding async across SAM_Tas is a large out-of-scope API change; batch benchmark tolerates synchronous runs. | +| D10 | Conditioning equivalence | In B0, document OS Ideal Loads ↔ TAS (thermostats + IZAMs + TBD sizing) as the **candidate** pairing. **Freeze it only after** the first TAS `SingleBox` run confirms meaningful conditioning, annual energy and loads (checkpoint in B2). | Freezing pre-validation risks encoding an invalid comparison into B1/B2. | +| D11 | B5 host | **Undetermined until B5 kickoff.** Do not pre-select the GH plugin/repo in an earlier milestone. Constraint: the host must not depend on any engine assembly. | Avoids premature, wrong coupling; decided with full B5 context. | + +--- + +## 4. Final B0–B6 milestone table + +All branches are created **from `sow/2026-Q3`** and PRs merge back **into `sow/2026-Q3`** (never `master`). + +| MS | Objective | Repository | Feature branch | Depends | +|----|-----------|------------|----------------|---------| +| **B0** | Methodology & contracts doc | SAM_Validation | `feature/benchmark-b0-methodology` | — | +| **B1a** | Neutral schema + serializer + shared CLI host | SAM_Validation | `feature/benchmark-b1-schema` | B0 | +| **B1b** | OpenStudio producer CLI + empirical unit pin | SAM_OpenStudio | `feature/benchmark-b1-os-producer` | B1a | +| **B2** | TAS producer CLI (+ conditioning-equivalence checkpoint) | SAM_Tas | `feature/benchmark-b2-tas-producer` | B1a | +| **B3** | Headless comparator CLI + md/csv/summary reports | SAM_Validation | `feature/benchmark-b3-comparator` | B1a | +| **B4** | Benchmark corpus | SAM_Samples | `feature/benchmark-b4-corpus` | B1b, B3 | +| **B2b** | *Optional, spike-gated:* shared-gbXML → OpenStudio | SAM_OpenStudio | `feature/benchmark-b2b-shared-gbxml` | B1b, B3 | +| **B5** | Grasshopper presentation component | *selected GH repo (decided at B5 kickoff)* | `feature/benchmark-b5-gh-viewer` | B3 | +| **B6** | Documentation + first validated report | SAM_Validation | `feature/benchmark-b6-docs-report` | B2, B3, B4 | + +If B1a's build-order verification finds the change must touch a repo not listed for that milestone, create a dedicated `feature/benchmark-b1-build-order` branch **in that repo** and open a separate PR — never edit another repo implicitly. + +--- + +## 5. Detailed implementation steps per milestone + +> Every implementation session must begin with the **Mandatory Git & repository rules** (§13) and verify that named files live in the stated repo before editing. + +### B0 — Methodology & contracts (docs only) +- **Objective:** freeze the engine-neutral contract: metric definitions, canonical units, sign, tolerance bands, provenance fields (incl. dual hashes §7), missing-data semantics (`available`/`null` invariant), space-alignment rules, the four routes. Record the **candidate** conditioning pairing (D10) — explicitly *not yet frozen*. +- **Repo/branch:** `SAM_Validation` / `feature/benchmark-b0-methodology`. Files: `docs/benchmark/{METHODOLOGY,METRICS,SCHEMA,TOLERANCES,GLOSSARY}.md`. +- **Reused APIs:** none (doc). +- **Public API boundary:** none. Documents the contract B1 encodes. +- **JSON ownership/versioning:** declares `schemaVersion` semver policy (**v1.0.0**) + compatibility (equal major required; minor drift warns) and `canonicalizationVersion`. +- **Invocation/tests:** N/A (link-check). **Human review** by an energy modeller of metric semantics, the peak-load caveat, the consumption-unit open item, and the candidate conditioning pairing. +- **Acceptance:** every §2.10 metric has definition + canonical unit + both-route source; excluded metrics listed; near-zero + peak-hour circularity rules stated; conditioning pairing labelled *candidate, pending B2 validation*. +- **Non-goals:** no code; no frozen conditioning contract; no tolerance validation. +- **Commit boundaries:** (1) methodology+glossary; (2) metrics+units; (3) tolerances+schema-spec. +- **Rollback:** revert doc PR. +- **Risks:** semantic ambiguity → mark design-vs-peak load and consumption units *provisional, pinned in B1*. + +### B1a — Neutral schema + serializer + shared CLI host +- **Objective:** encode the v1 schema + deterministic serializer + shared CLI host in `SAM.Analytical.Benchmark`, output to `SAM_Validation\build\`. +- **Repo/branch:** `SAM_Validation` / `feature/benchmark-b1-schema`. New `SAM_Validation\SAM.Analytical.Benchmark\SAM.Analytical.Benchmark.csproj` (netstandard2.0, `System.Text.Json` 8.0.5, **no SAM/engine refs**, OutputPath → `..\build\` mirroring SAM.Core convention), namespace `SAM.Analytical.Benchmark`. + - `Classes\` DTOs (§7): `BenchmarkDocument`, `BenchmarkProvenance`, `EngineInfo`, `WeatherInfo`, `ModelResult`, `SpaceResult`, `LoadTypeResult`, `MetricValue` (**`double? Value`, `Unit`, `bool Available`**), enums `BenchmarkRoute`, `EngineKind`, `MetricKey`, `Unit`, `RunState`. + - `Query\`: `Serializer` (STJ; sorted keys via explicit `JsonObject` build; `InvariantCulture`; fixed rounding), `SchemaVersion` const, `Validate(BenchmarkDocument)` — enforces the availability invariant. + - `Cli\`: `BenchmarkCli.Run(string[] args, Func body)`, `BenchmarkArgs` (`--model/--weather/--out/--route/--tolerance-profile`), invariant I/O, exit codes (`0 ok / 2 usage / 3 io / 4 engine`). + - `Variables\`: `Tolerances` (`Warn=0.05`, `Fail=0.15`, `NearZeroFloor`), `SchemaVersionInfo`. + - Test project `SAM_Validation\SAM_Analytical_Benchmark_Tests\` (net8.0 MSTest): round-trip determinism, invariant culture, key ordering, **availability invariant (available⇒non-null; unavailable⇒null; real 0⇒0+available)**, schema-version gate. + - **Solution + CI:** add the schema project (and its test project) to SAM_Validation's solution so `build.yml` builds it and the DLL lands in `SAM_Validation\build\`. +- **Reused APIs:** none upstream (isolation). Serializer mirrors SAM's invariant-culture wire-format intent. +- **Public API boundary:** DTOs + `Serializer` + `BenchmarkCli` (stable, versioned). +- **JSON ownership/versioning:** owns the schema (`schemaVersion="1.0.0"`). +- **Invocation:** `dotnet test` (MSTest). +- **Acceptance:** builds; DLL in `SAM_Validation\build\`; byte-identical re-serialize; availability invariant enforced by `Validate` + tests. +- **Non-goals:** no producers, comparator, reporting. +- **Dependencies:** B0. +- **Commit boundaries:** (1) DTOs + `MetricValue` invariant; (2) serializer + validation + tests; (3) CLI host; (4) solution/CI + build-output wiring. +- **Rollback:** self-contained new project; revert PR. +- **Risks:** netstandard2.0 STJ sorted-key determinism → build ordering explicitly, cover with re-serialize test. + +### B1b — OpenStudio producer CLI +- **Objective:** emit `benchmark-OpenStudio.json` (`route="Native-OpenStudio"`) reusing the headless OS route; **empirically pin the consumption unit**. +- **Repo/branch:** `SAM_OpenStudio` / `feature/benchmark-b1-os-producer`. New `SAM_OpenStudio\benchmark\SAM.Analytical.OpenStudio.Benchmark\` (net8.0, `OutputType=Exe`), namespace `SAM.Analytical.OpenStudio.Benchmark`. + - `Modify\ToBenchmark.cs`: `BenchmarkDocument ToBenchmark(this AnalyticalModel, OpenStudioBenchmarkContext)` — reads model + space results, maps to DTOs (Guid+Name per space). + - `Classes\OpenStudioBenchmarkContext.cs`: engine/weather/design-day/route metadata (`OpenStudioVersion()`, `EnergyPlusVersion()`, epw hash, `RuntimeSeconds`, warning counts). + - `Program.cs`: `BenchmarkCli.Run` → load model → `ToOpenStudio(epw, outDir, runOptions{AssignIdealLoads=true})` (or `RunAsync`+timeout) → `ToBenchmark` → write JSON. + - HintPath ref `..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll`. + - **CI (`build.yml`):** add a step to clone `SAM_Validation` @ matching branch and `msbuild` **only** `SAM.Analytical.Benchmark.csproj` → `SAM_Validation\build\` before building SAM_OpenStudio. + - NUnit tests (`benchmark\` folder): **offline** producer test on a synthetic-SQLite model (reuse `C5`) → committed golden JSON; one `[Category("Simulation")]` live end-to-end on `SingleBox()` that also **captures the raw SQL consumption value to pin the unit**. +- **Reused APIs:** `ToOpenStudio/ToOpenStudioAsync`, `OpenStudioSimulationRunner`, `Convert.ToSAM`/`ToSAM_SpaceSimulationResults`, `AddResults`, `Query.Source()`, `OpenStudioVersion/EnergyPlusVersion`, `IntervalHourOfYear`. +- **Public API boundary:** `ToBenchmark` (OS-internal) + exe. No change to existing OS public APIs. +- **JSON ownership/versioning:** consumes B1a schema; stamps engine + `route`. +- **Invocation:** `benchmark-openstudio --model model.json --weather x.epw --out benchmark-OpenStudio.json`. +- **Acceptance:** offline golden passes anywhere; live `SingleBox()` emits valid v1 doc; **consumption unit pinned & documented**; C1–C7 green. +- **Non-goals:** no comparator/TAS/B2b/reporting. +- **Dependencies:** B1a. +- **Commit boundaries:** (1) CI clone+build-schema step; (2) `ToBenchmark`+context; (3) `Program.cs`+offline test+golden; (4) live sim test + unit pin. +- **Rollback:** additive exe; revert PR. +- **Risks:** consumption-unit ambiguity → pin is a gating commit; BuildAlls/CI ordering → clean full build gate. + +### B2 — TAS producer +- **Objective:** emit `benchmark-TAS.json` (`route="Native-TAS"`) from the native TAS route; buildable on dev laptop, runnable on TAS laptop; **validate the candidate conditioning pairing (D10)**. +- **Repo/branch:** `SAM_Tas` / `feature/benchmark-b2-tas-producer`. New `SAM_Tas\benchmark\SAM.Analytical.Tas.Benchmark\` (net8.0-windows, `OutputType=Exe`), namespace `SAM.Analytical.Tas.Benchmark`. + - `Modify\ToBenchmark.cs`: `BenchmarkDocument ToBenchmark(this AnalyticalModel, TasBenchmarkContext)`. + - `Classes\TasBenchmarkContext.cs`: TAS provenance (`Query\TasVersion.cs` reading TAS exe FileVersion from the registry install dir; weather/design-day identity; `Timings` duration; `successful`/exception capture). + - `Program.cs`: `BenchmarkCli.Run` → load model → ensure shared gbXML (via `SAM.Analytical.gbXML` export if absent) → `WorkflowSettings{ AddIZAMs=true, Sizing=true, Simulate=true, UnmetHours=true, … }` → `new WorkflowCalculator(settings).Calculate(model)` → **explicit building-results read** for annual energy → `ToBenchmark` → write JSON. Watchdog hard-timeout (no cancellation). + - HintPath ref `..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll`. + - **CI (`build.yml`):** same clone + build-only-schema step as B1b. + - **Docs:** `SAM_Tas\benchmark\README.md` — TAS-laptop prerequisites (licensed EDSL install, registry keys), CLI, outputs. +- **Reused APIs:** `WorkflowCalculator.Calculate`, `WorkflowSettings`, `Modify.AddResults`, `Query.Sizing`, `Query.UnmetHours`, `Modify.UpdateDesignLoads`, `Convert.ToSAM` (building results), `SAM.Analytical.gbXML` export, `Query.Source()`. +- **Public API boundary:** `ToBenchmark` (TAS-internal) + exe. **No new async/cancellation; no changes to existing SAM_Tas public APIs.** +- **JSON ownership/versioning:** consumes B1a schema unchanged. +- **Invocation (TAS laptop only):** `benchmark-tas --model model.json --gbxml model.xml --weather x.epw --tbd out.tbd --out benchmark-TAS.json`. +- **Tests:** **Portable (no TAS):** MSTest/NUnit asserting `ToBenchmark` maps a **hand-built** `AnalyticalModel` (results set in-memory, no COM) to a golden JSON; build-smoke. **Human-validation (TAS laptop, marked):** run on fixture gbXML; confirm valid JSON, non-zero loads, populated provenance. **Conditioning checkpoint:** compare TAS `SingleBox` outputs against expectations to confirm the D10 pairing before it is frozen. +- **Acceptance:** builds on dev laptop; portable golden passes anywhere; documented TAS-laptop run produces schema-valid JSON with model annual energy present; D10 pairing validated & recorded; no new public API. +- **Non-goals:** no TPD/production HVAC; no async; no live TAS in CI. +- **Dependencies:** B1a schema. +- **Commit boundaries:** (1) CI clone+build-schema + project + build-smoke; (2) `ToBenchmark`+context+`TasVersion`; (3) `Program.cs`+building-results+timeout; (4) portable golden; (5) README + conditioning-checkpoint notes. +- **Rollback:** additive exe; revert PR. +- **Risks:** annual energy omitted by default → dedicated wiring + asserted golden; TAS version unavailable → helper + manual-override field; partial headlessness → call `Calculate` directly, `net8.0-windows`; space-by-Name upstream → emit source-model Guid. + +### B3 — Comparator + reports +- **Objective:** headless comparator reading two benchmark JSONs; align model + spaces; abs/rel diffs; tolerance bands; emit `comparison.md`, `comparison.csv`, `comparison-summary.json` + gate status. **Independent of both engines.** +- **Repo/branch:** `SAM_Validation` / `feature/benchmark-b3-comparator`. New `SAM_Validation\SAM.Analytical.Benchmark.Compare\` (net8.0, `OutputType=Exe`; refs schema lib only), namespace `SAM.Analytical.Benchmark.Compare`. + - `Classes\`: `ComparisonResult`, `MetricComparison` (abs, rel, band, availability), `SpaceComparison`, `SpaceMatchDiagnostics`, `ToleranceProfile`. + - `Query\`: `Compare(tas, os, ToleranceProfile)`, `AlignSpaces(...)` (Guid-first, Name-fallback), `Band(value, other, profile)` (abs+rel, near-zero floor; **either side unavailable/null ⇒ "N/A", never fail**), `CircularHourDiff(a,b)`, `GateStatus(...)`. + - `Modify\`: `WriteMarkdown`, `WriteCsv`, `WriteSummaryJson` (deterministic; invariant culture). + - `Program.cs`: `BenchmarkCli.Run` → load both → schema-version check → `Compare` → write reports. + - MSTest `SAM.Analytical.Benchmark.Compare.Tests\` — **fabricated** pairs: exact, within-warn, within-fail, beyond-fail, near-zero, **null/unavailable metric**, missing/duplicate/split space, peak-hour year-boundary, unit-mismatch guard, model-total vs sum-of-spaces reconciliation, schema-version drift. +- **Reused APIs:** schema `Serializer`/DTOs; extend `SAM.Validation.Modify.Report_*` band logic into non-assert computation; `SAM.Core.Query.Round`. +- **Public API boundary:** `Compare` + report writers + `ToleranceProfile`. Reports = external contract for B5/B6. +- **JSON ownership/versioning:** `comparison-summary.json` comparator-owned (`summarySchemaVersion="1.0.0"`). +- **Invocation:** `benchmark-compare --tas benchmark-TAS.json --openstudio benchmark-OpenStudio.json --out ./report --tolerance-profile default`. +- **Tests:** all portable; human review of one rendered `comparison.md`. +- **Acceptance:** byte-stable reports; missing/dup/split spaces diagnosed; unavailable⇒N/A; circular hours handled; totals reconciled; gate from configurable bands; **no engine assembly referenced** (CI ref-check). +- **Non-goals:** no simulation/GH; no threshold validation. +- **Dependencies:** B1a. +- **Commit boundaries:** (1) classes + `Compare`/`AlignSpaces`; (2) banding + circular-hour + reconciliation; (3) report writers; (4) `Program.cs`; (5) fabricated tests. +- **Rollback:** self-contained; revert PR. +- **Risks:** non-determinism → sorted keys + invariant culture + fixed rounding + byte-stability test. + +### B4 — Benchmark corpus +- **Objective:** graduated **engine-independent** source models (single→multi-zone→reviewed production), separate from generated artefacts. +- **Repo/branch:** `SAM_Samples` / `feature/benchmark-b4-corpus`. Layout: `benchmark/models/{01-single-zone,02-two-zone,03-multistorey,04-production}/model.json` + per-model `README.md`; `.gitignore` for `**/generated/**` (tbd/tsd/osm/sql/gbxml). +- **Reused assets:** export OS `AnalyticalModelFixtures.SingleBox()` to `01-single-zone/model.json`; evaluate `SAM_Samples\Revit\000000_SAM_Live.zip` + `Tas\Tas.zip` for the production model (human review). +- **Public API boundary:** none. Stable Space Guids authored once, reused by both engines. +- **JSON ownership:** source `model.json` = SAM `AnalyticalModel` serialization (not benchmark docs). Generated engine files never committed (goldens excepted). +- **Invocation:** producers point at `benchmark/models/**/model.json`. +- **Tests:** each synthetic model runs through the OS producer offline path in CI (schema-valid). Production model **human-reviewed**. +- **Acceptance:** ≥3 synthetic models with stable Guids; production model selected + reviewed; generated artefacts git-ignored; each documented. +- **Non-goals:** no tolerance calibration; no large committed generated files. +- **Dependencies:** B1b, B3. +- **Commit boundaries:** (1) single/two-zone; (2) multistorey; (3) production + review notes; (4) .gitignore + docs. +- **Rollback:** data-only; revert PR. +- **Risks:** licensing/PII → review+strip; large files → SAM_Samples LFS. + +### B2b — Shared-gbXML → OpenStudio (optional, spike-gated) +- **Objective:** run OpenStudio from the **same gbXML** TAS consumes via SDK `GbXMLReverseTranslator`; results separately identified. +- **Repo/branch:** `SAM_OpenStudio` / `feature/benchmark-b2b-shared-gbxml`. + - **Spike (gate):** `tests\…\B2b\GbXmlReverseSpikeTests.cs` `[Category("Simulation")]` — `new OpenStudio.GbXMLReverseTranslator().loadModel(path)`, confirm runnable `Model`, run via `OpenStudioSimulationRunner`, assert non-zero loads. **If it fails, stop** and record findings. + - **On success:** `Convert\FromGbXML\Model.cs` (`FromSharedGbXML(path, out diagnostics)`); OS producer gains `--route shared-gbxml` → `benchmark-OpenStudio-gbXML.json` (`route="SharedGbXML-OpenStudio"`). Space alignment falls back to Name/geometry (Guids not preserved) — documented. +- **Reused APIs:** SDK `GbXMLReverseTranslator`, `OpenStudioSimulationRunner`, producer `ToBenchmark`. +- **Public API boundary:** additive route flag + `FromGbXML`. No change to native route. +- **JSON ownership/versioning:** same schema; distinct `route`. +- **Invocation:** `benchmark-openstudio --route shared-gbxml --gbxml shared.xml --weather x.epw --out benchmark-OpenStudio-gbXML.json`. +- **Tests:** spike (live, marked); offline mapping test if persistable; human review of first shared-gbXML comparison. +- **Acceptance:** spike documented; if built, docs schema-valid + clearly route-tagged. +- **Non-goals:** not a native-route replacement; no gbXML authoring. +- **Dependencies:** B1b, B3, corpus gbXML (B4). +- **Commit boundaries:** (1) spike+findings; (2) `FromGbXML`; (3) route flag+tests. +- **Rollback:** feature-flagged route; revert PR. +- **Risks:** reverse translator may drop constructions/loads → spike is the gate; strictly optional. + +### B5 — Grasshopper presentation +- **Objective:** lightweight GH component that **loads completed benchmark/comparison JSON** and presents results; requires **neither engine**; does **not** re-implement comparison. +- **Repo/branch:** **host repo decided at B5 kickoff** (D11); branch `feature/benchmark-b5-gh-viewer`. Constraint: refs **only** the schema lib + B3 comparator lib, **never** an engine assembly. + - Components: `BenchmarkLoad` (path → `BenchmarkDocument`), `BenchmarkCompareView` (two docs → `ComparisonResult` via B3 `Compare`), `BenchmarkReportOpen`. Reuse `GH_SAMVariableOutputParameterComponent`. +- **Reused APIs:** schema `Serializer`, B3 `Compare`/report writers. +- **Public API boundary:** GH components only. +- **Invocation:** interactive in Rhino/GH (**marked: requires Rhino/GH**). +- **Tests:** headless unit test of load/compare (portable); manual GH canvas smoke. +- **Acceptance:** loads a comparison with no engine installed; delegates comparison to B3. +- **Non-goals:** not the primary comparator; no simulation triggering. +- **Dependencies:** B3. +- **Commit boundaries:** (1) load; (2) compare-view; (3) report-open. +- **Rollback:** GH plugin addition; revert PR. +- **Risks:** accidental engine coupling → CI ref-check restricting references. + +### B6 — Documentation + first validated report +- **Objective:** end-to-end docs + the first real comparison on the production model across both laptops. +- **Repo/branch:** `SAM_Validation` / `feature/benchmark-b6-docs-report`. `docs/benchmark/{INSTALL,RUN-OPENSTUDIO,RUN-TAS,TRANSFER,COMPARE,INTERPRET,SHARED-GBXML,REPRODUCIBILITY}.md`; `docs/benchmark/reports/first-validated/`. +- **Reused APIs:** all prior CLIs. +- **Invocation:** documented commands from B1b/B2/B3 (+ B2b if built). +- **Tests:** link-check; **human-validation** sign-off; reproducibility (same inputs → byte-identical benchmark docs; stable comparison). +- **Acceptance:** a technician can run both laptops + compare + interpret from docs alone; first report committed with full provenance; limitations stated (provisional tolerances, excluded metrics, peak-load/consumption caveats). +- **Non-goals:** no threshold finalisation; no CI TAS execution. +- **Commit boundaries:** (1) install+run; (2) transfer+compare+interpret; (3) first report+provenance; (4) reproducibility. +- **Rollback:** docs/data; revert PR. +- **Risks:** cross-laptop path drift → relative layouts + portable JSON; provenance mismatch → comparator surfaces it. + +--- + +## 6. Proposed branch & PR sequence + +All branches created from `sow/2026-Q3`; all PRs target `sow/2026-Q3`. + +| Order | PR | Repo | Branch | +|---|---|---|---| +| 1 | B0 | SAM_Validation | `feature/benchmark-b0-methodology` | +| 2 | B1a | SAM_Validation | `feature/benchmark-b1-schema` | +| 3 | B1b | SAM_OpenStudio | `feature/benchmark-b1-os-producer` | +| 4 | B2 | SAM_Tas | `feature/benchmark-b2-tas-producer` | +| 5 | B3 | SAM_Validation | `feature/benchmark-b3-comparator` | +| 6 | B4 | SAM_Samples | `feature/benchmark-b4-corpus` | +| 7 | B2b (spike then build) | SAM_OpenStudio | `feature/benchmark-b2b-shared-gbxml` | +| 8 | B5 | *selected GH repo* | `feature/benchmark-b5-gh-viewer` | +| 9 | B6 | SAM_Validation | `feature/benchmark-b6-docs-report` | + +Ordering rule: **B1a first**; B1b/B2/B3 parallelisable after B1a (B2 also gates the D10 conditioning freeze); B4 after B1b; B5/B6 after B3 (B6 also after B2+B4). **One milestone per session.** + +--- + +## 7. Benchmark schema outline (v1.0.0) + +`benchmark-[-].json` — serialized by `SAM.Analytical.Benchmark.Query.Serializer` (System.Text.Json, invariant culture, sorted keys, fixed rounding). + +**Availability invariant (enforced by `Validate`):** `available=true ⇒ value` non-null; `available=false ⇒ value` **null**; a real measured zero is `value:0, available:true`. Unavailable metrics **never** carry `0`. + +```jsonc +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "…", "sourceModelGuid": "…", + "sourceFileHash": "sha256:…", // hash of the exact input model file + "canonicalModelHash": "sha256:…", // hash of the canonical SAM representation + "canonicalizationVersion": "1.0.0", // how the canonical hash was produced + "samCommit": "…", "runnerCommit": "…", + "engine": { "kind": "OpenStudio|TAS", "name": "EnergyPlus", "version": "24.x", "sdkVersion": "3.10.0" }, + "route": "Native-OpenStudio|Native-TAS|SharedGbXML-OpenStudio", + "weather": { "identity": "USA_MA_Boston…TMYx", "hash": "sha256:…" }, + "designDaySource": "DDY|EmbeddedModel|None", + "runTimestampUtc": "2026-07-21T…Z", "durationSeconds": 12.3, + "state": "Success|Failure", "warnings": ["…"], "notes": ["consumption unit pinned = kWh"] + }, + "model": { + "consumptionHeating": { "value": 1234.5, "unit": "kWh", "available": true }, + "consumptionCooling": { "value": 678.9, "unit": "kWh", "available": true }, + "peakHeatingLoad": { "value": 12.3, "unit": "kW", "available": true }, + "peakHeatingHour": { "value": 205, "unit": "hourOfYear", "available": true }, + "peakCoolingLoad": { "value": 9.8, "unit": "kW", "available": true }, + "peakCoolingHour": { "value": 4602, "unit": "hourOfYear", "available": true }, + "floorArea": { "value": 240.0, "unit": "m2", "available": true }, + "volume": { "value": 720.0, "unit": "m3", "available": true } + }, + "spaces": [ + { + "guid": "…32hex…", "name": "Office 1", + "area": { "value": 40.0, "unit": "m2", "available": true }, + "volume": { "value": 120.0, "unit": "m3", "available": true }, + "heating": { "designLoad": {"value":2100,"unit":"W","available":true}, + "peakLoad": {"value":2000,"unit":"W","available":true}, + "peakHour": {"value":210,"unit":"hourOfYear","available":true}, + "unmetHours": {"value":3,"unit":"h","available":true} }, + "cooling": { "designLoad": {"value":null,"unit":"W","available":false}, "…": "…" } + } + ] +} +``` + +- **Units enum:** `kWh, kW, W, Wh, m2, m3, hourOfYear, h`. Consumption unit pinned in B1b; loads confirmed W. +- **Excluded (documented):** per-space annual energy; monthly profiles. +- **`comparison-summary.json`** (comparator-owned, separate version): per-metric abs/rel/band, per-space alignment diagnostics, totals reconciliation, both inputs' provenance, overall gate. + +--- + +## 8. Test matrix by machine / environment + +| Test | Dev laptop (no engine) | OpenStudio laptop | TAS laptop | Rhino/GH | Human | +|---|---|---|---|---|---| +| Schema round-trip / determinism / **availability invariant** | ✅ | ✅ | ✅ | — | — | +| CLI host arg/exit-code | ✅ | ✅ | ✅ | — | — | +| OS producer offline (synthetic SQLite golden) | ✅ | ✅ | — | — | — | +| OS producer live end-to-end (`SingleBox`, `[Simulation]`) | — | ✅ | — | — | — | +| Consumption-unit empirical pin | — | ✅ | — | — | ✅ | +| TAS producer build-smoke | ✅ | ✅ | ✅ | — | — | +| TAS producer portable mapping golden (hand-built results) | ✅ | ✅ | ✅ | — | — | +| TAS producer live run + **D10 conditioning checkpoint** | — | — | ✅ | — | ✅ | +| Comparator fabricated pairs (all bands/edge cases + null) | ✅ | ✅ | ✅ | — | — | +| Report byte-stability | ✅ | ✅ | ✅ | — | — | +| B2b reverse-translate spike (`[Simulation]`) | — | ✅ | — | — | ✅ | +| GH viewer load/compare (headless unit) | ✅ | ✅ | — | — | — | +| GH viewer canvas smoke | — | — | — | ✅ | ✅ | +| First validated cross-laptop report | — | ✅ | ✅ | — | ✅ | +| Existing C1–C7 regression | ✅ | ✅ (Simulation subset) | — | — | — | + +--- + +## 9. Risks & mitigations + +| Risk | Impact | Mitigation | +|---|---|---| +| Consumption unit (Wh vs kWh) ambiguity | Wrong model-energy comparisons | Empirical pin in B1b; explicit unit tokens; provenance note; comparator refuses mismatched units. | +| Peak-load semantics differ (OS coincident-total vs TAS building-profile max) | False "fail" bands | Document in B0; label semantics; informational until corpus supports thresholds. | +| TAS model annual energy not populated by default | Missing model metric | Producer explicitly invokes building-results conversion; asserted in golden. | +| TAS: no engine version / weak warnings | Incomplete provenance | `TasVersion` helper + manual-override field; capture `successful`+exceptions; document limitation. | +| TAS not cancellable / partial headless | Runner hangs | Call `WorkflowCalculator.Calculate` directly; watchdog timeout; `net8.0-windows`. | +| Schema DLL must precede engines in CI | Broken CI builds | Per-repo `build.yml` clone+build-only-schema step (B1b/B2); root BuildAlls local-only; clean full-build gate. | +| Engine coupling leaking into comparator/GH | Violates isolation | Comparator/GH ref only schema (+comparator) lib; CI reference-check. | +| Unavailable metric encoded as 0 | Silent false comparison | `MetricValue` invariant (`available=false ⇒ value null`), enforced by `Validate` + tests. | +| B2b greenfield reverse translator | Wasted effort | Spike-gated; strictly optional; distinct route tag. | +| Report non-determinism | Non-reproducible gate | Sorted keys, invariant culture, fixed rounding; byte-stability tests. | +| Tolerances mistaken as validated | Overclaiming | Label provisional; configurable profiles; corpus-driven revision later. | +| Conditioning pairing frozen too early | Invalid comparison baked into B1/B2 | D10: candidate in B0, frozen only after B2 SingleBox validation. | +| Cross-laptop path drift | Irreproducible | Portable JSON, relative layouts, no absolute paths committed. | + +--- + +## 10. Completion checklist + +- [ ] B0 docs merged & human-reviewed; conditioning pairing labelled *candidate*. +- [ ] B1a schema lib + serializer + CLI host + tests merged; DLL in `SAM_Validation\build\`; deterministic serialization + **availability invariant** proven. +- [ ] B1b OS producer merged; offline golden + live `SingleBox` pass; **consumption unit pinned**; CI clone+build-schema step added; C1–C7 green. +- [ ] B2 TAS producer merged; builds on dev laptop; portable golden passes; TAS-laptop run documented & validated; model annual energy present; **D10 pairing validated & frozen**; no new public API. +- [ ] B3 comparator + reports merged; all edge cases (incl. null/unavailable) pass; engine-independent (ref-checked); byte-stable. +- [ ] B4 corpus: ≥3 synthetic + 1 reviewed production model; generated artefacts git-ignored. +- [ ] B2b spike run & documented; producer route built only if spike passed; route-tagged. +- [ ] B5 host repo chosen at kickoff; viewer loads comparison with no engine installed; delegates to B3. +- [ ] B6 docs enable two-laptop run + compare; first validated report committed with provenance; limitations stated. +- [ ] No TAS deps in SAM_OpenStudio; no OpenStudio deps in SAM_Tas; comparator independent of both. +- [ ] Every milestone left repos buildable & testable; one milestone per PR; no oversized PRs. + +--- + +## 11. Recommended first implementation prompt (B0 only) + +> Prepend the **Mandatory Git & repository rules** (§13) to this prompt. +> +> **Implement milestone B0 (methodology & contracts) for the SAM TAS↔OpenStudio benchmark.** Repo `SAM_Validation`, branch `feature/benchmark-b0-methodology` created from `sow/2026-Q3`; PR targets `sow/2026-Q3`. **Docs only — no code.** +> +> Create under `SAM_Validation/docs/benchmark/`: +> - `METHODOLOGY.md` — the two-stage file-mediated workflow; the four routes (Native SAM→TAS, Native SAM→OpenStudio, shared-gbXML→both [optional], later TPD production [informational]); the **candidate** primary conditioning pairing **OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing** (state explicitly it is *not frozen* until the first TAS SingleBox run in B2 confirms meaningful conditioning, annual energy and loads); production HVAC is a separately-labelled later comparison. +> - `METRICS.md` — each metric's definition, canonical unit, sign convention, exact both-route SAM result-parameter source; include the §2.10 availability matrix; **explicitly list excluded metrics** (per-space annual energy; monthly profiles) with evidence; flag caveats: (a) peak-load definition differs (OpenStudio coincident-total vs TAS building-profile max); (b) model consumption stored-magnitude (Wh vs kWh) **unresolved, pinned empirically in B1b**. +> - `SCHEMA.md` — prose spec of the v1.0.0 schema in §7: provenance (**dual hashes `sourceFileHash` + `canonicalModelHash` + `canonicalizationVersion`**), model, spaces; every metric a `MetricValue` with explicit unit + `available` flag and the **availability invariant** (`available=false ⇒ value null`; real 0 ⇒ 0+available; **never 0 for unavailable**); `schemaVersion` semver policy + comparator compatibility (equal major required; minor drift warns). +> - `TOLERANCES.md` — provisional bands **±5% warn / ±15% fail** as **configurable reporting bands, NOT validated thresholds**; near-zero absolute-floor rule; unavailable⇒"N/A" (never fail); peak-hour circular/year-boundary comparison; model-total vs sum-of-spaces reconciliation. +> - `GLOSSARY.md` — engine, route, provenance, canonical unit, tolerance band, gate status, canonicalization. +> +> Constraints: no absolute/machine-specific paths; align with verified findings (System.Text.Json, Space Guid identity, `Query.Source()` provenance, `IntervalHourOfYear` peak hours). Keep each doc scannable. Commit in three boundaries: (1) methodology+glossary; (2) metrics+units; (3) tolerances+schema-spec. Open a PR to `sow/2026-Q3` titled "B0 — benchmark methodology & contracts". Do not modify any code or existing tests. Mark the energy-modeller review as a required human check before merge. + +--- + +## 12. Model allocation & session discipline + +Run **one milestone per session**; do not hand B1–B6 to a single model in one continuous run. + +| Milestone | Recommended model | +|---|---| +| B0 documentation | **Codex 5.6-SOL High** | +| B1a schema, serializer, deterministic tests | **Codex 5.6-SOL High** | +| B1b OpenStudio producer | **Codex 5.6-SOL High** | +| B2 TAS producer | **Kimi**, followed by **Codex review** | +| B3 comparator & reports | **Codex 5.6-SOL High** | +| B4 corpus preparation | **Kimi** | +| B2b OpenStudio SDK spike | **Codex 5.6-SOL High** | +| B5 Grasshopper components | **Codex 5.6-SOL High** | +| B6 documentation & report assembly | **Kimi** | + +Kimi fits B2 (well-specified orchestration around existing APIs) and B4/B6 (mechanical corpus/doc assembly). Deterministic-serialization, comparator, and SDK-spike work go to Codex 5.6-SOL High. **Immediate next step: B0 with Codex 5.6-SOL High; after B0 is reviewed and merged, start B1a in a fresh session.** + +Because sessions run on different laptops, **each session ends by committing and pushing its feature branch(es)** so the next session resumes from remote — see the *Multi-laptop continuity* rule in §13. + +--- + +## 13. Mandatory Git & repository rules (include in EVERY implementation prompt) + +All implementation work starts from the current `sow/2026-Q3` branch in each affected repository. For every repository modified: + +1. Confirm the working tree is clean. +2. Fetch current remote branches. +3. Check out `sow/2026-Q3`. +4. Fast-forward from `origin/sow/2026-Q3`. +5. Create the milestone-specific `feature/benchmark-*` branch. +6. Commit only files belonging to that repository and milestone. +7. Push the feature branch. +8. Open the PR against `sow/2026-Q3` — never `master` or another feature branch. + +```bash +git status +git fetch origin +git checkout sow/2026-Q3 +git pull --ff-only origin sow/2026-Q3 +git checkout -b +``` + +- If a repo lacks `sow/2026-Q3`, or it cannot fast-forward cleanly, **stop before modifying files** and report the exact repo and Git state. +- **Do not reuse one branch across repositories.** Each repo gets its own feature branch, commits and PR. +- **Before implementation, verify the files named in the plan actually live in the stated repository.** In particular, confirm the build-order change lives in each engine repo's own `.github/workflows/build.yml` (verified: the root `BuildAlls*` files are untracked and belong to no repo; do **not** rely on or commit them). + +### Multi-laptop continuity (REQUIRED — Michal works across several laptops) + +Michal moves between several laptops with different Windows user profiles (see [[two-laptop-workspace-paths]]). To avoid stranding work on one machine: + +- **Commit and push the feature branch at the end of every B stage/session** — a stage is only "done for this session" once `git push` has succeeded. Never leave a milestone's work committed-but-unpushed, or uncommitted, on a single laptop. +- **Push work-in-progress commits too**, not only at PR time, so any laptop always has the latest state. Keep messages conventional (`docs:`/`feat:`/`fix:`/…) and end them with the `Co-Authored-By` trailer. +- **When resuming a stage on another laptop**, do NOT recreate the branch: `git fetch origin` → `git checkout feature/benchmark-` → `git pull --ff-only origin feature/benchmark-`, then continue. If the local branch is behind and cannot fast-forward, stop and report before editing. +- Because each repo has its own feature branch, **push every affected repo's branch** at stage end (a milestone that touches two repos must push both). diff --git a/docs/benchmark/METHODOLOGY.md b/docs/benchmark/METHODOLOGY.md new file mode 100644 index 0000000..06f6878 --- /dev/null +++ b/docs/benchmark/METHODOLOGY.md @@ -0,0 +1,96 @@ +# Benchmark methodology + +## Purpose + +This programme compares results from EDSL Tas and OpenStudio/EnergyPlus for the same SAM `AnalyticalModel`. It is a cross-engine benchmark, not an assertion that either engine is ground truth. + +The benchmark is designed to be: + +- engine-neutral: neither engine loads or calls the other; +- repeatable: inputs, versions, routes, and weather are recorded; +- portable: engines exchange versioned JSON files, not runtime objects; +- auditable: missing data, alignment fallbacks, and semantic caveats remain visible. + +## Workflow + +The workflow is two-stage and file-mediated: + +1. Each engine runs independently from an agreed source model and weather file. +2. Each producer converts SAM results into the same benchmark JSON contract. +3. The benchmark JSON files are transferred to any machine that can run the headless comparator. +4. The comparator aligns spaces, computes differences and reporting bands, and writes Markdown, CSV, and summary JSON reports. + +There is no direct runtime coupling between Tas and OpenStudio. A failed engine run may emit failure provenance, but it must not emit invented measurements. + +## Routes + +Every document records one route. Results from different routes must not be merged or relabelled. + +| Route | Purpose | Gate role | +|---|---|---| +| Native-TAS | SAM model → shared gbXML workflow → Tas TBD/TSD → SAM results | Candidate primary comparison | +| Native-OpenStudio | SAM model → OpenStudio model → EnergyPlus SQL → SAM results | Candidate primary comparison | +| SharedGbXML-TAS / SharedGbXML-OpenStudio | The same gbXML is supplied to both engines | Optional diagnostic comparison, spike-gated | +| TPD production HVAC | Tas production plant through TPD | Later, separately labelled, informational only | + +The native routes test the complete production translation paths as well as the simulation engines. The optional shared-gbXML routes narrow the comparison by holding the interchange model constant. They do not replace the native routes. + +## Candidate conditioning pairing + +The candidate primary pairing is: + +- OpenStudio Ideal Loads; and +- Tas thermostats with IZAMs and TBD sizing. + +This pairing is **not frozen** in B0. B2 must run the first Tas `SingleBox` validation and confirm meaningful conditioning, annual heating/cooling energy, and heating/cooling loads. An energy modeller must review that evidence before the pairing can become the primary gate. If the checkpoint fails, the pairing and affected contracts must be revised explicitly; production TPD HVAC must not be substituted silently. + +## Inputs and run controls + +A comparison run must use the same intended source model, weather data, and agreed design-day basis. Provenance records both the exact source-file hash and the canonical SAM-model hash so that byte-level and semantic-model equality can be assessed separately. + +Each producer records: + +- source model identity and both hashes; +- SAM and producer revisions; +- engine, SDK, route, weather, and design-day information; +- UTC run time, duration, state, warnings, and notes. + +Absolute machine paths are runtime details and must not be committed or used as identities. Files are identified by portable names and content hashes. + +## Result extraction + +Producers read the existing SAM result model. Whole-model values come from `AnalyticalModelSimulationResult`; space values come from `SpaceSimulationResult` separated by `LoadType.Heating` and `LoadType.Cooling`. Engine-specific `Query.Source()` values are retained as provenance rather than treated as metric values. + +Peak hours use the SAM interval-hour-of-year convention: integers `0..8759`, with `0` representing the interval ending at 01:00 on 1 January. Date-time conversion uses `Core.Query.IntervalHourOfYear` where a timestamp is available. + +Unavailable results remain unavailable. They are represented by `available: false` and `value: null`; zero is reserved for a measured zero. + +## Space identity and alignment + +Both native producers begin with the same SAM spaces and emit each space's stable GUID and name. The comparator aligns spaces in this order: + +1. exact GUID match; +2. unique, normalised name match when GUID matching is unavailable; +3. no match, reported as a diagnostic. + +Duplicate GUIDs, duplicate fallback names, missing spaces, and ambiguous matches are never silently combined. Shared-gbXML translation may lose SAM GUIDs, so fallback use must be visible in the report. Geometry-based investigation may support human diagnosis but is not an automatic v1 match rule. + +## Comparison and gate interpretation + +The comparator checks schema compatibility, units, provenance, space alignment, metric availability, differences, and reconciliation before deriving a gate status. A unit mismatch is a contract error, not a numerical comparison. + +The default 5% warning and 15% failure bands are provisional reporting bands. They are configurable and are not scientifically validated thresholds. Missing metrics report `N/A` and cannot fail a numerical band; missing required coverage and invalid provenance are reported separately. + +See [METRICS.md](METRICS.md), [SCHEMA.md](SCHEMA.md), and [TOLERANCES.md](TOLERANCES.md) for the normative contracts. + +## Required review before merge + +An energy modeller must review: + +- the metric definitions and sign conventions; +- the different whole-model peak-load semantics; +- the unresolved annual-consumption stored magnitude; +- the candidate conditioning pairing and its B2 checkpoint; +- the provisional nature of the reporting bands. + +B0 is documentation only. It does not validate tolerances, freeze the conditioning pairing, or implement a producer or comparator. diff --git a/docs/benchmark/METRICS.md b/docs/benchmark/METRICS.md new file mode 100644 index 0000000..2a855f3 --- /dev/null +++ b/docs/benchmark/METRICS.md @@ -0,0 +1,90 @@ +# Benchmark metrics + +## Contract rules + +- Every metric carries an explicit canonical unit. A producer converts at emission time; a comparator never infers units from a SAM parameter name. +- Heating and cooling are separate, non-negative magnitudes. No signed heating/cooling convention is used. +- A negative magnitude is invalid unless the producer first applies a documented engine-specific sign conversion. +- A metric is available only when the route produced a meaningful value. Missing values use `available: false` and `value: null`. +- Peak hours use interval hour of year `0..8759`. SAM timestamp conversion uses `Core.Query.IntervalHourOfYear`. + +## Whole-model metrics + +| Schema field | Definition | Unit | OpenStudio SAM source | Tas SAM source | +|---|---|---|---|---| +| `consumptionHeating` | Annual delivered heating energy for conditioned spaces on the selected route | `kWh` | `AnalyticalModelSimulationResultParameter.ConsumptionHeating`, populated from `OpenStudioSimulationResultSet.TotalAnnualHeating` | `AnalyticalModelSimulationResultParameter.ConsumptionHeating`, populated by `Convert.ToSAM_AnalyticalModelSimulationResult` from the sum of `tsdBuildingArray.heatingProfile` | +| `consumptionCooling` | Annual delivered cooling energy for conditioned spaces on the selected route | `kWh` | `AnalyticalModelSimulationResultParameter.ConsumptionCooling`, populated from `OpenStudioSimulationResultSet.TotalAnnualCooling` | `AnalyticalModelSimulationResultParameter.ConsumptionCooling`, populated by `Convert.ToSAM_AnalyticalModelSimulationResult` from the sum of `tsdBuildingArray.coolingProfile` | +| `peakHeatingLoad` | Maximum whole-model heating-load magnitude reported by the route | `kW` | `AnalyticalModelSimulationResultParameter.PeakHeatingLoad`, from `PeakHeatingLoadTotal` | `AnalyticalModelSimulationResultParameter.PeakHeatingLoad`, maximum of `tsdBuildingArray.heatingProfile` | +| `peakHeatingHour` | Interval hour of year at the whole-model heating peak | `hourOfYear` | `AnalyticalModelSimulationResultParameter.PeakHeatingHour`, from `PeakHeatingHourTotal` | `AnalyticalModelSimulationResultParameter.PeakHeatingHour`, index of the maximum heating-profile value | +| `peakCoolingLoad` | Maximum whole-model cooling-load magnitude reported by the route | `kW` | `AnalyticalModelSimulationResultParameter.PeakCoolingLoad`, from `PeakCoolingLoadTotal` | `AnalyticalModelSimulationResultParameter.PeakCoolingLoad`, maximum of `tsdBuildingArray.coolingProfile` | +| `peakCoolingHour` | Interval hour of year at the whole-model cooling peak | `hourOfYear` | `AnalyticalModelSimulationResultParameter.PeakCoolingHour`, from `PeakCoolingHourTotal` | `AnalyticalModelSimulationResultParameter.PeakCoolingHour`, index of the maximum cooling-profile value | +| `floorArea` | Sum of model-space floor areas represented by the run | `m2` | `AnalyticalModelSimulationResultParameter.FloorArea`, summed from source SAM `SpaceParameter.Area` | `AnalyticalModelSimulationResultParameter.FloorArea`, read from Tas building data by `Core.Tas.Query.FloorArea` | +| `volume` | Sum of model-space volumes represented by the run | `m3` | `AnalyticalModelSimulationResultParameter.Volume`, summed from source SAM `SpaceParameter.Volume` | `AnalyticalModelSimulationResultParameter.Volume`, read from Tas building data by `Core.Tas.Query.Volume` | + +### Annual-consumption open item + +The canonical unit is `kWh`, but the stored SAM magnitude is not yet trusted: + +- the SAM enum label and the OpenStudio result-set contract say kWh; +- OpenStudio copies its result-set value directly into the SAM parameter; +- existing legacy consumers, including a Tas Grasshopper building-results path, divide the stored value by 1000. + +B1b must compare the raw EnergyPlus SQL value, the OpenStudio result-set value, and the emitted value on a live `SingleBox` run. Until that empirical pin is recorded, a producer must not guess from the enum name. The Tas conversion must then be verified against the same canonical unit. Any interim unavailable value is `null`, never zero. + +### Whole-model peak caveat + +The values are comparable reporting metrics but do not yet have proven identical semantics. OpenStudio reports its coincident-total result, while Tas takes the maximum of the Tas building profile. Differences can therefore reflect aggregation and reporting semantics as well as engine physics. These metrics remain informational until corpus evidence supports a stronger gate interpretation. + +## Per-space metrics + +Each space contains `area` and `volume` plus `heating` and `cooling` result groups. SAM results are selected by both space identity and `SpaceSimulationResultParameter.LoadType`. + +| Schema field | Definition | Unit | OpenStudio SAM source | Tas SAM source | +|---|---|---|---|---| +| `area` | Floor area of the source SAM space represented by the result | `m2` | `SpaceSimulationResultParameter.Area`; where absent on the result, source `SpaceParameter.Area` may be copied with provenance | `SpaceSimulationResultParameter.Area`, populated from Tas zone floor area | +| `volume` | Volume of the source SAM space represented by the result | `m3` | `SpaceSimulationResultParameter.Volume`; where absent on the result, source `SpaceParameter.Volume` may be copied with provenance | `SpaceSimulationResultParameter.Volume`, populated from Tas zone volume | +| `heating.designLoad` / `cooling.designLoad` | Route sizing load for the space and load type | `W` | `SpaceSimulationResultParameter.DesignLoad` only when a sizing-derived result exists; the current primary mapping otherwise has only a peak proxy and must emit unavailable rather than silently relabel `Load` | `SpaceSimulationResultParameter.DesignLoad`, populated from TBD zone `maxHeatingLoad` / `maxCoolingLoad` by `UpdateDesignLoads` | +| `heating.peakLoad` / `cooling.peakLoad` | Maximum simulated space-load magnitude for the load type | `W` | `SpaceSimulationResultParameter.Load`; `ToSAM_SpaceSimulationResults` converts engine kW to W | `SpaceSimulationResultParameter.Load`, populated from the corresponding Tas zone load/profile result | +| `heating.peakHour` / `cooling.peakHour` | Interval hour of year at the space peak | `hourOfYear` | `SpaceSimulationResultParameter.LoadIndex` | `SpaceSimulationResultParameter.LoadIndex` | +| `heating.unmetHours` / `cooling.unmetHours` | Number of annual intervals in which the route did not meet the applicable setpoint | `h` | `SpaceSimulationResultParameter.UnmetHours`, mapped from the route's setpoint-not-met result | `SpaceSimulationResultParameter.UnmetHours`, calculated by `Query.UnmetHours` and copied onto the load-type result | + +Design load and simulated peak load are distinct metrics. A producer must not populate both from one SAM `Load` value without an explicit future schema decision. + +`OccupiedUnmetHours` and `UnmetHourFirstIndex` exist in SAM but are outside the v1 primary metric set. They may be retained as producer diagnostics; they must not be substituted for `unmetHours`. + +## Availability matrix + +| Metric | OpenStudio native | Tas native (gbXML) | Neutral schema | +|---|---|---|---| +| Model annual heating/cooling | Available, subject to B1b unit pin | Requires explicit building-results read | Nullable | +| Model peak heating/cooling load | Available; coincident-total semantic | Available; building-profile maximum | Nullable; semantic caveat | +| Model peak heating/cooling hour | Available | Available | Nullable; circular comparison | +| Model floor area / volume | Available | Available | Nullable | +| Space area / volume | Available | Available | Nullable | +| Space peak load and hour | Available | Available | Nullable | +| Space design load | Peak proxy only in the primary result mapping | Available from sizing | Nullable; do not conflate with peak load | +| Space unmet hours | Available when requested/extracted | Available when requested/calculated | Nullable | +| **Per-space annual energy** | **Excluded** | **Excluded** | **Not present** | +| **Monthly profiles** | **Excluded** | **Excluded for the native TBD/TSD route** | **Not present** | + +## Excluded metrics + +### Per-space annual energy + +The v1 contract excludes per-space annual heating and cooling energy. `SpaceSimulationResultParameter` has no annual-energy parameter, and neither native producer attaches a common per-space annual-energy value to the SAM result graph. OpenStudio's internal extraction dictionaries do not create a shared SAM contract by themselves. + +### Monthly profiles + +The v1 contract excludes monthly energy and load profiles. The common native result routes provide scalar annual/peak values and hourly temperature-related results, not a shared monthly profile. Tas TPD exposes monthly plant-side data, but TPD is a later production-HVAC route and cannot supply a metric for the primary idealised comparison. + +Adding either excluded metric requires a schema version change, sources from both primary routes, canonical units, and energy-modeller review. + +## Review checklist + +Before B0 merges, an energy modeller must confirm the definitions above, especially: + +- annual delivered-energy scope; +- whole-model peak aggregation differences; +- sizing load versus simulated peak load; +- setpoint-not-met semantics; and +- the B1b consumption-unit validation procedure. diff --git a/docs/benchmark/SCHEMA.md b/docs/benchmark/SCHEMA.md new file mode 100644 index 0000000..e2193ca --- /dev/null +++ b/docs/benchmark/SCHEMA.md @@ -0,0 +1,202 @@ +# Benchmark JSON schema v1.0.0 + +## Scope + +`benchmark-[-].json` is the portable hand-off between an engine-specific producer and the engine-neutral comparator. The schema owns measurements and run provenance; it does not serialize engine runtime objects or the SAM result graph. + +The implementation will use System.Text.Json with invariant-culture numbers, deterministic property order, deterministic array order, and fixed numeric rounding. JSON object order is not semantic, but deterministic output is required for reproducibility. + +## Top-level document + +| Field | Type | Requirement | +|---|---|---| +| `schemaVersion` | string | Required semantic version; B0 defines `1.0.0` | +| `provenance` | object | Required | +| `model` | object | Required; every declared metric is present as a `MetricValue` | +| `spaces` | array | Required; sorted deterministically by GUID then ordinal name | + +Unknown fields do not change the meaning of known fields. A writer must not emit non-finite JSON numbers (`NaN` or infinity). + +## Provenance + +| Field | Type | Meaning | +|---|---|---| +| `sourceModelName` | string | Portable model label; not a filesystem path | +| `sourceModelGuid` | string or null | SAM model GUID in 32-character lowercase hexadecimal form; null only for a pre-model failure | +| `sourceFileHash` | string or null | SHA-256 of the exact input model-file bytes; null only when failure prevents hashing | +| `canonicalModelHash` | string or null | SHA-256 of the canonical SAM representation; null only when failure prevents canonicalization | +| `canonicalizationVersion` | string | Semantic version of the canonicalization rules | +| `samCommit` | string | SAM source revision used for the run | +| `runnerCommit` | string | Producer repository revision used for the run | +| `engine` | object | Engine kind, display name, engine version, and SDK version | +| `route` | string | Exact route identifier; routes are never inferred from engine kind | +| `weather` | object | Portable identity plus exact-file SHA-256 | +| `designDaySource` | string | `DDY`, `EmbeddedModel`, or `None` | +| `runTimestampUtc` | string | ISO 8601 UTC instant with `Z` suffix | +| `durationSeconds` | number | Non-negative elapsed wall-clock seconds | +| `state` | string | `Success` or `Failure` | +| `resultSources` | array of strings | Distinct non-empty SAM `Query.Source()` values retained from attached results | +| `warnings` | array of strings | Deterministically ordered warnings; no machine-specific paths | +| `notes` | array of strings | Deterministically ordered material assumptions or limitations | + +Hashes use `sha256:` followed by 64 lowercase hexadecimal characters. `sourceFileHash` proves byte equality of the input file. `canonicalModelHash` proves equality under the named canonicalization rules; it must be calculated independently of the source-file bytes. One hash must never be copied into the other merely because the current serialization happens to match. + +For a successful run, both model hashes and the weather hash are required. A failure document may use `null` only for a hash that could not be computed, and must explain the reason in `warnings` or `notes`. Canonicalization rules must remove non-semantic ordering and formatting variation without removing model content that can affect simulation. + +Engine fields are: + +| Field | Type | Meaning | +|---|---|---| +| `kind` | string | `OpenStudio` or `TAS` | +| `name` | string | Simulation engine name, such as `EnergyPlus` or `Tas` | +| `version` | string or null | Engine version; null only when unavailable and explained | +| `sdkVersion` | string or null | Translation/runtime SDK version when applicable | + +Weather fields are `identity` and `hash`. `identity` is a portable station/file identity, not an absolute path. + +v1 route tokens are `Native-OpenStudio`, `Native-TAS`, `SharedGbXML-OpenStudio`, and `SharedGbXML-TAS`. `TPD-TAS` is reserved for the later, informational production-HVAC comparison; a v1 primary-gate producer must not emit it. The two shared-gbXML tokens identify the paired documents generated from the same exact interchange file. + +## MetricValue + +Every metric has the same shape: + +```json +{ + "value": 123.4, + "unit": "kWh", + "available": true +} +``` + +`value` is a JSON number or `null`; the DTO representation is nullable. `unit` is always present, including for unavailable metrics. v1 unit tokens are: + +- `kWh` +- `kW` +- `W` +- `Wh` +- `m2` +- `m3` +- `hourOfYear` +- `h` + +### Availability invariant + +- `available: true` requires a finite, non-null `value`. +- `available: false` requires `value: null`. +- A measured zero is `value: 0` and `available: true`. +- Zero must never stand for missing, failed, unsupported, or not-applicable data. + +Violating the invariant makes the document invalid. The comparator does not repair it. + +Hour-of-year values are represented by the common numeric field but must be integers in `0..8759`. Hours and physical magnitudes must be non-negative. Units must match the field contract in [METRICS.md](METRICS.md). + +## Model object + +The model object contains these required `MetricValue` properties: + +- `consumptionHeating` +- `consumptionCooling` +- `peakHeatingLoad` +- `peakHeatingHour` +- `peakCoolingLoad` +- `peakCoolingHour` +- `floorArea` +- `volume` + +Required property means the metric wrapper is present, not that a measurement is available. Unsupported or unresolved measurements use the availability invariant. + +## Space object + +Each space contains: + +| Field | Type | Requirement | +|---|---|---| +| `guid` | string | Source SAM space GUID, 32-character lowercase hexadecimal; nullable only for a route that cannot preserve it | +| `name` | string | Source or translated space name | +| `area` | `MetricValue` | Unit `m2` | +| `volume` | `MetricValue` | Unit `m3` | +| `heating` | load-type object | Required | +| `cooling` | load-type object | Required | + +Each load-type object contains `designLoad` (`W`), `peakLoad` (`W`), `peakHour` (`hourOfYear`), and `unmetHours` (`h`). Missing load-type results still emit all wrappers as unavailable. + +Native routes must emit the GUID and name from the common source SAM `Space`. The comparator matches GUID first and then a unique normalised name. It diagnoses null/duplicate GUIDs, duplicate fallback names, ambiguous matches, and unmatched spaces. + +## Example outline + +```json +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "SingleBox", + "sourceModelGuid": "0123456789abcdef0123456789abcdef", + "sourceFileHash": "sha256:<64 lowercase hex characters>", + "canonicalModelHash": "sha256:<64 lowercase hex characters>", + "canonicalizationVersion": "1.0.0", + "samCommit": "", + "runnerCommit": "", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "", + "hash": "sha256:<64 lowercase hex characters>" + }, + "designDaySource": "DDY", + "runTimestampUtc": "2026-07-21T12:00:00Z", + "durationSeconds": 12.3, + "state": "Success", + "resultSources": [""], + "warnings": [], + "notes": [] + }, + "model": { + "consumptionHeating": { "value": 1234.5, "unit": "kWh", "available": true }, + "consumptionCooling": { "value": null, "unit": "kWh", "available": false }, + "peakHeatingLoad": { "value": 12.3, "unit": "kW", "available": true }, + "peakHeatingHour": { "value": 205, "unit": "hourOfYear", "available": true }, + "peakCoolingLoad": { "value": 9.8, "unit": "kW", "available": true }, + "peakCoolingHour": { "value": 4602, "unit": "hourOfYear", "available": true }, + "floorArea": { "value": 240.0, "unit": "m2", "available": true }, + "volume": { "value": 720.0, "unit": "m3", "available": true } + }, + "spaces": [ + { + "guid": "fedcba9876543210fedcba9876543210", + "name": "Office 1", + "area": { "value": 40.0, "unit": "m2", "available": true }, + "volume": { "value": 120.0, "unit": "m3", "available": true }, + "heating": { + "designLoad": { "value": 2100, "unit": "W", "available": true }, + "peakLoad": { "value": 2000, "unit": "W", "available": true }, + "peakHour": { "value": 210, "unit": "hourOfYear", "available": true }, + "unmetHours": { "value": 3, "unit": "h", "available": true } + }, + "cooling": { + "designLoad": { "value": null, "unit": "W", "available": false }, + "peakLoad": { "value": null, "unit": "W", "available": false }, + "peakHour": { "value": null, "unit": "hourOfYear", "available": false }, + "unmetHours": { "value": null, "unit": "h", "available": false } + } + } + ] +} +``` + +## Version and compatibility policy + +`schemaVersion` follows semantic versioning: + +- major: incompatible field meaning, structure, unit, or requiredness change; +- minor: backward-compatible additive fields or enum values; +- patch: clarification or serialization fix that does not change meaning. + +Comparator and document major versions must be equal. A major mismatch is rejected before metric comparison. An equal major with a different minor version produces a visible compatibility warning; comparison may continue only when all required v1 fields and understood enum values validate. Patch drift does not warn. Producers must not emit a version newer than the contract they actually implement. + +`canonicalizationVersion` is versioned independently. A canonicalization-version mismatch does not prove different models: the comparator reports it and does not directly compare the canonical hashes as equivalent evidence. + +The comparator-owned `comparison-summary.json` is a separate contract and carries its own `summarySchemaVersion`. diff --git a/docs/benchmark/TOLERANCES.md b/docs/benchmark/TOLERANCES.md new file mode 100644 index 0000000..f7cbb15 --- /dev/null +++ b/docs/benchmark/TOLERANCES.md @@ -0,0 +1,106 @@ +# Benchmark tolerances and reporting bands + +## Status + +The B0 bands are configurable reporting aids, **not validated acceptance thresholds**. They support early corpus review and must not be presented as evidence that two engines are equivalent. + +The default relative bands are: + +| Band | Absolute relative difference | +|---|---| +| Pass | Less than 5% | +| Warn | At least 5% and less than 15% | +| Fail | At least 15% | + +Reports must name the tolerance profile and include its actual values. A changed profile changes the report classification and must not be hidden. + +## Difference calculation + +For two available, finite, unit-compatible scalar values `tas` and `openStudio`: + +```text +signedDifference = openStudio - tas +absoluteDifference = abs(signedDifference) +scale = max(abs(tas), abs(openStudio), nearZeroFloor(metric)) +relativeDifference = absoluteDifference / scale +``` + +The magnitude is symmetric: swapping the two engines does not change the band. The signed difference is retained to show direction. `nearZeroFloor(metric)` is a positive, canonical-unit value configured per metric or metric family; B0 does not claim scientifically validated floor values. + +The comparator must report the absolute and relative differences used for classification. It must not round inputs before calculating a band; rounding is presentation-only. + +## Near-zero rule + +The floor prevents tiny denominators from turning negligible absolute differences into extreme percentages. When both magnitudes are below the configured floor, the formula is equivalent to comparing the absolute difference with 5% and 15% of that floor. + +Examples for a hypothetical load floor of `100 W`: + +| Tas | OpenStudio | Difference | Effective relative difference | Band | +|---:|---:|---:|---:|---| +| `0 W` | `0 W` | `0 W` | `0%` | Pass | +| `0 W` | `4 W` | `4 W` | `4%` | Pass | +| `0 W` | `10 W` | `10 W` | `10%` | Warn | +| `0 W` | `20 W` | `20 W` | `20%` | Fail | + +The example explains the rule; `100 W` is not a programme default. Each production profile requires energy-modeller review of its floors. + +## Unavailable and invalid values + +If either metric has `available: false` and `value: null`, the comparison band is `N/A`. `N/A` never becomes Pass, Warn, Fail, or a numeric zero and never causes a numerical gate failure. + +The following are contract errors rather than `N/A` comparisons: + +- the `available`/`value` invariant is broken; +- units differ; +- a value is non-finite; +- a magnitude is negative without a documented conversion; or +- an hour-of-year value is outside `0..8759` or non-integral. + +Coverage diagnostics count unavailable required metrics and unmatched spaces separately. A numerically passing report with poor coverage must not be described as complete. + +## Peak-hour comparison + +Peak hours are circular over a normal 8760-hour year. For valid hours `a` and `b`: + +```text +directDifference = abs(a - b) +circularDifference = min(directDifference, 8760 - directDifference) +``` + +Thus hours `8759` and `0` differ by one hour, not 8759 hours. Percentage bands do not apply to `hourOfYear`; the tolerance profile must provide absolute warn/fail thresholds in hours. Until those thresholds are reviewed, peak-hour differences are informational and cannot produce a numerical Fail. + +Leap-year or sub-hourly runs are outside v1. A producer must not force such indices into the `0..8759` contract without an explicit normalization policy and schema revision. + +## Whole-model reconciliation + +Reconciliation is a within-document diagnostic and remains separate from the Tas-versus-OpenStudio comparison. + +| Metric | Reconciliation rule | +|---|---| +| Floor area | Compare available model `floorArea` with the sum of available, uniquely matched space `area` values | +| Volume | Compare available model `volume` with the sum of available, uniquely matched space `volume` values | +| Annual heating/cooling | No v1 reconciliation because per-space annual energy is excluded | +| Peak heating/cooling load | Do not require equality with the sum of per-space peaks; peaks may occur at different hours. The sum may be shown only as a labelled non-coincident diagnostic | +| Peak hour | Not additive; no reconciliation | +| Design load | No model-level design-load metric exists in v1 | +| Unmet hours | Do not sum across spaces as a model total; simultaneous unmet intervals would be double-counted | + +Reconciliation uses only available values. The report states the number and identity of spaces included and excluded. Missing or ambiguous spaces prevent a reconciliation from being labelled complete. + +## Gate aggregation + +The comparator keeps these concerns distinct: + +1. schema and unit validity; +2. provenance compatibility; +3. space-alignment and metric coverage; +4. numerical bands; and +5. reconciliation diagnostics. + +For numerical bands, overall severity is the worst comparable metric: Fail over Warn over Pass. `N/A` does not affect that ordering. Contract errors prevent a valid numerical gate. Coverage and reconciliation statuses are always reported alongside the numerical status so missing data cannot improve the apparent result. + +The whole-model peak-load caveat and candidate conditioning pairing remain visible even when their numbers fall in Pass. Tolerance profiles cannot remove those methodological limitations. + +## Validation path + +The 5%/15% bands and all near-zero and peak-hour floors remain provisional through B0. They may be promoted or revised only after the benchmark corpus has been run across both engines and an energy modeller has reviewed distributions, outliers, route effects, and metric semantics. Any revision must be versioned and recorded with the report. From 3ea88355ad68bcc35ae68f73abc986d8c0c40d98 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Tue, 21 Jul 2026 15:46:10 +0200 Subject: [PATCH 02/11] =?UTF-8?q?B1a=20=E2=80=94=20benchmark=20schema,=20s?= =?UTF-8?q?erializer=20and=20CLI=20host=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: clarify benchmark numeric serialization * feat: add engine-neutral benchmark schema * feat: add deterministic benchmark serialization * feat: add reusable benchmark CLI host * test: complete benchmark schema validation coverage * fix: harden benchmark input parsing * docs: align benchmark precision guidance * chore: add required SPDX headers --- SAM.Analytical.Benchmark.Tests/CliTests.cs | 146 ++++++ .../CultureAndPrecisionTests.cs | 69 +++ .../Fixtures/golden-benchmark-input.json | 87 ++++ .../Fixtures/golden-benchmark.json | 199 ++++++++ .../MSTestSettings.cs | 6 + .../SAM.Analytical.Benchmark.Tests.csproj | 35 ++ .../SerializerTests.cs | 136 ++++++ .../TestDocuments.cs | 89 ++++ .../ValidationTests.cs | 304 ++++++++++++ .../BenchmarkArgumentParseResult.cs | 20 + .../BenchmarkArgumentParser.cs | 98 ++++ .../BenchmarkArguments.cs | 80 ++++ SAM.Analytical.Benchmark/BenchmarkCliHost.cs | 94 ++++ SAM.Analytical.Benchmark/BenchmarkCliPaths.cs | 49 ++ .../BenchmarkConditionResult.cs | 26 ++ SAM.Analytical.Benchmark/BenchmarkDocument.cs | 27 ++ SAM.Analytical.Benchmark/BenchmarkEngine.cs | 26 ++ SAM.Analytical.Benchmark/BenchmarkExitCode.cs | 14 + SAM.Analytical.Benchmark/BenchmarkHash.cs | 46 ++ .../BenchmarkModelResult.cs | 42 ++ .../BenchmarkProvenance.cs | 80 ++++ SAM.Analytical.Benchmark/BenchmarkSchema.cs | 54 +++ .../BenchmarkSerializer.cs | 200 ++++++++ .../BenchmarkSpaceResult.cs | 34 ++ .../BenchmarkValidationException.cs | 29 ++ .../BenchmarkValidationResult.cs | 26 ++ .../BenchmarkValidator.cs | 436 ++++++++++++++++++ SAM.Analytical.Benchmark/BenchmarkWeather.cs | 18 + SAM.Analytical.Benchmark/Enums.cs | 63 +++ SAM.Analytical.Benchmark/JsonConverters.cs | 155 +++++++ SAM.Analytical.Benchmark/MetricValue.cs | 32 ++ .../SAM.Analytical.Benchmark.csproj | 14 + SAM.Analytical.Benchmark/ValidationIssue.cs | 29 ++ SAM_Validation.sln | 12 + docs/benchmark/IMPLEMENTATION-PLAN.md | 8 +- docs/benchmark/SCHEMA.md | 2 +- 36 files changed, 2780 insertions(+), 5 deletions(-) create mode 100644 SAM.Analytical.Benchmark.Tests/CliTests.cs create mode 100644 SAM.Analytical.Benchmark.Tests/CultureAndPrecisionTests.cs create mode 100644 SAM.Analytical.Benchmark.Tests/Fixtures/golden-benchmark-input.json create mode 100644 SAM.Analytical.Benchmark.Tests/Fixtures/golden-benchmark.json create mode 100644 SAM.Analytical.Benchmark.Tests/MSTestSettings.cs create mode 100644 SAM.Analytical.Benchmark.Tests/SAM.Analytical.Benchmark.Tests.csproj create mode 100644 SAM.Analytical.Benchmark.Tests/SerializerTests.cs create mode 100644 SAM.Analytical.Benchmark.Tests/TestDocuments.cs create mode 100644 SAM.Analytical.Benchmark.Tests/ValidationTests.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkArgumentParseResult.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkArgumentParser.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkArguments.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkCliHost.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkCliPaths.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkConditionResult.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkDocument.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkEngine.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkExitCode.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkHash.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkModelResult.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkProvenance.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkSchema.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkSerializer.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkSpaceResult.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkValidationException.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkValidationResult.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkValidator.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkWeather.cs create mode 100644 SAM.Analytical.Benchmark/Enums.cs create mode 100644 SAM.Analytical.Benchmark/JsonConverters.cs create mode 100644 SAM.Analytical.Benchmark/MetricValue.cs create mode 100644 SAM.Analytical.Benchmark/SAM.Analytical.Benchmark.csproj create mode 100644 SAM.Analytical.Benchmark/ValidationIssue.cs diff --git a/SAM.Analytical.Benchmark.Tests/CliTests.cs b/SAM.Analytical.Benchmark.Tests/CliTests.cs new file mode 100644 index 0000000..6fef727 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/CliTests.cs @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Globalization; +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Tests +{ + [TestClass] + public sealed class CliTests + { + [TestMethod] + public void ParserSupportsSeparatedAndEqualsOptions() + { + BenchmarkArgumentParseResult result = BenchmarkArgumentParser.Parse( + new[] { "--model", "model.json", "--out=result.json", "--timeout-seconds", "2.5" }, + new[] { "model", "out" }); + + Assert.IsTrue(result.IsSuccess); + Assert.AreEqual("model.json", result.Arguments!.RequireOption("--model")); + Assert.AreEqual("result.json", result.Arguments.GetOption("out")); + Assert.AreEqual(TimeSpan.FromSeconds(2.5), result.Arguments.GetTimeout()); + } + + [TestMethod] + public void ParserReportsMissingRequiredOption() + { + BenchmarkArgumentParseResult result = BenchmarkArgumentParser.Parse( + new[] { "--model", "model.json" }, + new[] { "model", "out" }); + + Assert.IsFalse(result.IsSuccess); + Assert.AreEqual("Missing required option '--out'.", result.Error); + } + + [TestMethod] + public void ParserRejectsDuplicateAndMissingValues() + { + Assert.IsFalse(BenchmarkArgumentParser.Parse(new[] { "--out", "a", "--out", "b" }).IsSuccess); + Assert.IsFalse(BenchmarkArgumentParser.Parse(new[] { "--out" }).IsSuccess); + } + + [TestMethod] + public void TimeoutOutsideTimeSpanRangeIsAUsageError() + { + BenchmarkArgumentParseResult parsed = BenchmarkArgumentParser.Parse(new[] { "--timeout-seconds", "100000000000000000000000000000" }); + + Assert.ThrowsException(() => parsed.Arguments!.GetTimeout()); + } + + [TestMethod] + public void HostPrintsHelpWithoutInvokingBody() + { + var output = new StringWriter(); + bool invoked = false; + + int exitCode = BenchmarkCliHost.Run( + new[] { "--help" }, + "usage: benchmark --model ", + new[] { "model" }, + (_, _) => { invoked = true; return 0; }, + output, + new StringWriter()); + + Assert.AreEqual((int)BenchmarkExitCode.Success, exitCode); + Assert.IsFalse(invoked); + StringAssert.Contains(output.ToString(), "usage: benchmark"); + } + + [TestMethod] + public void HostReturnsUsageExitCodeAndConsistentError() + { + var error = new StringWriter(); + + int exitCode = BenchmarkCliHost.Run( + Array.Empty(), + "usage: benchmark --model ", + new[] { "model" }, + (_, _) => 0, + new StringWriter(), + error); + + Assert.AreEqual((int)BenchmarkExitCode.InvalidUsage, exitCode); + StringAssert.StartsWith(error.ToString(), "error: Missing required option '--model'."); + StringAssert.Contains(error.ToString(), "usage: benchmark"); + } + + [TestMethod] + public void HostUsesInvariantCultureAndRestoresCallerCulture() + { + CultureInfo original = CultureInfo.CurrentCulture; + var commaCulture = CultureInfo.GetCultureInfo("de-DE"); + CultureInfo.CurrentCulture = commaCulture; + try + { + int exitCode = BenchmarkCliHost.Run( + Array.Empty(), + "usage", + Array.Empty(), + (_, _) => + { + Assert.AreEqual(CultureInfo.InvariantCulture, CultureInfo.CurrentCulture); + Assert.AreEqual("1.5", 1.5.ToString(CultureInfo.CurrentCulture)); + return 0; + }, + new StringWriter(), + new StringWriter()); + + Assert.AreEqual(0, exitCode); + Assert.AreEqual(commaCulture, CultureInfo.CurrentCulture); + } + finally + { + CultureInfo.CurrentCulture = original; + } + } + + [TestMethod] + public void ExitCodeMappingIsStable() + { + BenchmarkValidationResult invalid = BenchmarkValidator.Validate(new BenchmarkDocument()); + + Assert.AreEqual(BenchmarkExitCode.ValidationFailure, BenchmarkCliHost.MapException(new BenchmarkValidationException(invalid))); + Assert.AreEqual(BenchmarkExitCode.InputOutputOrSerializationFailure, BenchmarkCliHost.MapException(new IOException("io"))); + Assert.AreEqual(BenchmarkExitCode.InvalidUsage, BenchmarkCliHost.MapException(new ArgumentException("usage"))); + Assert.AreEqual(BenchmarkExitCode.ProducerFailure, BenchmarkCliHost.MapException(new InvalidOperationException("producer"))); + } + + [TestMethod] + public void PathHelpersValidateExistingInputAndOutputDirectory() + { + string input = Path.GetTempFileName(); + string output = Path.Combine(Path.GetTempPath(), $"benchmark-{Guid.NewGuid():N}.json"); + try + { + Assert.AreEqual(Path.GetFullPath(input), BenchmarkCliPaths.ValidateInputFile(input)); + Assert.AreEqual(Path.GetFullPath(output), BenchmarkCliPaths.ValidateOutputFile(output)); + Assert.ThrowsException(() => BenchmarkCliPaths.ValidateInputFile(output)); + } + finally + { + File.Delete(input); + } + } + } +} diff --git a/SAM.Analytical.Benchmark.Tests/CultureAndPrecisionTests.cs b/SAM.Analytical.Benchmark.Tests/CultureAndPrecisionTests.cs new file mode 100644 index 0000000..1e7c645 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/CultureAndPrecisionTests.cs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Globalization; +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Tests +{ + [TestClass] + public sealed class CultureAndPrecisionTests + { + [TestMethod] + public void SerializationIsIndependentOfCommaDecimalCulture() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + CultureInfo original = CultureInfo.CurrentCulture; + string invariant; + string german; + try + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + invariant = BenchmarkSerializer.Serialize(document); + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE"); + german = BenchmarkSerializer.Serialize(document); + } + finally + { + CultureInfo.CurrentCulture = original; + } + + Assert.AreEqual(invariant, german); + StringAssert.Contains(german, "12.3"); + Assert.IsFalse(german.Contains("12,3", StringComparison.Ordinal)); + } + + [TestMethod] + public void FiniteDoublePrecisionIsPreservedWithoutRounding() + { + const double expected = 0.12345678901234566; + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Model!.ConsumptionHeating!.Value = expected; + + BenchmarkDocument roundTrip = BenchmarkSerializer.Deserialize(BenchmarkSerializer.Serialize(document)); + + Assert.AreEqual(expected, roundTrip.Model!.ConsumptionHeating!.Value); + } + + [TestMethod] + public void SerializerDoesNotIntroduceAbsolutePaths() + { + string json = BenchmarkSerializer.Serialize(TestDocuments.CreateValid()); + + Assert.IsFalse(json.Contains(@"C:\", StringComparison.OrdinalIgnoreCase)); + Assert.IsFalse(json.Contains(Path.GetTempPath(), StringComparison.OrdinalIgnoreCase)); + Assert.IsFalse(json.Contains(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), StringComparison.OrdinalIgnoreCase)); + } + + [TestMethod] + public void Utf8SpanDeserializationRoundTrips() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + byte[] utf8 = BenchmarkSerializer.SerializeToUtf8(document); + + BenchmarkDocument roundTrip = BenchmarkSerializer.Deserialize(utf8.AsSpan()); + + CollectionAssert.AreEqual(utf8, BenchmarkSerializer.SerializeToUtf8(roundTrip)); + } + } +} diff --git a/SAM.Analytical.Benchmark.Tests/Fixtures/golden-benchmark-input.json b/SAM.Analytical.Benchmark.Tests/Fixtures/golden-benchmark-input.json new file mode 100644 index 0000000..f6fb933 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/Fixtures/golden-benchmark-input.json @@ -0,0 +1,87 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "SingleBox", + "sourceModelGuid": "0123456789abcdef0123456789abcdef", + "sourceFileHash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "canonicalModelHash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "canonicalizationVersion": "1.0.0", + "samCommit": "1111111111111111111111111111111111111111", + "runnerCommit": "2222222222222222222222222222222222222222", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "24.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "London-Gatwick.epw", + "hash": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + }, + "designDaySource": "DDY", + "runTimestampUtc": "2026-07-21T12:00:00Z", + "durationSeconds": 12.3, + "state": "Success", + "resultSources": [ + "SpaceSimulationResult", + "AnalyticalModelSimulationResult" + ], + "warnings": [ + "Zulu warning", + "Alpha warning" + ], + "notes": [ + "second note", + "first note" + ] + }, + "model": { + "consumptionHeating": { "value": 1234.5, "unit": "kWh", "available": true }, + "consumptionCooling": { "value": 0, "unit": "kWh", "available": true }, + "peakHeatingLoad": { "value": 12.3, "unit": "kW", "available": true }, + "peakHeatingHour": { "value": 0, "unit": "hourOfYear", "available": true }, + "peakCoolingLoad": { "value": 9.8, "unit": "kW", "available": true }, + "peakCoolingHour": { "value": 8759, "unit": "hourOfYear", "available": true }, + "floorArea": { "value": 240, "unit": "m2", "available": true }, + "volume": { "value": 720, "unit": "m3", "available": true } + }, + "spaces": [ + { + "guid": "fedcba9876543210fedcba9876543210", + "name": "Office B", + "area": { "value": 40, "unit": "m2", "available": true }, + "volume": { "value": 120, "unit": "m3", "available": true }, + "heating": { + "designLoad": { "value": null, "unit": "W", "available": false }, + "peakLoad": { "value": 2100, "unit": "W", "available": true }, + "peakHour": { "value": 205, "unit": "hourOfYear", "available": true }, + "unmetHours": { "value": 0, "unit": "h", "available": true } + }, + "cooling": { + "designLoad": { "value": null, "unit": "W", "available": false }, + "peakLoad": { "value": null, "unit": "W", "available": false }, + "peakHour": { "value": null, "unit": "hourOfYear", "available": false }, + "unmetHours": { "value": null, "unit": "h", "available": false } + } + }, + { + "guid": "0123456789abcdef0123456789abcdef", + "name": "Office A", + "area": { "value": 200, "unit": "m2", "available": true }, + "volume": { "value": 600, "unit": "m3", "available": true }, + "heating": { + "designLoad": { "value": null, "unit": "W", "available": false }, + "peakLoad": { "value": 2200, "unit": "W", "available": true }, + "peakHour": { "value": 205, "unit": "hourOfYear", "available": true }, + "unmetHours": { "value": 0, "unit": "h", "available": true } + }, + "cooling": { + "designLoad": { "value": null, "unit": "W", "available": false }, + "peakLoad": { "value": null, "unit": "W", "available": false }, + "peakHour": { "value": null, "unit": "hourOfYear", "available": false }, + "unmetHours": { "value": null, "unit": "h", "available": false } + } + } + ] +} diff --git a/SAM.Analytical.Benchmark.Tests/Fixtures/golden-benchmark.json b/SAM.Analytical.Benchmark.Tests/Fixtures/golden-benchmark.json new file mode 100644 index 0000000..1a35fd1 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/Fixtures/golden-benchmark.json @@ -0,0 +1,199 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "SingleBox", + "sourceModelGuid": "0123456789abcdef0123456789abcdef", + "sourceFileHash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "canonicalModelHash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "canonicalizationVersion": "1.0.0", + "samCommit": "1111111111111111111111111111111111111111", + "runnerCommit": "2222222222222222222222222222222222222222", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "24.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "London-Gatwick.epw", + "hash": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + }, + "designDaySource": "DDY", + "runTimestampUtc": "2026-07-21T12:00:00Z", + "durationSeconds": 12.3, + "state": "Success", + "resultSources": [ + "AnalyticalModelSimulationResult", + "SpaceSimulationResult" + ], + "warnings": [ + "Alpha warning", + "Zulu warning" + ], + "notes": [ + "first note", + "second note" + ] + }, + "model": { + "consumptionHeating": { + "value": 1234.5, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": 0, + "unit": "kWh", + "available": true + }, + "peakHeatingLoad": { + "value": 12.3, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 9.8, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 8759, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 240, + "unit": "m2", + "available": true + }, + "volume": { + "value": 720, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "0123456789abcdef0123456789abcdef", + "name": "Office A", + "area": { + "value": 200, + "unit": "m2", + "available": true + }, + "volume": { + "value": 600, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 2200, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 205, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "fedcba9876543210fedcba9876543210", + "name": "Office B", + "area": { + "value": 40, + "unit": "m2", + "available": true + }, + "volume": { + "value": 120, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 2100, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 205, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + } + ] +} diff --git a/SAM.Analytical.Benchmark.Tests/MSTestSettings.cs b/SAM.Analytical.Benchmark.Tests/MSTestSettings.cs new file mode 100644 index 0000000..df16d1c --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/MSTestSettings.cs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/SAM.Analytical.Benchmark.Tests/SAM.Analytical.Benchmark.Tests.csproj b/SAM.Analytical.Benchmark.Tests/SAM.Analytical.Benchmark.Tests.csproj new file mode 100644 index 0000000..e281899 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/SAM.Analytical.Benchmark.Tests.csproj @@ -0,0 +1,35 @@ + + + + net8.0 + latest + enable + enable + true + Exe + true + true + + + + + + + + + + + + + + + + PreserveNewest + + + + + + + + diff --git a/SAM.Analytical.Benchmark.Tests/SerializerTests.cs b/SAM.Analytical.Benchmark.Tests/SerializerTests.cs new file mode 100644 index 0000000..38af3c7 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/SerializerTests.cs @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Text; +using System.Text.Json; +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Tests +{ + [TestClass] + public sealed class SerializerTests + { + [TestMethod] + public void ValidDocumentRoundTrips() + { + string json = BenchmarkSerializer.Serialize(TestDocuments.CreateValid()); + + BenchmarkDocument roundTrip = BenchmarkSerializer.Deserialize(json); + + Assert.AreEqual(json, BenchmarkSerializer.Serialize(roundTrip)); + } + + [TestMethod] + public void RepeatedSerializationIsByteIdentical() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + + CollectionAssert.AreEqual( + BenchmarkSerializer.SerializeToUtf8(document), + BenchmarkSerializer.SerializeToUtf8(document)); + } + + [TestMethod] + public void SpacesWarningsAndNotesAreCanonicalizedWithoutMutatingInput() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + + string json = BenchmarkSerializer.Serialize(document); + + Assert.IsTrue(json.IndexOf("Office A", StringComparison.Ordinal) < json.IndexOf("Office B", StringComparison.Ordinal)); + Assert.IsTrue(json.IndexOf("Alpha warning", StringComparison.Ordinal) < json.IndexOf("Zulu warning", StringComparison.Ordinal)); + Assert.IsTrue(json.IndexOf("first note", StringComparison.Ordinal) < json.IndexOf("second note", StringComparison.Ordinal)); + Assert.AreEqual("Office B", document.Spaces![0].Name); + } + + [TestMethod] + public void EnumsUseCanonicalStringTokens() + { + string json = BenchmarkSerializer.Serialize(TestDocuments.CreateValid()); + + StringAssert.Contains(json, "\"route\": \"Native-OpenStudio\""); + StringAssert.Contains(json, "\"kind\": \"OpenStudio\""); + StringAssert.Contains(json, "\"unit\": \"kWh\""); + StringAssert.Contains(json, "\"state\": \"Success\""); + Assert.IsFalse(json.Contains("\"route\": 1", StringComparison.Ordinal)); + } + + [TestMethod] + public void ZeroRemainsAvailableAndUnavailableRemainsNull() + { + string json = BenchmarkSerializer.Serialize(TestDocuments.CreateValid()); + using JsonDocument parsed = JsonDocument.Parse(json); + + JsonElement cooling = parsed.RootElement.GetProperty("model").GetProperty("consumptionCooling"); + Assert.AreEqual(0d, cooling.GetProperty("value").GetDouble()); + Assert.IsTrue(cooling.GetProperty("available").GetBoolean()); + + JsonElement designLoad = parsed.RootElement.GetProperty("spaces")[0].GetProperty("heating").GetProperty("designLoad"); + Assert.AreEqual(JsonValueKind.Null, designLoad.GetProperty("value").ValueKind); + Assert.IsFalse(designLoad.GetProperty("available").GetBoolean()); + } + + [TestMethod] + public void InvalidDocumentIsRefusedBeforeSerialization() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Model!.PeakHeatingLoad!.Value = double.NaN; + + Assert.ThrowsException(() => BenchmarkSerializer.Serialize(document)); + } + + [TestMethod] + public void Utf8SerializationHasNoBomAndUsesLf() + { + byte[] bytes = BenchmarkSerializer.SerializeToUtf8(TestDocuments.CreateValid()); + string json = Encoding.UTF8.GetString(bytes); + + Assert.IsFalse(bytes.Length >= 3 && bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf); + Assert.IsFalse(json.Contains('\r')); + } + + [TestMethod] + public void ReadWriteRoundTripsThroughFile() + { + string path = Path.Combine(Path.GetTempPath(), $"benchmark-{Guid.NewGuid():N}.json"); + try + { + BenchmarkDocument document = TestDocuments.CreateValid(); + BenchmarkSerializer.Write(path, document); + + Assert.AreEqual(BenchmarkSerializer.Serialize(document), BenchmarkSerializer.Serialize(BenchmarkSerializer.Read(path))); + } + finally + { + if (File.Exists(path)) + { + File.Delete(path); + } + } + } + + [TestMethod] + public void GoldenInputSerializesToExpectedFixture() + { + string input = File.ReadAllText(FixturePath("golden-benchmark-input.json")); + string expected = File.ReadAllText(FixturePath("golden-benchmark.json")).Replace("\r\n", "\n").TrimEnd('\n'); + + string actual = BenchmarkSerializer.Serialize(BenchmarkSerializer.Deserialize(input)); + + Assert.AreEqual(expected, actual); + } + + [TestMethod] + public void Sha256HelpersUseCanonicalPrefixAndLowercaseHex() + { + Assert.AreEqual( + "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + BenchmarkHash.ComputeSha256("abc")); + } + + private static string FixturePath(string name) + { + return Path.Combine(AppContext.BaseDirectory, "Fixtures", name); + } + } +} diff --git a/SAM.Analytical.Benchmark.Tests/TestDocuments.cs b/SAM.Analytical.Benchmark.Tests/TestDocuments.cs new file mode 100644 index 0000000..d72ba8c --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/TestDocuments.cs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Tests +{ + internal static class TestDocuments + { + internal const string HashA = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + internal const string HashB = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; + internal const string HashC = "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"; + + internal static BenchmarkDocument CreateValid() + { + return new BenchmarkDocument + { + SchemaVersion = BenchmarkSchema.CurrentVersion, + Provenance = new BenchmarkProvenance + { + SourceModelName = "SingleBox", + SourceModelGuid = "0123456789abcdef0123456789abcdef", + SourceFileHash = HashA, + CanonicalModelHash = HashB, + CanonicalizationVersion = "1.0.0", + SamCommit = "1111111111111111111111111111111111111111", + RunnerCommit = "2222222222222222222222222222222222222222", + Engine = new BenchmarkEngine + { + Kind = EngineKind.OpenStudio, + Name = "EnergyPlus", + Version = "24.1.0", + SdkVersion = "3.10.0" + }, + Route = BenchmarkRoute.NativeOpenStudio, + Weather = new BenchmarkWeather { Identity = "London-Gatwick.epw", Hash = HashC }, + DesignDaySource = DesignDaySource.Ddy, + RunTimestampUtc = new DateTimeOffset(2026, 7, 21, 12, 0, 0, TimeSpan.Zero), + DurationSeconds = 12.3, + State = RunState.Success, + ResultSources = new List { "SpaceSimulationResult", "AnalyticalModelSimulationResult" }, + Warnings = new List { "Zulu warning", "Alpha warning" }, + Notes = new List { "second note", "first note" } + }, + Model = new BenchmarkModelResult + { + ConsumptionHeating = MetricValue.AvailableValue(1234.5, MetricUnit.KilowattHour), + ConsumptionCooling = MetricValue.AvailableValue(0, MetricUnit.KilowattHour), + PeakHeatingLoad = MetricValue.AvailableValue(12.3, MetricUnit.Kilowatt), + PeakHeatingHour = MetricValue.AvailableValue(0, MetricUnit.HourOfYear), + PeakCoolingLoad = MetricValue.AvailableValue(9.8, MetricUnit.Kilowatt), + PeakCoolingHour = MetricValue.AvailableValue(8759, MetricUnit.HourOfYear), + FloorArea = MetricValue.AvailableValue(240, MetricUnit.SquareMetre), + Volume = MetricValue.AvailableValue(720, MetricUnit.CubicMetre) + }, + Spaces = new List + { + CreateSpace("fedcba9876543210fedcba9876543210", "Office B", 40, 120, 2100), + CreateSpace("0123456789abcdef0123456789abcdef", "Office A", 200, 600, 2200) + } + }; + } + + private static BenchmarkSpaceResult CreateSpace(string guid, string name, double area, double volume, double peakLoad) + { + return new BenchmarkSpaceResult + { + Guid = guid, + Name = name, + Area = MetricValue.AvailableValue(area, MetricUnit.SquareMetre), + Volume = MetricValue.AvailableValue(volume, MetricUnit.CubicMetre), + Heating = new BenchmarkConditionResult + { + DesignLoad = MetricValue.Unavailable(MetricUnit.Watt), + PeakLoad = MetricValue.AvailableValue(peakLoad, MetricUnit.Watt), + PeakHour = MetricValue.AvailableValue(205, MetricUnit.HourOfYear), + UnmetHours = MetricValue.AvailableValue(0, MetricUnit.Hour) + }, + Cooling = new BenchmarkConditionResult + { + DesignLoad = MetricValue.Unavailable(MetricUnit.Watt), + PeakLoad = MetricValue.Unavailable(MetricUnit.Watt), + PeakHour = MetricValue.Unavailable(MetricUnit.HourOfYear), + UnmetHours = MetricValue.Unavailable(MetricUnit.Hour) + } + }; + } + } +} diff --git a/SAM.Analytical.Benchmark.Tests/ValidationTests.cs b/SAM.Analytical.Benchmark.Tests/ValidationTests.cs new file mode 100644 index 0000000..1ec3334 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/ValidationTests.cs @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Tests +{ + [TestClass] + public sealed class ValidationTests + { + [TestMethod] + public void ValidDocumentAndSha256HashesAreAccepted() + { + BenchmarkValidationResult result = BenchmarkValidator.Validate(TestDocuments.CreateValid()); + + Assert.IsTrue(result.IsValid); + Assert.AreEqual(0, result.Warnings.Count); + } + + [TestMethod] + public void MissingAndMalformedSchemaVersionsAreRejected() + { + BenchmarkDocument missing = TestDocuments.CreateValid(); + missing.SchemaVersion = null; + BenchmarkDocument malformed = TestDocuments.CreateValid(); + malformed.SchemaVersion = "1.0"; + + AssertInvalid(missing, "schema.version.malformed"); + AssertInvalid(malformed, "schema.version.malformed"); + } + + [TestMethod] + public void MajorSchemaMismatchIsRejected() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.SchemaVersion = "2.0.0"; + + AssertInvalid(document, "schema.version.major"); + } + + [TestMethod] + public void MinorSchemaMismatchProducesCompatibilityWarning() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.SchemaVersion = "1.1.0"; + + BenchmarkValidationResult result = BenchmarkValidator.Validate(document); + + Assert.IsTrue(result.IsValid); + Assert.IsTrue(result.Warnings.Any(x => x.Code == "schema.version.minor")); + } + + [TestMethod] + public void PatchSchemaMismatchDoesNotWarn() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.SchemaVersion = "1.0.1"; + + BenchmarkValidationResult result = BenchmarkValidator.Validate(document); + + Assert.IsTrue(result.IsValid); + Assert.AreEqual(0, result.Warnings.Count); + } + + [TestMethod] + public void MissingProvenanceIsRejected() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance = null; + + AssertInvalid(document, "provenance.missing"); + } + + [TestMethod] + public void UnsupportedRouteAndEngineAreRejected() + { + BenchmarkDocument route = TestDocuments.CreateValid(); + route.Provenance!.Route = (BenchmarkRoute)999; + BenchmarkDocument engine = TestDocuments.CreateValid(); + engine.Provenance!.Engine!.Kind = (EngineKind)999; + + AssertInvalid(route, "provenance.route"); + AssertInvalid(engine, "provenance.engine.kind"); + } + + [TestMethod] + public void RouteMustMatchEngineKind() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.Route = BenchmarkRoute.NativeTas; + + AssertInvalid(document, "provenance.routeEngine.mismatch"); + } + + [TestMethod] + public void MalformedHashIsRejected() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.SourceFileHash = "SHA256:ABC"; + + AssertInvalid(document, "provenance.hash.malformed"); + } + + [TestMethod] + public void SuccessfulRunRequiresAllHashes() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.CanonicalModelHash = null; + + AssertInvalid(document, "provenance.hash.required"); + } + + [TestMethod] + public void NonUtcTimestampIsRejected() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.RunTimestampUtc = new DateTimeOffset(2026, 7, 21, 12, 0, 0, TimeSpan.FromHours(2)); + + AssertInvalid(document, "provenance.timestamp.utc"); + } + + [TestMethod] + public void NonStringTimestampIsRejectedAsInvalidJson() + { + string json = BenchmarkSerializer.Serialize(TestDocuments.CreateValid()) + .Replace("\"runTimestampUtc\": \"2026-07-21T12:00:00Z\"", "\"runTimestampUtc\": 123"); + + Assert.ThrowsException(() => BenchmarkSerializer.Deserialize(json)); + } + + [TestMethod] + public void NegativeAndNonFiniteDurationsAreRejected() + { + BenchmarkDocument negative = TestDocuments.CreateValid(); + negative.Provenance!.DurationSeconds = -0.1; + BenchmarkDocument nonFinite = TestDocuments.CreateValid(); + nonFinite.Provenance!.DurationSeconds = double.PositiveInfinity; + + AssertInvalid(negative, "provenance.duration.invalid"); + AssertInvalid(nonFinite, "provenance.duration.invalid"); + } + + [TestMethod] + public void DuplicateSpaceGuidIsRejected() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Spaces![1].Guid = document.Spaces[0].Guid; + + AssertInvalid(document, "space.guid.duplicate"); + } + + [TestMethod] + public void EmptySpaceGuidAndIdentityAreRejected() + { + BenchmarkDocument emptyGuid = TestDocuments.CreateValid(); + emptyGuid.Spaces![0].Guid = string.Empty; + BenchmarkDocument emptyIdentity = TestDocuments.CreateValid(); + emptyIdentity.Spaces![0].Guid = null; + emptyIdentity.Spaces[0].Name = " "; + + AssertInvalid(emptyGuid, "space.guid.empty"); + AssertInvalid(emptyIdentity, "space.identity.missing"); + } + + [TestMethod] + public void SharedGbXmlRouteAllowsGuidlessSpaceWithUniqueName() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.Route = BenchmarkRoute.SharedGbXmlOpenStudio; + document.Spaces![0].Guid = null; + + Assert.IsTrue(BenchmarkValidator.Validate(document).IsValid); + } + + [TestMethod] + public void UnavailableMetricWithZeroIsRejected() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Model!.ConsumptionCooling!.Available = false; + + AssertInvalid(document, "metric.availability.valueForbidden"); + } + + [TestMethod] + public void AvailableMetricWithNullIsRejected() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Model!.ConsumptionCooling!.Value = null; + + AssertInvalid(document, "metric.availability.valueRequired"); + } + + [TestMethod] + public void NaNAndInfinitiesAreRejected() + { + foreach (double value in new[] { double.NaN, double.PositiveInfinity, double.NegativeInfinity }) + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Model!.PeakHeatingLoad!.Value = value; + AssertInvalid(document, "metric.value.nonFinite"); + } + } + + [TestMethod] + public void HourOfYearBoundaryValuesAreAccepted() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Model!.PeakHeatingHour!.Value = 0; + document.Model.PeakCoolingHour!.Value = 8759; + + Assert.IsTrue(BenchmarkValidator.Validate(document).IsValid); + } + + [TestMethod] + public void InvalidHourOfYearValuesAreRejected() + { + foreach (double value in new[] { -1d, 8760d, 1.5d }) + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Model!.PeakHeatingHour!.Value = value; + AssertInvalid(document, "metric.value.hourOfYear"); + } + } + + [TestMethod] + public void IncompatibleAndUnsupportedUnitsAreRejected() + { + BenchmarkDocument incompatible = TestDocuments.CreateValid(); + incompatible.Model!.ConsumptionHeating!.Unit = MetricUnit.WattHour; + BenchmarkDocument unsupported = TestDocuments.CreateValid(); + unsupported.Model!.ConsumptionHeating!.Unit = (MetricUnit)999; + + AssertInvalid(incompatible, "metric.unit.incompatible"); + AssertInvalid(unsupported, "metric.unit.unsupported"); + } + + [TestMethod] + public void NegativeAreaVolumeLoadConsumptionAndUnmetHoursAreRejected() + { + var cases = new Action[] + { + x => x.Model!.FloorArea!.Value = -1, + x => x.Model!.Volume!.Value = -1, + x => x.Model!.PeakHeatingLoad!.Value = -1, + x => x.Model!.ConsumptionHeating!.Value = -1, + x => x.Spaces![0].Heating!.UnmetHours!.Value = -1 + }; + + foreach (Action mutate in cases) + { + BenchmarkDocument document = TestDocuments.CreateValid(); + mutate(document); + AssertInvalid(document, "metric.value.negative"); + } + } + + [TestMethod] + public void FailedRunRequiresExplanationButMayOmitUnavailableHashes() + { + BenchmarkDocument invalid = TestDocuments.CreateValid(); + invalid.Provenance!.State = RunState.Failure; + invalid.Provenance.Warnings!.Clear(); + invalid.Provenance.Notes!.Clear(); + + BenchmarkDocument valid = TestDocuments.CreateValid(); + valid.Provenance!.State = RunState.Failure; + valid.Provenance.SourceFileHash = null; + valid.Provenance.CanonicalModelHash = null; + valid.Provenance.Weather!.Hash = null; + + AssertInvalid(invalid, "provenance.failure.explanation"); + Assert.IsTrue(BenchmarkValidator.Validate(valid).IsValid); + } + + [TestMethod] + public void AbsoluteProvenanceIdentityIsRejected() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.SourceModelName = @"C:\models\SingleBox.json"; + + AssertInvalid(document, "provenance.identity.path"); + } + + [TestMethod] + public void MalformedCommitAndCanonicalizationVersionAreRejected() + { + BenchmarkDocument commit = TestDocuments.CreateValid(); + commit.Provenance!.SamCommit = "not-a-commit"; + BenchmarkDocument version = TestDocuments.CreateValid(); + version.Provenance!.CanonicalizationVersion = "v1"; + + AssertInvalid(commit, "provenance.commit.malformed"); + AssertInvalid(version, "provenance.canonicalizationVersion"); + } + + private static void AssertInvalid(BenchmarkDocument document, string expectedCode) + { + BenchmarkValidationResult result = BenchmarkValidator.Validate(document); + Assert.IsFalse(result.IsValid, "Expected validation to fail."); + Assert.IsTrue(result.Errors.Any(x => x.Code == expectedCode), + $"Expected validation code '{expectedCode}'. Actual: {string.Join(", ", result.Errors.Select(x => x.Code))}"); + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkArgumentParseResult.cs b/SAM.Analytical.Benchmark/BenchmarkArgumentParseResult.cs new file mode 100644 index 0000000..1081b7f --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkArgumentParseResult.cs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkArgumentParseResult + { + internal BenchmarkArgumentParseResult(BenchmarkArguments? arguments, string? error) + { + Arguments = arguments; + Error = error; + } + + public BenchmarkArguments? Arguments { get; } + + public string? Error { get; } + + public bool IsSuccess => Error == null; + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkArgumentParser.cs b/SAM.Analytical.Benchmark/BenchmarkArgumentParser.cs new file mode 100644 index 0000000..82681ab --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkArgumentParser.cs @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace SAM.Analytical.Benchmark +{ + public static class BenchmarkArgumentParser + { + private static readonly Regex OptionNamePattern = new Regex("^[A-Za-z][A-Za-z0-9-]*$", RegexOptions.CultureInvariant); + + public static BenchmarkArgumentParseResult Parse(string[]? args, IEnumerable? requiredOptions = null) + { + if (args == null) + { + return Failure("Arguments are required."); + } + + var options = new Dictionary(StringComparer.OrdinalIgnoreCase); + bool helpRequested = false; + for (int index = 0; index < args.Length; index++) + { + string argument = args[index]; + if (argument == "--help" || argument == "-h") + { + helpRequested = true; + continue; + } + + if (!argument.StartsWith("--", StringComparison.Ordinal) || argument.Length == 2) + { + return Failure($"Unexpected argument '{argument}'. Options must use --name value or --name=value."); + } + + string option; + string value; + int separator = argument.IndexOf('='); + if (separator >= 0) + { + option = argument.Substring(2, separator - 2); + value = argument.Substring(separator + 1); + } + else + { + option = argument.Substring(2); + if (index + 1 >= args.Length || args[index + 1].StartsWith("--", StringComparison.Ordinal)) + { + return Failure($"Option '--{option}' requires a value."); + } + + value = args[++index]; + } + + if (!OptionNamePattern.IsMatch(option)) + { + return Failure($"Invalid option name '--{option}'."); + } + + if (string.IsNullOrEmpty(value)) + { + return Failure($"Option '--{option}' requires a non-empty value."); + } + + if (options.ContainsKey(option)) + { + return Failure($"Option '--{option}' was supplied more than once."); + } + + options.Add(option, value); + } + + var parsedArguments = new BenchmarkArguments(options, helpRequested); + if (helpRequested) + { + return new BenchmarkArgumentParseResult(parsedArguments, null); + } + + foreach (string required in requiredOptions ?? Enumerable.Empty()) + { + string normalizedName = BenchmarkArguments.NormalizeName(required); + if (!options.ContainsKey(normalizedName)) + { + return Failure($"Missing required option '--{normalizedName}'."); + } + } + + return new BenchmarkArgumentParseResult(parsedArguments, null); + } + + private static BenchmarkArgumentParseResult Failure(string message) + { + return new BenchmarkArgumentParseResult(null, message); + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkArguments.cs b/SAM.Analytical.Benchmark/BenchmarkArguments.cs new file mode 100644 index 0000000..74e1e22 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkArguments.cs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkArguments + { + private readonly IReadOnlyDictionary options; + + internal BenchmarkArguments(IDictionary options, bool helpRequested) + { + this.options = new ReadOnlyDictionary(options); + HelpRequested = helpRequested; + } + + public bool HelpRequested { get; } + + public IReadOnlyDictionary Options => options; + + public bool HasOption(string name) + { + return options.ContainsKey(NormalizeName(name)); + } + + public string? GetOption(string name) + { + return options.TryGetValue(NormalizeName(name), out string? value) ? value : null; + } + + public string RequireOption(string name) + { + string normalizedName = NormalizeName(name); + if (!options.TryGetValue(normalizedName, out string? value)) + { + throw new ArgumentException($"Missing required option '--{normalizedName}'.", nameof(name)); + } + + return value; + } + + public TimeSpan? GetTimeout(string name = "timeout-seconds") + { + string? text = GetOption(name); + if (text == null) + { + return null; + } + + if (!double.TryParse(text, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out double seconds) + || double.IsNaN(seconds) || double.IsInfinity(seconds) || seconds <= 0) + { + throw new ArgumentException($"Option '--{NormalizeName(name)}' must be a positive number of seconds.", nameof(name)); + } + + try + { + return TimeSpan.FromSeconds(seconds); + } + catch (OverflowException exception) + { + throw new ArgumentException($"Option '--{NormalizeName(name)}' is outside the supported timeout range.", nameof(name), exception); + } + } + + internal static string NormalizeName(string name) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return name.StartsWith("--", StringComparison.Ordinal) ? name.Substring(2) : name; + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkCliHost.cs b/SAM.Analytical.Benchmark/BenchmarkCliHost.cs new file mode 100644 index 0000000..654baf1 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkCliHost.cs @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading; + +namespace SAM.Analytical.Benchmark +{ + public static class BenchmarkCliHost + { + public static int Run( + string[] args, + string usage, + IEnumerable requiredOptions, + Func body, + TextWriter? standardOutput = null, + TextWriter? standardError = null, + CancellationToken cancellationToken = default) + { + if (body == null) + { + throw new ArgumentNullException(nameof(body)); + } + + standardOutput = standardOutput ?? Console.Out; + standardError = standardError ?? Console.Error; + CultureInfo originalCulture = CultureInfo.CurrentCulture; + CultureInfo originalUiCulture = CultureInfo.CurrentUICulture; + try + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture; + + BenchmarkArgumentParseResult parsed = BenchmarkArgumentParser.Parse(args, requiredOptions); + if (!parsed.IsSuccess) + { + standardError.WriteLine("error: " + parsed.Error); + standardError.WriteLine(usage); + return (int)BenchmarkExitCode.InvalidUsage; + } + + if (parsed.Arguments!.HelpRequested) + { + standardOutput.WriteLine(usage); + return (int)BenchmarkExitCode.Success; + } + + cancellationToken.ThrowIfCancellationRequested(); + return body(parsed.Arguments, cancellationToken); + } + catch (Exception exception) + { + standardError.WriteLine("error: " + exception.Message); + return (int)MapException(exception); + } + finally + { + CultureInfo.CurrentCulture = originalCulture; + CultureInfo.CurrentUICulture = originalUiCulture; + } + } + + public static BenchmarkExitCode MapException(Exception exception) + { + if (exception == null) + { + throw new ArgumentNullException(nameof(exception)); + } + + if (exception is BenchmarkValidationException) + { + return BenchmarkExitCode.ValidationFailure; + } + + if (exception is IOException || exception is UnauthorizedAccessException + || exception is JsonException || exception is DecoderFallbackException) + { + return BenchmarkExitCode.InputOutputOrSerializationFailure; + } + + if (exception is ArgumentException || exception is FormatException) + { + return BenchmarkExitCode.InvalidUsage; + } + + return BenchmarkExitCode.ProducerFailure; + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkCliPaths.cs b/SAM.Analytical.Benchmark/BenchmarkCliPaths.cs new file mode 100644 index 0000000..23498ac --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkCliPaths.cs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.IO; + +namespace SAM.Analytical.Benchmark +{ + public static class BenchmarkCliPaths + { + public static string ValidateInputFile(string path) + { + string fullPath = GetFullPath(path, nameof(path)); + if (!File.Exists(fullPath)) + { + throw new FileNotFoundException("The input file does not exist.", fullPath); + } + + return fullPath; + } + + public static string ValidateOutputFile(string path) + { + string fullPath = GetFullPath(path, nameof(path)); + if (Directory.Exists(fullPath)) + { + throw new IOException("The output path identifies a directory, not a file."); + } + + string? directory = Path.GetDirectoryName(fullPath); + if (string.IsNullOrEmpty(directory) || !Directory.Exists(directory)) + { + throw new DirectoryNotFoundException("The output directory does not exist."); + } + + return fullPath; + } + + private static string GetFullPath(string path, string parameterName) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException("A file path is required.", parameterName); + } + + return Path.GetFullPath(path); + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkConditionResult.cs b/SAM.Analytical.Benchmark/BenchmarkConditionResult.cs new file mode 100644 index 0000000..0e94199 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkConditionResult.cs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkConditionResult + { + [JsonPropertyName("designLoad")] + [JsonPropertyOrder(0)] + public MetricValue? DesignLoad { get; set; } + + [JsonPropertyName("peakLoad")] + [JsonPropertyOrder(1)] + public MetricValue? PeakLoad { get; set; } + + [JsonPropertyName("peakHour")] + [JsonPropertyOrder(2)] + public MetricValue? PeakHour { get; set; } + + [JsonPropertyName("unmetHours")] + [JsonPropertyOrder(3)] + public MetricValue? UnmetHours { get; set; } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkDocument.cs b/SAM.Analytical.Benchmark/BenchmarkDocument.cs new file mode 100644 index 0000000..ba154e1 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkDocument.cs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkDocument + { + [JsonPropertyName("schemaVersion")] + [JsonPropertyOrder(0)] + public string? SchemaVersion { get; set; } + + [JsonPropertyName("provenance")] + [JsonPropertyOrder(1)] + public BenchmarkProvenance? Provenance { get; set; } + + [JsonPropertyName("model")] + [JsonPropertyOrder(2)] + public BenchmarkModelResult? Model { get; set; } + + [JsonPropertyName("spaces")] + [JsonPropertyOrder(3)] + public List? Spaces { get; set; } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkEngine.cs b/SAM.Analytical.Benchmark/BenchmarkEngine.cs new file mode 100644 index 0000000..bb182a9 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkEngine.cs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkEngine + { + [JsonPropertyName("kind")] + [JsonPropertyOrder(0)] + public EngineKind Kind { get; set; } + + [JsonPropertyName("name")] + [JsonPropertyOrder(1)] + public string? Name { get; set; } + + [JsonPropertyName("version")] + [JsonPropertyOrder(2)] + public string? Version { get; set; } + + [JsonPropertyName("sdkVersion")] + [JsonPropertyOrder(3)] + public string? SdkVersion { get; set; } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkExitCode.cs b/SAM.Analytical.Benchmark/BenchmarkExitCode.cs new file mode 100644 index 0000000..d13cf77 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkExitCode.cs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +namespace SAM.Analytical.Benchmark +{ + public enum BenchmarkExitCode + { + Success = 0, + InvalidUsage = 2, + InputOutputOrSerializationFailure = 3, + ValidationFailure = 4, + ProducerFailure = 5 + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkHash.cs b/SAM.Analytical.Benchmark/BenchmarkHash.cs new file mode 100644 index 0000000..ab3e752 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkHash.cs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Security.Cryptography; +using System.Text; + +namespace SAM.Analytical.Benchmark +{ + public static class BenchmarkHash + { + public static string ComputeSha256(byte[] bytes) + { + if (bytes == null) + { + throw new ArgumentNullException(nameof(bytes)); + } + + using (SHA256 sha256 = SHA256.Create()) + { + return "sha256:" + ToLowerHex(sha256.ComputeHash(bytes)); + } + } + + public static string ComputeSha256(string text) + { + if (text == null) + { + throw new ArgumentNullException(nameof(text)); + } + + return ComputeSha256(Encoding.UTF8.GetBytes(text)); + } + + private static string ToLowerHex(byte[] bytes) + { + var builder = new StringBuilder(bytes.Length * 2); + foreach (byte value in bytes) + { + builder.Append(value.ToString("x2")); + } + + return builder.ToString(); + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkModelResult.cs b/SAM.Analytical.Benchmark/BenchmarkModelResult.cs new file mode 100644 index 0000000..9b708ae --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkModelResult.cs @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkModelResult + { + [JsonPropertyName("consumptionHeating")] + [JsonPropertyOrder(0)] + public MetricValue? ConsumptionHeating { get; set; } + + [JsonPropertyName("consumptionCooling")] + [JsonPropertyOrder(1)] + public MetricValue? ConsumptionCooling { get; set; } + + [JsonPropertyName("peakHeatingLoad")] + [JsonPropertyOrder(2)] + public MetricValue? PeakHeatingLoad { get; set; } + + [JsonPropertyName("peakHeatingHour")] + [JsonPropertyOrder(3)] + public MetricValue? PeakHeatingHour { get; set; } + + [JsonPropertyName("peakCoolingLoad")] + [JsonPropertyOrder(4)] + public MetricValue? PeakCoolingLoad { get; set; } + + [JsonPropertyName("peakCoolingHour")] + [JsonPropertyOrder(5)] + public MetricValue? PeakCoolingHour { get; set; } + + [JsonPropertyName("floorArea")] + [JsonPropertyOrder(6)] + public MetricValue? FloorArea { get; set; } + + [JsonPropertyName("volume")] + [JsonPropertyOrder(7)] + public MetricValue? Volume { get; set; } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkProvenance.cs b/SAM.Analytical.Benchmark/BenchmarkProvenance.cs new file mode 100644 index 0000000..9d406b7 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkProvenance.cs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkProvenance + { + [JsonPropertyName("sourceModelName")] + [JsonPropertyOrder(0)] + public string? SourceModelName { get; set; } + + [JsonPropertyName("sourceModelGuid")] + [JsonPropertyOrder(1)] + public string? SourceModelGuid { get; set; } + + [JsonPropertyName("sourceFileHash")] + [JsonPropertyOrder(2)] + public string? SourceFileHash { get; set; } + + [JsonPropertyName("canonicalModelHash")] + [JsonPropertyOrder(3)] + public string? CanonicalModelHash { get; set; } + + [JsonPropertyName("canonicalizationVersion")] + [JsonPropertyOrder(4)] + public string? CanonicalizationVersion { get; set; } + + [JsonPropertyName("samCommit")] + [JsonPropertyOrder(5)] + public string? SamCommit { get; set; } + + [JsonPropertyName("runnerCommit")] + [JsonPropertyOrder(6)] + public string? RunnerCommit { get; set; } + + [JsonPropertyName("engine")] + [JsonPropertyOrder(7)] + public BenchmarkEngine? Engine { get; set; } + + [JsonPropertyName("route")] + [JsonPropertyOrder(8)] + public BenchmarkRoute Route { get; set; } + + [JsonPropertyName("weather")] + [JsonPropertyOrder(9)] + public BenchmarkWeather? Weather { get; set; } + + [JsonPropertyName("designDaySource")] + [JsonPropertyOrder(10)] + public DesignDaySource DesignDaySource { get; set; } + + [JsonPropertyName("runTimestampUtc")] + [JsonPropertyOrder(11)] + public DateTimeOffset RunTimestampUtc { get; set; } + + [JsonPropertyName("durationSeconds")] + [JsonPropertyOrder(12)] + public double? DurationSeconds { get; set; } + + [JsonPropertyName("state")] + [JsonPropertyOrder(13)] + public RunState State { get; set; } + + [JsonPropertyName("resultSources")] + [JsonPropertyOrder(14)] + public List? ResultSources { get; set; } + + [JsonPropertyName("warnings")] + [JsonPropertyOrder(15)] + public List? Warnings { get; set; } + + [JsonPropertyName("notes")] + [JsonPropertyOrder(16)] + public List? Notes { get; set; } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkSchema.cs b/SAM.Analytical.Benchmark/BenchmarkSchema.cs new file mode 100644 index 0000000..fddf9a7 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkSchema.cs @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace SAM.Analytical.Benchmark +{ + public static class BenchmarkSchema + { + public const string CurrentVersion = "1.0.0"; + + private static readonly Regex SemanticVersionPattern = new Regex( + @"^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$", + RegexOptions.CultureInvariant); + + public static SchemaCompatibility GetCompatibility(string? version) + { + if (!TryParseVersion(version, out int major, out int minor, out int patch)) + { + return SchemaCompatibility.Malformed; + } + + TryParseVersion(CurrentVersion, out int currentMajor, out int currentMinor, out int currentPatch); + if (major != currentMajor) + { + return SchemaCompatibility.IncompatibleMajor; + } + + return minor == currentMinor + ? SchemaCompatibility.Compatible + : SchemaCompatibility.CompatibleWithMinorWarning; + } + + public static bool TryParseVersion(string? version, out int major, out int minor, out int patch) + { + major = 0; + minor = 0; + patch = 0; + + if (string.IsNullOrWhiteSpace(version)) + { + return false; + } + + Match match = SemanticVersionPattern.Match(version); + return match.Success + && int.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out major) + && int.TryParse(match.Groups[2].Value, NumberStyles.None, CultureInfo.InvariantCulture, out minor) + && int.TryParse(match.Groups[3].Value, NumberStyles.None, CultureInfo.InvariantCulture, out patch); + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkSerializer.cs b/SAM.Analytical.Benchmark/BenchmarkSerializer.cs new file mode 100644 index 0000000..463de57 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkSerializer.cs @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public static class BenchmarkSerializer + { + private static readonly UTF8Encoding Utf8WithoutBom = new UTF8Encoding(false, true); + private static readonly JsonSerializerOptions SerializerOptions = CreateOptions(); + + public static string Serialize(BenchmarkDocument document) + { + BenchmarkValidator.ThrowIfInvalid(document); + BenchmarkDocument canonicalDocument = Canonicalize(document); + string json = JsonSerializer.Serialize(canonicalDocument, SerializerOptions); + return json.Replace("\r\n", "\n").Replace('\r', '\n'); + } + + public static byte[] SerializeToUtf8(BenchmarkDocument document) + { + return Utf8WithoutBom.GetBytes(Serialize(document)); + } + + public static BenchmarkDocument Deserialize(string json) + { + if (json == null) + { + throw new ArgumentNullException(nameof(json)); + } + + BenchmarkDocument? document = JsonSerializer.Deserialize(json, SerializerOptions); + BenchmarkValidator.ThrowIfInvalid(document); + return document!; + } + + public static BenchmarkDocument Deserialize(ReadOnlySpan utf8) + { + BenchmarkDocument? document = JsonSerializer.Deserialize(utf8, SerializerOptions); + BenchmarkValidator.ThrowIfInvalid(document); + return document!; + } + + public static void Write(string path, BenchmarkDocument document) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException("An output path is required.", nameof(path)); + } + + File.WriteAllBytes(path, SerializeToUtf8(document)); + } + + public static BenchmarkDocument Read(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException("An input path is required.", nameof(path)); + } + + return Deserialize(File.ReadAllBytes(path)); + } + + private static JsonSerializerOptions CreateOptions() + { + var options = new JsonSerializerOptions + { + AllowTrailingCommas = false, + DefaultIgnoreCondition = JsonIgnoreCondition.Never, + PropertyNameCaseInsensitive = false, + ReadCommentHandling = JsonCommentHandling.Disallow, + WriteIndented = true + }; + options.Converters.Add(new BenchmarkRouteConverter()); + options.Converters.Add(new EngineKindConverter()); + options.Converters.Add(new MetricUnitConverter()); + options.Converters.Add(new RunStateConverter()); + options.Converters.Add(new DesignDaySourceConverter()); + options.Converters.Add(new UtcDateTimeOffsetConverter()); + return options; + } + + private static BenchmarkDocument Canonicalize(BenchmarkDocument source) + { + return new BenchmarkDocument + { + SchemaVersion = source.SchemaVersion, + Provenance = Clone(source.Provenance!), + Model = Clone(source.Model!), + Spaces = source.Spaces! + .OrderBy(x => string.IsNullOrEmpty(x.Guid) ? "\uffff" : x.Guid, StringComparer.Ordinal) + .ThenBy(x => NormalizeName(x.Name), StringComparer.Ordinal) + .ThenBy(x => x.Name, StringComparer.Ordinal) + .Select(Clone) + .ToList() + }; + } + + private static BenchmarkProvenance Clone(BenchmarkProvenance source) + { + return new BenchmarkProvenance + { + SourceModelName = source.SourceModelName, + SourceModelGuid = source.SourceModelGuid, + SourceFileHash = source.SourceFileHash, + CanonicalModelHash = source.CanonicalModelHash, + CanonicalizationVersion = source.CanonicalizationVersion, + SamCommit = source.SamCommit, + RunnerCommit = source.RunnerCommit, + Engine = Clone(source.Engine!), + Route = source.Route, + Weather = Clone(source.Weather!), + DesignDaySource = source.DesignDaySource, + RunTimestampUtc = source.RunTimestampUtc, + DurationSeconds = source.DurationSeconds, + State = source.State, + ResultSources = Sort(source.ResultSources!), + Warnings = Sort(source.Warnings!), + Notes = Sort(source.Notes!) + }; + } + + private static BenchmarkEngine Clone(BenchmarkEngine source) + { + return new BenchmarkEngine + { + Kind = source.Kind, + Name = source.Name, + Version = source.Version, + SdkVersion = source.SdkVersion + }; + } + + private static BenchmarkWeather Clone(BenchmarkWeather source) + { + return new BenchmarkWeather { Identity = source.Identity, Hash = source.Hash }; + } + + private static BenchmarkModelResult Clone(BenchmarkModelResult source) + { + return new BenchmarkModelResult + { + ConsumptionHeating = Clone(source.ConsumptionHeating!), + ConsumptionCooling = Clone(source.ConsumptionCooling!), + PeakHeatingLoad = Clone(source.PeakHeatingLoad!), + PeakHeatingHour = Clone(source.PeakHeatingHour!), + PeakCoolingLoad = Clone(source.PeakCoolingLoad!), + PeakCoolingHour = Clone(source.PeakCoolingHour!), + FloorArea = Clone(source.FloorArea!), + Volume = Clone(source.Volume!) + }; + } + + private static BenchmarkSpaceResult Clone(BenchmarkSpaceResult source) + { + return new BenchmarkSpaceResult + { + Guid = source.Guid, + Name = source.Name, + Area = Clone(source.Area!), + Volume = Clone(source.Volume!), + Heating = Clone(source.Heating!), + Cooling = Clone(source.Cooling!) + }; + } + + private static BenchmarkConditionResult Clone(BenchmarkConditionResult source) + { + return new BenchmarkConditionResult + { + DesignLoad = Clone(source.DesignLoad!), + PeakLoad = Clone(source.PeakLoad!), + PeakHour = Clone(source.PeakHour!), + UnmetHours = Clone(source.UnmetHours!) + }; + } + + private static MetricValue Clone(MetricValue source) + { + return new MetricValue { Value = source.Value, Unit = source.Unit, Available = source.Available }; + } + + private static List Sort(IEnumerable values) + { + return values.OrderBy(x => x, StringComparer.Ordinal).ToList(); + } + + private static string NormalizeName(string? value) + { + return value?.Trim().Normalize().ToUpperInvariant() ?? string.Empty; + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkSpaceResult.cs b/SAM.Analytical.Benchmark/BenchmarkSpaceResult.cs new file mode 100644 index 0000000..16bb2e7 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkSpaceResult.cs @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkSpaceResult + { + [JsonPropertyName("guid")] + [JsonPropertyOrder(0)] + public string? Guid { get; set; } + + [JsonPropertyName("name")] + [JsonPropertyOrder(1)] + public string? Name { get; set; } + + [JsonPropertyName("area")] + [JsonPropertyOrder(2)] + public MetricValue? Area { get; set; } + + [JsonPropertyName("volume")] + [JsonPropertyOrder(3)] + public MetricValue? Volume { get; set; } + + [JsonPropertyName("heating")] + [JsonPropertyOrder(4)] + public BenchmarkConditionResult? Heating { get; set; } + + [JsonPropertyName("cooling")] + [JsonPropertyOrder(5)] + public BenchmarkConditionResult? Cooling { get; set; } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkValidationException.cs b/SAM.Analytical.Benchmark/BenchmarkValidationException.cs new file mode 100644 index 0000000..7efdc97 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkValidationException.cs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Linq; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkValidationException : Exception + { + public BenchmarkValidationException(BenchmarkValidationResult validationResult) + : base(CreateMessage(validationResult)) + { + ValidationResult = validationResult ?? throw new ArgumentNullException(nameof(validationResult)); + } + + public BenchmarkValidationResult ValidationResult { get; } + + private static string CreateMessage(BenchmarkValidationResult validationResult) + { + if (validationResult == null) + { + return "Benchmark validation failed."; + } + + return "Benchmark validation failed: " + string.Join("; ", validationResult.Errors.Select(x => x.ToString())); + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkValidationResult.cs b/SAM.Analytical.Benchmark/BenchmarkValidationResult.cs new file mode 100644 index 0000000..60fc374 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkValidationResult.cs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Collections.Generic; +using System.Linq; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkValidationResult + { + internal BenchmarkValidationResult(IEnumerable issues) + { + Issues = issues.ToArray(); + Errors = Issues.Where(x => x.Severity == ValidationSeverity.Error).ToArray(); + Warnings = Issues.Where(x => x.Severity == ValidationSeverity.Warning).ToArray(); + } + + public IReadOnlyList Issues { get; } + + public IReadOnlyList Errors { get; } + + public IReadOnlyList Warnings { get; } + + public bool IsValid => Errors.Count == 0; + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkValidator.cs b/SAM.Analytical.Benchmark/BenchmarkValidator.cs new file mode 100644 index 0000000..cdcc9e7 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkValidator.cs @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + +namespace SAM.Analytical.Benchmark +{ + public static class BenchmarkValidator + { + private static readonly Regex GuidPattern = new Regex("^[0-9a-f]{32}$", RegexOptions.CultureInvariant); + private static readonly Regex HashPattern = new Regex("^sha256:[0-9a-f]{64}$", RegexOptions.CultureInvariant); + private static readonly Regex CommitPattern = new Regex("^[0-9a-f]{7,64}$", RegexOptions.CultureInvariant); + + public static BenchmarkValidationResult Validate(BenchmarkDocument? document) + { + var issues = new List(); + if (document == null) + { + AddError(issues, "document.missing", "$", "The benchmark document is required."); + return new BenchmarkValidationResult(issues); + } + + ValidateSchemaVersion(document.SchemaVersion, issues); + ValidateProvenance(document.Provenance, issues); + ValidateModel(document.Model, issues); + ValidateSpaces(document.Spaces, document.Provenance?.Route ?? BenchmarkRoute.Unknown, issues); + return new BenchmarkValidationResult(issues); + } + + public static void ThrowIfInvalid(BenchmarkDocument? document) + { + BenchmarkValidationResult result = Validate(document); + if (!result.IsValid) + { + throw new BenchmarkValidationException(result); + } + } + + private static void ValidateSchemaVersion(string? version, ICollection issues) + { + switch (BenchmarkSchema.GetCompatibility(version)) + { + case SchemaCompatibility.Malformed: + AddError(issues, "schema.version.malformed", "$.schemaVersion", "A semantic version in major.minor.patch form is required."); + break; + case SchemaCompatibility.IncompatibleMajor: + AddError(issues, "schema.version.major", "$.schemaVersion", $"Schema major version must match {BenchmarkSchema.CurrentVersion}."); + break; + case SchemaCompatibility.CompatibleWithMinorWarning: + AddWarning(issues, "schema.version.minor", "$.schemaVersion", $"Schema minor version differs from {BenchmarkSchema.CurrentVersion}; known v1 fields will be validated."); + break; + } + } + + private static void ValidateProvenance(BenchmarkProvenance? provenance, ICollection issues) + { + if (provenance == null) + { + AddError(issues, "provenance.missing", "$.provenance", "Provenance is required."); + return; + } + + RequirePortableIdentity(provenance.SourceModelName, "$.provenance.sourceModelName", issues); + ValidateOptionalGuid(provenance.SourceModelGuid, "$.provenance.sourceModelGuid", issues); + ValidateOptionalHash(provenance.SourceFileHash, "$.provenance.sourceFileHash", issues); + ValidateOptionalHash(provenance.CanonicalModelHash, "$.provenance.canonicalModelHash", issues); + + if (!BenchmarkSchema.TryParseVersion(provenance.CanonicalizationVersion, out _, out _, out _)) + { + AddError(issues, "provenance.canonicalizationVersion", "$.provenance.canonicalizationVersion", "A semantic canonicalization version is required."); + } + + RequireCommit(provenance.SamCommit, "$.provenance.samCommit", issues); + RequireCommit(provenance.RunnerCommit, "$.provenance.runnerCommit", issues); + ValidateEngine(provenance.Engine, issues); + ValidateEnum(provenance.Route, BenchmarkRoute.Unknown, "provenance.route", "$.provenance.route", issues); + ValidateWeather(provenance.Weather, issues); + ValidateEnum(provenance.DesignDaySource, DesignDaySource.Unknown, "provenance.designDaySource", "$.provenance.designDaySource", issues); + + if (provenance.RunTimestampUtc == default) + { + AddError(issues, "provenance.timestamp.missing", "$.provenance.runTimestampUtc", "A UTC run timestamp is required."); + } + else if (provenance.RunTimestampUtc.Offset != TimeSpan.Zero) + { + AddError(issues, "provenance.timestamp.utc", "$.provenance.runTimestampUtc", "The run timestamp must be UTC."); + } + + if (!provenance.DurationSeconds.HasValue) + { + AddError(issues, "provenance.duration.missing", "$.provenance.durationSeconds", "Run duration is required."); + } + else if (!IsFinite(provenance.DurationSeconds.Value) || provenance.DurationSeconds.Value < 0) + { + AddError(issues, "provenance.duration.invalid", "$.provenance.durationSeconds", "Run duration must be a finite, non-negative number."); + } + + ValidateEnum(provenance.State, RunState.Unknown, "provenance.state", "$.provenance.state", issues); + ValidateStringList(provenance.ResultSources, "resultSources", "$.provenance.resultSources", issues, requireDistinct: true); + ValidateStringList(provenance.Warnings, "warnings", "$.provenance.warnings", issues, requireDistinct: false); + ValidateStringList(provenance.Notes, "notes", "$.provenance.notes", issues, requireDistinct: false); + ValidateRouteEngine(provenance.Route, provenance.Engine?.Kind ?? EngineKind.Unknown, issues); + + bool hasExplanation = (provenance.Warnings?.Count ?? 0) > 0 || (provenance.Notes?.Count ?? 0) > 0; + if (provenance.State == RunState.Success) + { + RequireGuid(provenance.SourceModelGuid, "$.provenance.sourceModelGuid", issues); + RequireHash(provenance.SourceFileHash, "$.provenance.sourceFileHash", issues); + RequireHash(provenance.CanonicalModelHash, "$.provenance.canonicalModelHash", issues); + RequireHash(provenance.Weather?.Hash, "$.provenance.weather.hash", issues); + } + else if (provenance.State == RunState.Failure && !hasExplanation) + { + AddError(issues, "provenance.failure.explanation", "$.provenance", "A failed run must include a warning or note explaining the failure."); + } + + if (provenance.Engine != null && provenance.Engine.Version == null && !hasExplanation) + { + AddError(issues, "provenance.engine.versionExplanation", "$.provenance.engine.version", "An unavailable engine version must be explained in warnings or notes."); + } + + } + + private static void ValidateEngine(BenchmarkEngine? engine, ICollection issues) + { + if (engine == null) + { + AddError(issues, "provenance.engine.missing", "$.provenance.engine", "Engine information is required."); + return; + } + + ValidateEnum(engine.Kind, EngineKind.Unknown, "provenance.engine.kind", "$.provenance.engine.kind", issues); + RequireText(engine.Name, "provenance.engine.name", "$.provenance.engine.name", issues); + OptionalText(engine.Version, "provenance.engine.version", "$.provenance.engine.version", issues); + OptionalText(engine.SdkVersion, "provenance.engine.sdkVersion", "$.provenance.engine.sdkVersion", issues); + } + + private static void ValidateWeather(BenchmarkWeather? weather, ICollection issues) + { + if (weather == null) + { + AddError(issues, "provenance.weather.missing", "$.provenance.weather", "Weather information is required."); + return; + } + + RequirePortableIdentity(weather.Identity, "$.provenance.weather.identity", issues); + ValidateOptionalHash(weather.Hash, "$.provenance.weather.hash", issues); + } + + private static void ValidateModel(BenchmarkModelResult? model, ICollection issues) + { + if (model == null) + { + AddError(issues, "model.missing", "$.model", "Model results are required."); + return; + } + + ValidateMetric(model.ConsumptionHeating, MetricUnit.KilowattHour, "$.model.consumptionHeating", MetricKind.NonNegative, issues); + ValidateMetric(model.ConsumptionCooling, MetricUnit.KilowattHour, "$.model.consumptionCooling", MetricKind.NonNegative, issues); + ValidateMetric(model.PeakHeatingLoad, MetricUnit.Kilowatt, "$.model.peakHeatingLoad", MetricKind.NonNegative, issues); + ValidateMetric(model.PeakHeatingHour, MetricUnit.HourOfYear, "$.model.peakHeatingHour", MetricKind.HourOfYear, issues); + ValidateMetric(model.PeakCoolingLoad, MetricUnit.Kilowatt, "$.model.peakCoolingLoad", MetricKind.NonNegative, issues); + ValidateMetric(model.PeakCoolingHour, MetricUnit.HourOfYear, "$.model.peakCoolingHour", MetricKind.HourOfYear, issues); + ValidateMetric(model.FloorArea, MetricUnit.SquareMetre, "$.model.floorArea", MetricKind.NonNegative, issues); + ValidateMetric(model.Volume, MetricUnit.CubicMetre, "$.model.volume", MetricKind.NonNegative, issues); + } + + private static void ValidateSpaces(IList? spaces, BenchmarkRoute route, ICollection issues) + { + if (spaces == null) + { + AddError(issues, "spaces.missing", "$.spaces", "The spaces array is required."); + return; + } + + var guids = new HashSet(StringComparer.Ordinal); + var fallbackNames = new HashSet(StringComparer.Ordinal); + bool nativeRoute = route == BenchmarkRoute.NativeOpenStudio || route == BenchmarkRoute.NativeTas; + + for (int index = 0; index < spaces.Count; index++) + { + BenchmarkSpaceResult? space = spaces[index]; + string path = $"$.spaces[{index}]"; + if (space == null) + { + AddError(issues, "space.missing", path, "A space result cannot be null."); + continue; + } + + if (space.Guid == string.Empty) + { + AddError(issues, "space.guid.empty", path + ".guid", "A space GUID must be null or a 32-character lowercase hexadecimal value."); + } + else if (space.Guid != null) + { + if (!GuidPattern.IsMatch(space.Guid)) + { + AddError(issues, "space.guid.malformed", path + ".guid", "A space GUID must contain 32 lowercase hexadecimal characters."); + } + else if (!guids.Add(space.Guid)) + { + AddError(issues, "space.guid.duplicate", path + ".guid", "Space GUIDs must be unique."); + } + } + else if (nativeRoute) + { + AddError(issues, "space.guid.required", path + ".guid", "Native routes require a source SAM space GUID."); + } + + RequireText(space.Name, "space.name.missing", path + ".name", issues); + if (space.Guid == null && !string.IsNullOrWhiteSpace(space.Name)) + { + string normalizedName = NormalizeName(space.Name!); + if (!fallbackNames.Add(normalizedName)) + { + AddError(issues, "space.name.duplicateFallback", path + ".name", "GUID-less fallback space names must be unique after normalization."); + } + } + + if (space.Guid == null && string.IsNullOrWhiteSpace(space.Name)) + { + AddError(issues, "space.identity.missing", path, "A space requires a GUID or non-empty name identity."); + } + + ValidateMetric(space.Area, MetricUnit.SquareMetre, path + ".area", MetricKind.NonNegative, issues); + ValidateMetric(space.Volume, MetricUnit.CubicMetre, path + ".volume", MetricKind.NonNegative, issues); + ValidateCondition(space.Heating, path + ".heating", issues); + ValidateCondition(space.Cooling, path + ".cooling", issues); + } + } + + private static void ValidateCondition(BenchmarkConditionResult? condition, string path, ICollection issues) + { + if (condition == null) + { + AddError(issues, "condition.missing", path, "Heating and cooling condition results are required."); + return; + } + + ValidateMetric(condition.DesignLoad, MetricUnit.Watt, path + ".designLoad", MetricKind.NonNegative, issues); + ValidateMetric(condition.PeakLoad, MetricUnit.Watt, path + ".peakLoad", MetricKind.NonNegative, issues); + ValidateMetric(condition.PeakHour, MetricUnit.HourOfYear, path + ".peakHour", MetricKind.HourOfYear, issues); + ValidateMetric(condition.UnmetHours, MetricUnit.Hour, path + ".unmetHours", MetricKind.NonNegative, issues); + } + + private static void ValidateMetric(MetricValue? metric, MetricUnit expectedUnit, string path, MetricKind kind, ICollection issues) + { + if (metric == null) + { + AddError(issues, "metric.missing", path, "The metric wrapper is required, even when the value is unavailable."); + return; + } + + if (!Enum.IsDefined(typeof(MetricUnit), metric.Unit) || metric.Unit == MetricUnit.Unknown) + { + AddError(issues, "metric.unit.unsupported", path + ".unit", "A supported metric unit is required."); + } + else if (metric.Unit != expectedUnit) + { + AddError(issues, "metric.unit.incompatible", path + ".unit", $"Expected unit {expectedUnit}."); + } + + if (metric.Available && !metric.Value.HasValue) + { + AddError(issues, "metric.availability.valueRequired", path, "An available metric must have a value."); + } + else if (!metric.Available && metric.Value.HasValue) + { + AddError(issues, "metric.availability.valueForbidden", path, "An unavailable metric must have a null value."); + } + + if (!metric.Value.HasValue) + { + return; + } + + double value = metric.Value.Value; + if (!IsFinite(value)) + { + AddError(issues, "metric.value.nonFinite", path + ".value", "Metric values must be finite."); + return; + } + + if (kind == MetricKind.NonNegative && value < 0) + { + AddError(issues, "metric.value.negative", path + ".value", "This metric must be non-negative."); + } + else if (kind == MetricKind.HourOfYear && (value < 0 || value > 8759 || value != Math.Truncate(value))) + { + AddError(issues, "metric.value.hourOfYear", path + ".value", "Hour-of-year must be an integer from 0 through 8759."); + } + } + + private static void ValidateOptionalHash(string? value, string path, ICollection issues) + { + if (value != null && !HashPattern.IsMatch(value)) + { + AddError(issues, "provenance.hash.malformed", path, "Hashes must use sha256: followed by 64 lowercase hexadecimal characters."); + } + } + + private static void RequireHash(string? value, string path, ICollection issues) + { + if (value == null) + { + AddError(issues, "provenance.hash.required", path, "A successful run requires this SHA-256 hash."); + } + } + + private static void ValidateOptionalGuid(string? value, string path, ICollection issues) + { + if (value != null && !GuidPattern.IsMatch(value)) + { + AddError(issues, "provenance.guid.malformed", path, "GUIDs must contain 32 lowercase hexadecimal characters."); + } + } + + private static void RequireGuid(string? value, string path, ICollection issues) + { + if (value == null) + { + AddError(issues, "provenance.guid.required", path, "A successful run requires the source model GUID."); + } + } + + private static void RequireCommit(string? value, string path, ICollection issues) + { + if (string.IsNullOrWhiteSpace(value) || !CommitPattern.IsMatch(value)) + { + AddError(issues, "provenance.commit.malformed", path, "A source revision of 7 to 64 lowercase hexadecimal characters is required."); + } + } + + private static void RequirePortableIdentity(string? value, string path, ICollection issues) + { + if (string.IsNullOrWhiteSpace(value)) + { + AddError(issues, "provenance.identity.missing", path, "A portable identity is required."); + return; + } + + if (Path.IsPathRooted(value!) || value!.StartsWith("/", StringComparison.Ordinal) || value.StartsWith("\\", StringComparison.Ordinal) + || Regex.IsMatch(value, "^[A-Za-z]:[\\\\/]", RegexOptions.CultureInvariant)) + { + AddError(issues, "provenance.identity.path", path, "Portable identities must not be absolute machine paths."); + } + } + + private static void RequireText(string? value, string code, string path, ICollection issues) + { + if (string.IsNullOrWhiteSpace(value)) + { + AddError(issues, code, path, "A non-empty value is required."); + } + } + + private static void OptionalText(string? value, string code, string path, ICollection issues) + { + if (value != null && string.IsNullOrWhiteSpace(value)) + { + AddError(issues, code, path, "Use null rather than an empty value."); + } + } + + private static void ValidateStringList(IList? values, string name, string path, ICollection issues, bool requireDistinct) + { + if (values == null) + { + AddError(issues, $"provenance.{name}.missing", path, $"The {name} array is required."); + return; + } + + var distinct = new HashSet(StringComparer.Ordinal); + for (int index = 0; index < values.Count; index++) + { + string? value = values[index]; + if (string.IsNullOrWhiteSpace(value)) + { + AddError(issues, $"provenance.{name}.empty", $"{path}[{index}]", "List entries must be non-empty."); + } + else if (requireDistinct && !distinct.Add(value)) + { + AddError(issues, $"provenance.{name}.duplicate", $"{path}[{index}]", "List entries must be distinct."); + } + } + } + + private static void ValidateRouteEngine(BenchmarkRoute route, EngineKind engine, ICollection issues) + { + bool openStudioRoute = route == BenchmarkRoute.NativeOpenStudio || route == BenchmarkRoute.SharedGbXmlOpenStudio; + bool tasRoute = route == BenchmarkRoute.NativeTas || route == BenchmarkRoute.SharedGbXmlTas; + if ((openStudioRoute && engine != EngineKind.OpenStudio) || (tasRoute && engine != EngineKind.Tas)) + { + AddError(issues, "provenance.routeEngine.mismatch", "$.provenance.route", "The route must match the declared engine kind."); + } + } + + private static void ValidateEnum(T value, T unknown, string code, string path, ICollection issues) where T : struct + { + if (!Enum.IsDefined(typeof(T), value) || EqualityComparer.Default.Equals(value, unknown)) + { + AddError(issues, code, path, "A supported value is required."); + } + } + + private static bool IsFinite(double value) + { + return !double.IsNaN(value) && !double.IsInfinity(value); + } + + private static string NormalizeName(string value) + { + return value.Trim().Normalize().ToUpperInvariant(); + } + + private static void AddError(ICollection issues, string code, string path, string message) + { + issues.Add(new ValidationIssue(code, path, message, ValidationSeverity.Error)); + } + + private static void AddWarning(ICollection issues, string code, string path, string message) + { + issues.Add(new ValidationIssue(code, path, message, ValidationSeverity.Warning)); + } + + private enum MetricKind + { + NonNegative, + HourOfYear + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkWeather.cs b/SAM.Analytical.Benchmark/BenchmarkWeather.cs new file mode 100644 index 0000000..ee1d0c8 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkWeather.cs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class BenchmarkWeather + { + [JsonPropertyName("identity")] + [JsonPropertyOrder(0)] + public string? Identity { get; set; } + + [JsonPropertyName("hash")] + [JsonPropertyOrder(1)] + public string? Hash { get; set; } + } +} diff --git a/SAM.Analytical.Benchmark/Enums.cs b/SAM.Analytical.Benchmark/Enums.cs new file mode 100644 index 0000000..3ad6147 --- /dev/null +++ b/SAM.Analytical.Benchmark/Enums.cs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +namespace SAM.Analytical.Benchmark +{ + public enum BenchmarkRoute + { + Unknown = 0, + NativeOpenStudio, + NativeTas, + SharedGbXmlOpenStudio, + SharedGbXmlTas + } + + public enum EngineKind + { + Unknown = 0, + OpenStudio, + Tas + } + + public enum MetricUnit + { + Unknown = 0, + KilowattHour, + WattHour, + Kilowatt, + Watt, + SquareMetre, + CubicMetre, + HourOfYear, + Hour + } + + public enum RunState + { + Unknown = 0, + Success, + Failure + } + + public enum DesignDaySource + { + Unknown = 0, + Ddy, + EmbeddedModel, + None + } + + public enum SchemaCompatibility + { + Malformed = 0, + IncompatibleMajor, + Compatible, + CompatibleWithMinorWarning + } + + public enum ValidationSeverity + { + Error = 0, + Warning + } +} diff --git a/SAM.Analytical.Benchmark/JsonConverters.cs b/SAM.Analytical.Benchmark/JsonConverters.cs new file mode 100644 index 0000000..a24360c --- /dev/null +++ b/SAM.Analytical.Benchmark/JsonConverters.cs @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + internal abstract class StringValueConverter : JsonConverter where T : struct + { + private readonly IReadOnlyDictionary values; + private readonly IReadOnlyDictionary names; + + protected StringValueConverter(IReadOnlyDictionary values) + { + this.values = values; + var reverse = new Dictionary(); + foreach (KeyValuePair item in values) + { + reverse.Add(item.Value, item.Key); + } + + names = reverse; + } + + protected abstract T Unknown { get; } + + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + { + throw new JsonException($"Expected a string value for {typeof(T).Name}."); + } + + string? value = reader.GetString(); + return value != null && values.TryGetValue(value, out T result) ? result : Unknown; + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + if (!names.TryGetValue(value, out string? name)) + { + throw new JsonException($"Unsupported {typeof(T).Name} value '{value}'."); + } + + writer.WriteStringValue(name); + } + } + + internal sealed class BenchmarkRouteConverter : StringValueConverter + { + public BenchmarkRouteConverter() + : base(new Dictionary(StringComparer.Ordinal) + { + ["Native-OpenStudio"] = BenchmarkRoute.NativeOpenStudio, + ["Native-TAS"] = BenchmarkRoute.NativeTas, + ["SharedGbXML-OpenStudio"] = BenchmarkRoute.SharedGbXmlOpenStudio, + ["SharedGbXML-TAS"] = BenchmarkRoute.SharedGbXmlTas + }) + { + } + + protected override BenchmarkRoute Unknown => BenchmarkRoute.Unknown; + } + + internal sealed class EngineKindConverter : StringValueConverter + { + public EngineKindConverter() + : base(new Dictionary(StringComparer.Ordinal) + { + ["OpenStudio"] = EngineKind.OpenStudio, + ["TAS"] = EngineKind.Tas + }) + { + } + + protected override EngineKind Unknown => EngineKind.Unknown; + } + + internal sealed class MetricUnitConverter : StringValueConverter + { + public MetricUnitConverter() + : base(new Dictionary(StringComparer.Ordinal) + { + ["kWh"] = MetricUnit.KilowattHour, + ["Wh"] = MetricUnit.WattHour, + ["kW"] = MetricUnit.Kilowatt, + ["W"] = MetricUnit.Watt, + ["m2"] = MetricUnit.SquareMetre, + ["m3"] = MetricUnit.CubicMetre, + ["hourOfYear"] = MetricUnit.HourOfYear, + ["h"] = MetricUnit.Hour + }) + { + } + + protected override MetricUnit Unknown => MetricUnit.Unknown; + } + + internal sealed class RunStateConverter : StringValueConverter + { + public RunStateConverter() + : base(new Dictionary(StringComparer.Ordinal) + { + ["Success"] = RunState.Success, + ["Failure"] = RunState.Failure + }) + { + } + + protected override RunState Unknown => RunState.Unknown; + } + + internal sealed class DesignDaySourceConverter : StringValueConverter + { + public DesignDaySourceConverter() + : base(new Dictionary(StringComparer.Ordinal) + { + ["DDY"] = DesignDaySource.Ddy, + ["EmbeddedModel"] = DesignDaySource.EmbeddedModel, + ["None"] = DesignDaySource.None + }) + { + } + + protected override DesignDaySource Unknown => DesignDaySource.Unknown; + } + + internal sealed class UtcDateTimeOffsetConverter : JsonConverter + { + public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) + { + throw new JsonException("Expected an ISO 8601 timestamp string."); + } + + string? text = reader.GetString(); + if (text == null || !DateTimeOffset.TryParse(text, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTimeOffset value)) + { + throw new JsonException("Expected an ISO 8601 timestamp."); + } + + return value; + } + + public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.UtcDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.FFFFFFF'Z'", CultureInfo.InvariantCulture)); + } + } +} diff --git a/SAM.Analytical.Benchmark/MetricValue.cs b/SAM.Analytical.Benchmark/MetricValue.cs new file mode 100644 index 0000000..423515b --- /dev/null +++ b/SAM.Analytical.Benchmark/MetricValue.cs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Text.Json.Serialization; + +namespace SAM.Analytical.Benchmark +{ + public sealed class MetricValue + { + [JsonPropertyName("value")] + [JsonPropertyOrder(0)] + public double? Value { get; set; } + + [JsonPropertyName("unit")] + [JsonPropertyOrder(1)] + public MetricUnit Unit { get; set; } + + [JsonPropertyName("available")] + [JsonPropertyOrder(2)] + public bool Available { get; set; } + + public static MetricValue AvailableValue(double value, MetricUnit unit) + { + return new MetricValue { Value = value, Unit = unit, Available = true }; + } + + public static MetricValue Unavailable(MetricUnit unit) + { + return new MetricValue { Value = null, Unit = unit, Available = false }; + } + } +} diff --git a/SAM.Analytical.Benchmark/SAM.Analytical.Benchmark.csproj b/SAM.Analytical.Benchmark/SAM.Analytical.Benchmark.csproj new file mode 100644 index 0000000..73e518f --- /dev/null +++ b/SAM.Analytical.Benchmark/SAM.Analytical.Benchmark.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + latest + enable + disable + + + + + + + diff --git a/SAM.Analytical.Benchmark/ValidationIssue.cs b/SAM.Analytical.Benchmark/ValidationIssue.cs new file mode 100644 index 0000000..58b97a7 --- /dev/null +++ b/SAM.Analytical.Benchmark/ValidationIssue.cs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +namespace SAM.Analytical.Benchmark +{ + public sealed class ValidationIssue + { + public ValidationIssue(string code, string path, string message, ValidationSeverity severity) + { + Code = code; + Path = path; + Message = message; + Severity = severity; + } + + public string Code { get; } + + public string Path { get; } + + public string Message { get; } + + public ValidationSeverity Severity { get; } + + public override string ToString() + { + return $"{Severity}: {Code} at {Path}: {Message}"; + } + } +} diff --git a/SAM_Validation.sln b/SAM_Validation.sln index 2aff899..c3745f2 100644 --- a/SAM_Validation.sln +++ b/SAM_Validation.sln @@ -9,6 +9,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAM_Mollier", "SAM_Mollier\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAM_Validation", "SAM_Validation\SAM_Validation.csproj", "{5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAM.Analytical.Benchmark", "SAM.Analytical.Benchmark\SAM.Analytical.Benchmark.csproj", "{A0E10A5F-698A-4D12-A76D-2F8D293B89E1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAM.Analytical.Benchmark.Tests", "SAM.Analytical.Benchmark.Tests\SAM.Analytical.Benchmark.Tests.csproj", "{A6C4F453-F2A9-43E4-8628-968868162703}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0B5632B9-81D8-46FF-B139-112C05D4428C}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig @@ -32,6 +36,14 @@ Global {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Release|Any CPU.Build.0 = Release|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Release|Any CPU.Build.0 = Release|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/benchmark/IMPLEMENTATION-PLAN.md b/docs/benchmark/IMPLEMENTATION-PLAN.md index 240d83c..b321071 100644 --- a/docs/benchmark/IMPLEMENTATION-PLAN.md +++ b/docs/benchmark/IMPLEMENTATION-PLAN.md @@ -183,7 +183,7 @@ If B1a's build-order verification finds the change must touch a repo not listed - **Objective:** encode the v1 schema + deterministic serializer + shared CLI host in `SAM.Analytical.Benchmark`, output to `SAM_Validation\build\`. - **Repo/branch:** `SAM_Validation` / `feature/benchmark-b1-schema`. New `SAM_Validation\SAM.Analytical.Benchmark\SAM.Analytical.Benchmark.csproj` (netstandard2.0, `System.Text.Json` 8.0.5, **no SAM/engine refs**, OutputPath → `..\build\` mirroring SAM.Core convention), namespace `SAM.Analytical.Benchmark`. - `Classes\` DTOs (§7): `BenchmarkDocument`, `BenchmarkProvenance`, `EngineInfo`, `WeatherInfo`, `ModelResult`, `SpaceResult`, `LoadTypeResult`, `MetricValue` (**`double? Value`, `Unit`, `bool Available`**), enums `BenchmarkRoute`, `EngineKind`, `MetricKey`, `Unit`, `RunState`. - - `Query\`: `Serializer` (STJ; sorted keys via explicit `JsonObject` build; `InvariantCulture`; fixed rounding), `SchemaVersion` const, `Validate(BenchmarkDocument)` — enforces the availability invariant. + - `Query\`: `Serializer` (STJ; sorted keys via explicit `JsonObject` build; `InvariantCulture`; full finite `double` precision without result rounding), `SchemaVersion` const, `Validate(BenchmarkDocument)` — enforces the availability invariant. - `Cli\`: `BenchmarkCli.Run(string[] args, Func body)`, `BenchmarkArgs` (`--model/--weather/--out/--route/--tolerance-profile`), invariant I/O, exit codes (`0 ok / 2 usage / 3 io / 4 engine`). - `Variables\`: `Tolerances` (`Warn=0.05`, `Fail=0.15`, `NearZeroFloor`), `SchemaVersionInfo`. - Test project `SAM_Validation\SAM_Analytical_Benchmark_Tests\` (net8.0 MSTest): round-trip determinism, invariant culture, key ordering, **availability invariant (available⇒non-null; unavailable⇒null; real 0⇒0+available)**, schema-version gate. @@ -258,7 +258,7 @@ If B1a's build-order verification finds the change must touch a repo not listed - **Dependencies:** B1a. - **Commit boundaries:** (1) classes + `Compare`/`AlignSpaces`; (2) banding + circular-hour + reconciliation; (3) report writers; (4) `Program.cs`; (5) fabricated tests. - **Rollback:** self-contained; revert PR. -- **Risks:** non-determinism → sorted keys + invariant culture + fixed rounding + byte-stability test. +- **Risks:** non-determinism → sorted keys + invariant culture + full finite `double` precision without result rounding + byte-stability test. ### B4 — Benchmark corpus - **Objective:** graduated **engine-independent** source models (single→multi-zone→reviewed production), separate from generated artefacts. @@ -343,7 +343,7 @@ Ordering rule: **B1a first**; B1b/B2/B3 parallelisable after B1a (B2 also gates ## 7. Benchmark schema outline (v1.0.0) -`benchmark-[-].json` — serialized by `SAM.Analytical.Benchmark.Query.Serializer` (System.Text.Json, invariant culture, sorted keys, fixed rounding). +`benchmark-[-].json` — serialized by `SAM.Analytical.Benchmark.Query.Serializer` (System.Text.Json, invariant culture, sorted keys, full finite `double` precision without result rounding). **Availability invariant (enforced by `Validate`):** `available=true ⇒ value` non-null; `available=false ⇒ value` **null**; a real measured zero is `value:0, available:true`. Unavailable metrics **never** carry `0`. @@ -429,7 +429,7 @@ Ordering rule: **B1a first**; B1b/B2/B3 parallelisable after B1a (B2 also gates | Engine coupling leaking into comparator/GH | Violates isolation | Comparator/GH ref only schema (+comparator) lib; CI reference-check. | | Unavailable metric encoded as 0 | Silent false comparison | `MetricValue` invariant (`available=false ⇒ value null`), enforced by `Validate` + tests. | | B2b greenfield reverse translator | Wasted effort | Spike-gated; strictly optional; distinct route tag. | -| Report non-determinism | Non-reproducible gate | Sorted keys, invariant culture, fixed rounding; byte-stability tests. | +| Report non-determinism | Non-reproducible gate | Sorted keys, invariant culture, full finite `double` precision without result rounding; byte-stability tests. | | Tolerances mistaken as validated | Overclaiming | Label provisional; configurable profiles; corpus-driven revision later. | | Conditioning pairing frozen too early | Invalid comparison baked into B1/B2 | D10: candidate in B0, frozen only after B2 SingleBox validation. | | Cross-laptop path drift | Irreproducible | Portable JSON, relative layouts, no absolute paths committed. | diff --git a/docs/benchmark/SCHEMA.md b/docs/benchmark/SCHEMA.md index e2193ca..891045f 100644 --- a/docs/benchmark/SCHEMA.md +++ b/docs/benchmark/SCHEMA.md @@ -4,7 +4,7 @@ `benchmark-[-].json` is the portable hand-off between an engine-specific producer and the engine-neutral comparator. The schema owns measurements and run provenance; it does not serialize engine runtime objects or the SAM result graph. -The implementation will use System.Text.Json with invariant-culture numbers, deterministic property order, deterministic array order, and fixed numeric rounding. JSON object order is not semantic, but deterministic output is required for reproducibility. +The implementation will use System.Text.Json with invariant-culture numbers, deterministic property order, deterministic array order, and full finite `double` precision without result rounding. JSON object order is not semantic, but deterministic output is required for reproducibility. ## Top-level document From 5cd1a3b534d315aa20db91f41270c8b11cca733c Mon Sep 17 00:00:00 2001 From: SAM-BIM-AI Date: Wed, 22 Jul 2026 10:08:59 +0200 Subject: [PATCH 03/11] =?UTF-8?q?B1a=20follow-up=20=E2=80=94=20canonical?= =?UTF-8?q?=20benchmark=20model=20hashing=20(#6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: define canonical benchmark model hashing B1a requires canonicalModelHash for successful benchmark documents, but the merged schema defined no reproducible canonical SAM representation, so B1b producers had nothing to call and would have had to invent engine-specific hashing. Add an engine-neutral canonicalization contract and implementation: - BenchmarkCanonicalization: the single authoritative canonicalization version ("1.0.0") plus major/minor classification, reusing BenchmarkSchema version parsing. - BenchmarkCanonicalJson: canonical JSON v1 over UTF-8 bytes or text, and SHA-256 of the canonical bytes through the existing BenchmarkHash format. - CanonicalJsonWriter: internal recursive traversal that sorts object properties in ordinal order, preserves array order, normalises numbers and escapes, and rejects duplicate names, comments, trailing commas, non-finite numbers and malformed text. Validation now classifies canonicalizationVersion instead of only parsing it: 1.0.0 is accepted, an unsupported major version is rejected as provenance.canonicalizationVersion.major, and malformed versions keep the existing code. canonicalModelHash stays mandatory for successful documents and failure documents are not made stricter. The library gains no SAM, OpenStudio, TAS, Rhino or Grasshopper dependency: it canonicalizes JSON a producer supplies. Obtaining the neutral SAM model JSON from a loaded AnalyticalModel remains each producer's responsibility. Co-Authored-By: Claude Opus 4.8 * docs: specify canonical benchmark model representation B0 named canonicalization and B1a made canonicalModelHash mandatory, but neither document said what the canonical representation actually is, so a producer could not implement the hash without guessing. SCHEMA.md gains a Canonical model hashing section: the sourceFileHash versus canonicalModelHash distinction as a table, the six-step producer procedure, the source-bytes-to-canonical-hash pipeline, the requirement to hash the neutral loaded SAM model before any engine-specific translation or mutation, the exact canonical JSON v1 rules, and a warning that changing any rule changes every hash and requires a new canonicalizationVersion. The compatibility policy now states what validation enforces: a well-formed version on every document, and rejection of an unsupported major. METHODOLOGY.md gains the same distinction and pipeline in workflow terms, and states that producers call the shared helper rather than inventing their own canonicalization, so a hash difference means a model difference. The rules are documented as a versioned SAM benchmark contract, explicitly not a claim of full semantic JSON equivalence, and no OpenStudio- or TAS-specific implementation detail is introduced. Co-Authored-By: Claude Opus 4.8 --------- Co-authored-by: Claude Opus 4.8 --- .../CanonicalJsonTests.cs | 335 ++++++++++++++++++ .../Fixtures/canonicalization-model-a.json | 24 ++ .../Fixtures/canonicalization-model-b.json | 1 + .../canonicalization-model.canonical.json | 1 + .../TestDocuments.cs | 2 +- .../ValidationTests.cs | 84 ++++- .../BenchmarkCanonicalJson.cs | 132 +++++++ .../BenchmarkCanonicalization.cs | 52 +++ .../BenchmarkValidator.cs | 20 +- .../CanonicalJsonWriter.cs | 138 ++++++++ docs/benchmark/METHODOLOGY.md | 21 ++ docs/benchmark/SCHEMA.md | 70 +++- 12 files changed, 870 insertions(+), 10 deletions(-) create mode 100644 SAM.Analytical.Benchmark.Tests/CanonicalJsonTests.cs create mode 100644 SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model-a.json create mode 100644 SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model-b.json create mode 100644 SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model.canonical.json create mode 100644 SAM.Analytical.Benchmark/BenchmarkCanonicalJson.cs create mode 100644 SAM.Analytical.Benchmark/BenchmarkCanonicalization.cs create mode 100644 SAM.Analytical.Benchmark/CanonicalJsonWriter.cs diff --git a/SAM.Analytical.Benchmark.Tests/CanonicalJsonTests.cs b/SAM.Analytical.Benchmark.Tests/CanonicalJsonTests.cs new file mode 100644 index 0000000..d9e3ee7 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/CanonicalJsonTests.cs @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Globalization; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Tests +{ + /// + /// Canonical JSON v1 rules. The source is deliberately ASCII-only: JSON escape sequences appear in + /// raw string literals and literal code points in ordinary literals, so no assertion depends on how + /// this file happens to be encoded on disk. + /// + [TestClass] + public sealed class CanonicalJsonTests + { + private static readonly Regex HashPattern = new Regex("^sha256:[0-9a-f]{64}$", RegexOptions.CultureInvariant); + + [TestMethod] + public void ObjectPropertyOrderIsNotSignificant() + { + const string first = """{"b":1,"a":2,"c":3}"""; + const string second = """{"c":3,"b":1,"a":2}"""; + + Assert.AreEqual("""{"a":2,"b":1,"c":3}""", BenchmarkCanonicalJson.Canonicalize(first)); + CollectionAssert.AreEqual(Canonicalize(first), Canonicalize(second)); + Assert.AreEqual(BenchmarkCanonicalJson.ComputeSha256(first), BenchmarkCanonicalJson.ComputeSha256(second)); + } + + [TestMethod] + public void InsignificantWhitespaceIsNotSignificant() + { + const string compact = """{"a":[1,2],"b":{"c":null}}"""; + const string spaced = " {\n\t\"a\" : [ 1 , 2 ] ,\r\n \"b\" : { \"c\" : null }\n} \n"; + + Assert.AreEqual(compact, BenchmarkCanonicalJson.Canonicalize(spaced)); + CollectionAssert.AreEqual(Canonicalize(compact), Canonicalize(spaced)); + Assert.AreEqual(BenchmarkCanonicalJson.ComputeSha256(compact), BenchmarkCanonicalJson.ComputeSha256(spaced)); + } + + [TestMethod] + public void ArrayOrderRemainsSignificant() + { + const string first = """{"a":[1,2,3]}"""; + const string second = """{"a":[3,2,1]}"""; + + Assert.AreEqual(first, BenchmarkCanonicalJson.Canonicalize(first)); + Assert.AreEqual(second, BenchmarkCanonicalJson.Canonicalize(second)); + Assert.AreNotEqual(BenchmarkCanonicalJson.ComputeSha256(first), BenchmarkCanonicalJson.ComputeSha256(second)); + } + + [TestMethod] + public void ArrayElementObjectsAreSortedWithoutReorderingTheArray() + { + Assert.AreEqual( + """[{"a":2,"b":1},{"a":1,"b":2}]""", + BenchmarkCanonicalJson.Canonicalize("""[{"b":1,"a":2},{"b":2,"a":1}]""")); + } + + [TestMethod] + public void NestedObjectPropertiesAreSortedRecursively() + { + Assert.AreEqual( + """{"a":[{"c":2,"d":1}],"z":{"y":{"a":2,"b":1},"z":{"b":3,"c":4}}}""", + BenchmarkCanonicalJson.Canonicalize("""{"z":{"z":{"c":4,"b":3},"y":{"b":1,"a":2}},"a":[{"d":1,"c":2}]}""")); + } + + [TestMethod] + public void PropertyNamesAreSortedByOrdinalUtf16CodeUnit() + { + // Ordinal order is I (U+0049), i (U+0069), U+0130, U+0131 - never a linguistic collation. + Assert.AreEqual( + """{"I":2,"i":1,"\u0130":3,"\u0131":4}""", + BenchmarkCanonicalJson.Canonicalize("""{"i":1,"I":2,"\u0130":3,"\u0131":4}""")); + + // A supplementary code point sorts by its leading surrogate, so U+1F600 precedes U+E000. + Assert.AreEqual( + """{"\uD83D\uDE00":2,"\uE000":1,"\uFFFF":3}""", + BenchmarkCanonicalJson.Canonicalize("""{"\uE000":1,"\uD83D\uDE00":2,"\uFFFF":3}""")); + } + + [TestMethod] + public void StringEscapingIsDeterministic() + { + // Whichever legal spelling the input used, one escaped form comes out. + Assert.AreEqual("""["Ab"]""", BenchmarkCanonicalJson.Canonicalize("""["\u0041\u0062"]""")); + Assert.AreEqual("""["a/b\\c"]""", BenchmarkCanonicalJson.Canonicalize("""["a\/b\\c"]""")); + Assert.AreEqual( + """["\t\n\r\b\f\u0000\u001F"]""", + BenchmarkCanonicalJson.Canonicalize("""["\t\n\r\b\f\u0000\u001f"]""")); + Assert.AreEqual( + """["\u003C\u0026\u003E\u0027\u002B=\u0060"]""", + BenchmarkCanonicalJson.Canonicalize("""["<&>'+=`"]""")); + Assert.AreEqual( + """["\u007F\u00A0"]""", + BenchmarkCanonicalJson.Canonicalize("""["\u007f\u00a0"]""")); + } + + [TestMethod] + public void UnicodeIsDeterministicAndCanonicalOutputIsAscii() + { + // U+00E9, U+2014, U+4E2D U+6587, U+1F600 as literal code points and as JSON escapes. + const string literal = "[\"caf\u00e9 \u2014 \u4e2d\u6587 \ud83d\ude00\"]"; + const string escaped = """["caf\u00e9 \u2014 \u4e2d\u6587 \ud83d\ude00"]"""; + + Assert.AreEqual( + """["caf\u00E9 \u2014 \u4E2D\u6587 \uD83D\uDE00"]""", + BenchmarkCanonicalJson.Canonicalize(literal)); + CollectionAssert.AreEqual(Canonicalize(literal), Canonicalize(escaped)); + Assert.AreEqual(BenchmarkCanonicalJson.ComputeSha256(literal), BenchmarkCanonicalJson.ComputeSha256(escaped)); + Assert.IsTrue(Canonicalize(literal).All(x => x < 0x80), "Canonical JSON v1 escapes every non-ASCII character."); + } + + [TestMethod] + public void IntegerAndFloatingPointValuesSerializeDeterministically() + { + Assert.AreEqual( + """[1,1,1,1,1,0.1,-12.5,100,9223372036854775807,-9223372036854775808,9223372036854775808]""", + BenchmarkCanonicalJson.Canonicalize("""[1,1.0,1.00,1e0,1E0,0.1,-12.5,1e2,9223372036854775807,-9223372036854775808,9223372036854775808]""")); + + // Finite binary64 precision survives canonicalization unrounded. + Assert.AreEqual( + """[0.12345678901234566,3.141592653589793]""", + BenchmarkCanonicalJson.Canonicalize("""[0.12345678901234566,3.141592653589793]""")); + } + + [TestMethod] + public void GenuineNumericZeroRemainsZero() + { + // Zero is a measured value; it must never be dropped, nulled, or split by its spelling. + Assert.AreEqual( + """{"a":0,"b":0,"c":0,"d":0,"e":0,"f":0}""", + BenchmarkCanonicalJson.Canonicalize("""{"a":0,"b":0.0,"c":-0.0,"d":0e0,"e":-0,"f":-0.0e5}""")); + } + + [TestMethod] + public void BooleansAndNullsArePreserved() + { + Assert.AreEqual( + """{"f":false,"n":null,"t":true}""", + BenchmarkCanonicalJson.Canonicalize("""{"t":true,"f":false,"n":null}""")); + + Assert.AreEqual("""{"a":{},"b":[]}""", BenchmarkCanonicalJson.Canonicalize("""{"a":{ },"b":[ ]}""")); + } + + [TestMethod] + public void DuplicatePropertyNamesAreRejected() + { + AssertRejected("""{"a":1,"a":2}"""); + AssertRejected("""{"a":1,"a":1}"""); + AssertRejected("""{"x":{"a":1,"a":2}}"""); + + // Names that differ only by escape spelling are the same name. + AssertRejected("""{"a":1,"\u0061":2}"""); + } + + [TestMethod] + public void CommentsAreRejected() + { + AssertRejected("""{"a":1 /* block */}"""); + AssertRejected("""{"a":1} // line"""); + } + + [TestMethod] + public void TrailingCommasAreRejected() + { + AssertRejected("""{"a":1,}"""); + AssertRejected("""[1,2,]"""); + } + + [TestMethod] + public void InvalidJsonIsRejected() + { + AssertRejected(string.Empty); + AssertRejected("{"); + AssertRejected("""{"a"}"""); + AssertRejected("""{'a':1}"""); + AssertRejected("[01]"); + AssertRejected("[NaN]"); + AssertRejected("""{"a":Infinity}"""); + AssertRejected("""{"a":1} {"b":2}"""); + AssertRejected("\ufeff{}"); + + // Overflowing binary64 is a non-finite value, not a very large number. + AssertRejected("[1e400]"); + AssertRejected("[-1e400]"); + } + + [TestMethod] + public void NestingBeyondTheSupportedDepthIsRejected() + { + string supported = new string('[', 255) + "1" + new string(']', 255); + + Assert.AreEqual(supported, BenchmarkCanonicalJson.Canonicalize(supported)); + AssertRejected(new string('[', 300) + "1" + new string(']', 300)); + } + + [TestMethod] + public void NullInputIsRejected() + { + Assert.ThrowsException(() => BenchmarkCanonicalJson.Canonicalize((string)null!)); + Assert.ThrowsException(() => BenchmarkCanonicalJson.ComputeSha256((string)null!)); + } + + [TestMethod] + public void RepeatedCallsAreByteIdentical() + { + const string json = """{"b":[1,2,{"z":null,"a":"\u00e9"}],"a":0.1}"""; + + byte[] first = Canonicalize(json); + byte[] second = Canonicalize(json); + byte[] reapplied = Canonicalize(BenchmarkCanonicalJson.Canonicalize(json)); + + CollectionAssert.AreEqual(first, second); + CollectionAssert.AreEqual(first, reapplied); + Assert.AreEqual(BenchmarkCanonicalJson.ComputeSha256(json), BenchmarkCanonicalJson.ComputeSha256(json)); + } + + [TestMethod] + public void CurrentOperatingSystemCultureDoesNotChangeTheResult() + { + const string json = """{"i":1,"I":2,"value":12.5,"small":1e-7,"text":"Istanbul"}"""; + + CultureInfo culture = CultureInfo.CurrentCulture; + CultureInfo uiCulture = CultureInfo.CurrentUICulture; + byte[] invariant; + byte[] german; + byte[] turkish; + try + { + // de-DE uses a comma decimal separator; tr-TR collates dotted and dotless i differently. + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture; + invariant = Canonicalize(json); + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE"); + CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("de-DE"); + german = Canonicalize(json); + CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("tr-TR"); + CultureInfo.CurrentUICulture = CultureInfo.GetCultureInfo("tr-TR"); + turkish = Canonicalize(json); + } + finally + { + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = uiCulture; + } + + CollectionAssert.AreEqual(invariant, german); + CollectionAssert.AreEqual(invariant, turkish); + StringAssert.Contains(Encoding.UTF8.GetString(invariant), "12.5"); + } + + [TestMethod] + public void Utf8AndStringInputProduceIdenticalResults() + { + const string json = """{"b":"\u00e9\u4e2d\ud83d\ude00","a":[0,-0.0,1e2]}"""; + + byte[] utf8 = Encoding.UTF8.GetBytes(json); + + CollectionAssert.AreEqual(BenchmarkCanonicalJson.Canonicalize(utf8.AsSpan()), Canonicalize(json)); + Assert.AreEqual( + Encoding.UTF8.GetString(BenchmarkCanonicalJson.Canonicalize(utf8.AsSpan())), + BenchmarkCanonicalJson.Canonicalize(json)); + Assert.AreEqual(BenchmarkCanonicalJson.ComputeSha256(utf8.AsSpan()), BenchmarkCanonicalJson.ComputeSha256(json)); + } + + [TestMethod] + public void HashUsesTheBenchmarkSha256FormatOverCanonicalBytes() + { + const string json = """{ "b" : 1 , "a" : 2 }"""; + + string hash = BenchmarkCanonicalJson.ComputeSha256(json); + + Assert.IsTrue(HashPattern.IsMatch(hash), $"Expected sha256:<64 lowercase hex>, got '{hash}'."); + Assert.AreEqual(hash, hash.ToLowerInvariant()); + Assert.AreEqual(BenchmarkHash.ComputeSha256(Canonicalize(json)), hash); + Assert.AreEqual(BenchmarkHash.ComputeSha256("""{"a":2,"b":1}"""), hash); + } + + [TestMethod] + public void CanonicalOutputHasNoBomAndNoInsignificantWhitespace() + { + byte[] canonical = Canonicalize("""{ "a" : 1 , "b" : "x" }"""); + + Assert.IsFalse(canonical.Length >= 3 && canonical[0] == 0xef && canonical[1] == 0xbb && canonical[2] == 0xbf); + Assert.IsFalse(canonical.Contains((byte)13), "Canonical JSON v1 never emits a carriage return."); + Assert.IsFalse(canonical.Contains((byte)10), "Canonical JSON v1 emits no trailing newline."); + Assert.IsFalse(canonical.Contains((byte)32), "Canonical JSON v1 emits no insignificant whitespace."); + } + + [TestMethod] + public void EquivalentFixturesCanonicalizeToTheSameRecordedBytesAndHash() + { + byte[] expected = File.ReadAllBytes(FixturePath("canonicalization-model.canonical.json")); + byte[] first = File.ReadAllBytes(FixturePath("canonicalization-model-a.json")); + byte[] second = File.ReadAllBytes(FixturePath("canonicalization-model-b.json")); + + CollectionAssert.AreEqual(expected, BenchmarkCanonicalJson.Canonicalize(first.AsSpan())); + CollectionAssert.AreEqual(expected, BenchmarkCanonicalJson.Canonicalize(second.AsSpan())); + Assert.AreEqual( + BenchmarkCanonicalJson.ComputeSha256(first.AsSpan()), + BenchmarkCanonicalJson.ComputeSha256(second.AsSpan())); + Assert.AreEqual(BenchmarkHash.ComputeSha256(expected), BenchmarkCanonicalJson.ComputeSha256(first.AsSpan())); + } + + private static byte[] Canonicalize(string json) + { + return BenchmarkCanonicalJson.Canonicalize(Encoding.UTF8.GetBytes(json).AsSpan()); + } + + private static void AssertRejected(string json) + { + Exception? thrown = null; + try + { + BenchmarkCanonicalJson.Canonicalize(json); + } + catch (Exception exception) + { + thrown = exception; + } + + Assert.IsNotNull(thrown, $"Expected canonical JSON v1 to reject '{json}'."); + Assert.IsInstanceOfType(thrown, $"Expected a JsonException for '{json}', got {thrown!.GetType().Name}."); + } + + private static string FixturePath(string name) + { + return Path.Combine(AppContext.BaseDirectory, "Fixtures", name); + } + } +} diff --git a/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model-a.json b/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model-a.json new file mode 100644 index 0000000..4e8d7a9 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model-a.json @@ -0,0 +1,24 @@ +{ + "type": "SAM.Analytical.AnalyticalModel", + "name": "SingleBox", + "guid": "0123456789abcdef0123456789abcdef", + "spaces": [ + { "name": "Office B", "area": 40.0, "index": 1 }, + { "name": "Office A", "area": 200, "index": 0 } + ], + "metrics": { + "zero": 0, + "negative": -12.5, + "exponent": 1e2, + "trailingZeros": 1.500 + }, + "flags": { + "occupied": true, + "vented": false, + "override": null + }, + "text": { + "unicode": "café — 中文 😀", + "escaped": "line\nbreak\t\"quoted\" \\ " + } +} diff --git a/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model-b.json b/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model-b.json new file mode 100644 index 0000000..bc49b2b --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model-b.json @@ -0,0 +1 @@ +{"flags":{"override":null,"vented":false,"occupied":true},"guid":"0123456789abcdef0123456789abcdef","metrics":{"trailingZeros":1.5,"exponent":100,"negative":-12.5,"zero":0},"name":"SingleBox","spaces":[{"index":1,"area":40,"name":"Office B"},{"index":0,"area":200.0,"name":"Office A"}],"text":{"escaped":"line\nbreak\t\u0022quoted\u0022 \\ <\/script>","unicode":"caf\u00e9 \u2014 中文 😀"},"type":"SAM.Analytical.AnalyticalModel"} diff --git a/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model.canonical.json b/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model.canonical.json new file mode 100644 index 0000000..9508b20 --- /dev/null +++ b/SAM.Analytical.Benchmark.Tests/Fixtures/canonicalization-model.canonical.json @@ -0,0 +1 @@ +{"flags":{"occupied":true,"override":null,"vented":false},"guid":"0123456789abcdef0123456789abcdef","metrics":{"exponent":100,"negative":-12.5,"trailingZeros":1.5,"zero":0},"name":"SingleBox","spaces":[{"area":40,"index":1,"name":"Office B"},{"area":200,"index":0,"name":"Office A"}],"text":{"escaped":"line\nbreak\t\u0022quoted\u0022 \\ \u003C/script\u003E","unicode":"caf\u00E9 \u2014 \u4E2D\u6587 \uD83D\uDE00"},"type":"SAM.Analytical.AnalyticalModel"} \ No newline at end of file diff --git a/SAM.Analytical.Benchmark.Tests/TestDocuments.cs b/SAM.Analytical.Benchmark.Tests/TestDocuments.cs index d72ba8c..3eb7258 100644 --- a/SAM.Analytical.Benchmark.Tests/TestDocuments.cs +++ b/SAM.Analytical.Benchmark.Tests/TestDocuments.cs @@ -22,7 +22,7 @@ internal static BenchmarkDocument CreateValid() SourceModelGuid = "0123456789abcdef0123456789abcdef", SourceFileHash = HashA, CanonicalModelHash = HashB, - CanonicalizationVersion = "1.0.0", + CanonicalizationVersion = BenchmarkCanonicalization.CurrentVersion, SamCommit = "1111111111111111111111111111111111111111", RunnerCommit = "2222222222222222222222222222222222222222", Engine = new BenchmarkEngine diff --git a/SAM.Analytical.Benchmark.Tests/ValidationTests.cs b/SAM.Analytical.Benchmark.Tests/ValidationTests.cs index 1ec3334..bbcd2fc 100644 --- a/SAM.Analytical.Benchmark.Tests/ValidationTests.cs +++ b/SAM.Analytical.Benchmark.Tests/ValidationTests.cs @@ -286,11 +286,89 @@ public void MalformedCommitAndCanonicalizationVersionAreRejected() { BenchmarkDocument commit = TestDocuments.CreateValid(); commit.Provenance!.SamCommit = "not-a-commit"; - BenchmarkDocument version = TestDocuments.CreateValid(); - version.Provenance!.CanonicalizationVersion = "v1"; AssertInvalid(commit, "provenance.commit.malformed"); - AssertInvalid(version, "provenance.canonicalizationVersion"); + + foreach (string malformed in new[] { "v1", "1.0", "1.0.0-rc1", "01.0.0", " ", "" }) + { + BenchmarkDocument version = TestDocuments.CreateValid(); + version.Provenance!.CanonicalizationVersion = malformed; + AssertInvalid(version, "provenance.canonicalizationVersion"); + } + + BenchmarkDocument missing = TestDocuments.CreateValid(); + missing.Provenance!.CanonicalizationVersion = null; + AssertInvalid(missing, "provenance.canonicalizationVersion"); + } + + [TestMethod] + public void ValidationAcceptsTheCanonicalizationVersionConstant() + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.CanonicalizationVersion = BenchmarkCanonicalization.CurrentVersion; + + BenchmarkValidationResult result = BenchmarkValidator.Validate(document); + + Assert.AreEqual("1.0.0", BenchmarkCanonicalization.CurrentVersion); + Assert.IsTrue(result.IsValid); + Assert.AreEqual(0, result.Warnings.Count); + } + + [TestMethod] + public void UnsupportedCanonicalizationMajorVersionIsRejected() + { + foreach (string unsupported in new[] { "2.0.0", "0.9.0", "10.0.0" }) + { + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.CanonicalizationVersion = unsupported; + AssertInvalid(document, "provenance.canonicalizationVersion.major"); + } + } + + [TestMethod] + public void CanonicalizationVersionIsRequiredEvenWhenAFailureOmitsTheCanonicalHash() + { + // A failure document may omit hashes, but must still declare which rules it would have used. + BenchmarkDocument document = TestDocuments.CreateValid(); + document.Provenance!.State = RunState.Failure; + document.Provenance.SourceFileHash = null; + document.Provenance.CanonicalModelHash = null; + document.Provenance.Weather!.Hash = null; + + Assert.IsTrue(BenchmarkValidator.Validate(document).IsValid); + + document.Provenance.CanonicalizationVersion = null; + AssertInvalid(document, "provenance.canonicalizationVersion"); + } + + [TestMethod] + public void SuccessfulDocumentStillRequiresBothTheCanonicalHashAndItsVersion() + { + BenchmarkDocument noHash = TestDocuments.CreateValid(); + noHash.Provenance!.CanonicalModelHash = null; + BenchmarkDocument malformedHash = TestDocuments.CreateValid(); + malformedHash.Provenance!.CanonicalModelHash = "sha256:NOTHEX"; + BenchmarkDocument noVersion = TestDocuments.CreateValid(); + noVersion.Provenance!.CanonicalizationVersion = null; + + Assert.AreEqual(RunState.Success, TestDocuments.CreateValid().Provenance!.State); + AssertInvalid(noHash, "provenance.hash.required"); + AssertInvalid(malformedHash, "provenance.hash.malformed"); + AssertInvalid(noVersion, "provenance.canonicalizationVersion"); + } + + [TestMethod] + public void GoldenBenchmarkDocumentRemainsValidUnderTheCanonicalizationContract() + { + string path = Path.Combine(AppContext.BaseDirectory, "Fixtures", "golden-benchmark.json"); + + BenchmarkDocument document = BenchmarkSerializer.Read(path); + BenchmarkValidationResult result = BenchmarkValidator.Validate(document); + + Assert.IsTrue(result.IsValid, string.Join(", ", result.Errors.Select(x => x.Code))); + Assert.AreEqual(0, result.Warnings.Count); + Assert.AreEqual(BenchmarkCanonicalization.CurrentVersion, document.Provenance!.CanonicalizationVersion); + Assert.IsNotNull(document.Provenance.CanonicalModelHash); } private static void AssertInvalid(BenchmarkDocument document, string expectedCode) diff --git a/SAM.Analytical.Benchmark/BenchmarkCanonicalJson.cs b/SAM.Analytical.Benchmark/BenchmarkCanonicalJson.cs new file mode 100644 index 0000000..5d07efb --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkCanonicalJson.cs @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.IO; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; + +namespace SAM.Analytical.Benchmark +{ + /// + /// Canonical JSON v1: the engine-neutral representation hashed into + /// provenance.canonicalModelHash. + /// + /// + /// + /// The helper canonicalizes JSON supplied by a producer. It has no knowledge of SAM, OpenStudio, + /// Tas, Rhino, or Grasshopper: obtaining the neutral SAM model JSON is each producer's + /// responsibility, and the canonical hash must be taken from the loaded AnalyticalModel + /// before any engine-specific translation or mutation. + /// + /// + /// The v1 rules are: + /// input must be a single valid JSON value encoded as UTF-8 without a byte order mark; + /// object properties are written in ascending ordinal (UTF-16 code unit) property-name order; + /// array order is preserved exactly; + /// property names and string values are escaped by using + /// ; + /// booleans and nulls keep their normal JSON representation; + /// numbers that fit or are emitted exactly and all other + /// numbers are emitted through the shortest round-trippable binary64 form, with negative zero + /// normalised to zero; + /// no indentation or insignificant whitespace is emitted, and no trailing newline; + /// duplicate object-property names, comments, trailing commas, non-finite numbers, text that is not + /// well-formed UTF-8, and nesting deeper than 256 levels are rejected; + /// no property is removed, ignored, or otherwise normalised in version 1. + /// + /// + /// This is a versioned SAM benchmark canonicalization contract, not a claim of full semantic JSON + /// equivalence. Numeric precision beyond binary64 is not preserved, and two documents that a JSON + /// reader would treat as equal may still canonicalize differently. Changing any rule above changes + /// every hash and requires a new . + /// + /// + public static class BenchmarkCanonicalJson + { + private const int MaxDepth = 256; + + private static readonly UTF8Encoding Utf8WithoutBom = new UTF8Encoding(false, true); + + private static readonly JsonDocumentOptions DocumentOptions = new JsonDocumentOptions + { + AllowTrailingCommas = false, + CommentHandling = JsonCommentHandling.Disallow, + MaxDepth = MaxDepth + }; + + private static readonly JsonWriterOptions WriterOptions = new JsonWriterOptions + { + Encoder = JavaScriptEncoder.Default, + Indented = false, + SkipValidation = false + }; + + /// + /// Canonicalizes a UTF-8 JSON value into canonical JSON v1 bytes. + /// + /// A single valid JSON value encoded as UTF-8 without a byte order mark. + /// Canonical UTF-8 bytes with no byte order mark and no trailing newline. + /// The input violates the canonical JSON v1 rules. + public static byte[] Canonicalize(ReadOnlySpan utf8Json) + { + // JsonDocument.Parse references the buffer it is given, so canonicalization owns a copy. + using (JsonDocument document = JsonDocument.Parse(utf8Json.ToArray(), DocumentOptions)) + using (var stream = new MemoryStream()) + { + using (var writer = new Utf8JsonWriter(stream, WriterOptions)) + { + CanonicalJsonWriter.Write(document.RootElement, writer); + } + + return stream.ToArray(); + } + } + + /// + /// Canonicalizes a JSON value into canonical JSON v1 text. + /// + /// A single valid JSON value. + /// Canonical JSON text; the canonical form is always ASCII. + /// is null. + /// The input violates the canonical JSON v1 rules. + public static string Canonicalize(string json) + { + if (json == null) + { + throw new ArgumentNullException(nameof(json)); + } + + return Utf8WithoutBom.GetString(Canonicalize(Utf8WithoutBom.GetBytes(json).AsSpan())); + } + + /// + /// Computes the SHA-256 of the canonical JSON v1 bytes of a UTF-8 JSON value. + /// + /// A single valid JSON value encoded as UTF-8 without a byte order mark. + /// sha256: followed by 64 lowercase hexadecimal characters. + /// The input violates the canonical JSON v1 rules. + public static string ComputeSha256(ReadOnlySpan utf8Json) + { + return BenchmarkHash.ComputeSha256(Canonicalize(utf8Json)); + } + + /// + /// Computes the SHA-256 of the canonical JSON v1 bytes of a JSON value. + /// + /// A single valid JSON value. + /// sha256: followed by 64 lowercase hexadecimal characters. + /// is null. + /// The input violates the canonical JSON v1 rules. + public static string ComputeSha256(string json) + { + if (json == null) + { + throw new ArgumentNullException(nameof(json)); + } + + return ComputeSha256(Utf8WithoutBom.GetBytes(json).AsSpan()); + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkCanonicalization.cs b/SAM.Analytical.Benchmark/BenchmarkCanonicalization.cs new file mode 100644 index 0000000..ee17763 --- /dev/null +++ b/SAM.Analytical.Benchmark/BenchmarkCanonicalization.cs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +namespace SAM.Analytical.Benchmark +{ + /// + /// The versioned SAM benchmark canonicalization contract recorded in + /// provenance.canonicalizationVersion. + /// + /// + /// This is the single authoritative source of the canonicalization version. DTOs, validation, + /// documentation examples, and tests must reference rather than + /// repeating the literal. Any change to the canonical JSON rules implemented by + /// changes the resulting hashes and therefore requires a new + /// canonicalization version; a changed algorithm must never masquerade as a changed model. + /// + public static class BenchmarkCanonicalization + { + /// + /// The canonicalization rules implemented by this library. + /// + public const string CurrentVersion = "1.0.0"; + + /// + /// Classifies a document's canonicalization version against . + /// + /// + /// Version parsing and the major/minor comparison rules are shared with + /// , so is reused as the result + /// type. An equal major version means the canonical hashes were produced by rules this library + /// understands; a differing major version means they were not and must not be treated as + /// comparable evidence. + /// + public static SchemaCompatibility GetCompatibility(string? version) + { + if (!BenchmarkSchema.TryParseVersion(version, out int major, out int minor, out _)) + { + return SchemaCompatibility.Malformed; + } + + BenchmarkSchema.TryParseVersion(CurrentVersion, out int currentMajor, out int currentMinor, out _); + if (major != currentMajor) + { + return SchemaCompatibility.IncompatibleMajor; + } + + return minor == currentMinor + ? SchemaCompatibility.Compatible + : SchemaCompatibility.CompatibleWithMinorWarning; + } + } +} diff --git a/SAM.Analytical.Benchmark/BenchmarkValidator.cs b/SAM.Analytical.Benchmark/BenchmarkValidator.cs index cdcc9e7..b3c0c9e 100644 --- a/SAM.Analytical.Benchmark/BenchmarkValidator.cs +++ b/SAM.Analytical.Benchmark/BenchmarkValidator.cs @@ -69,10 +69,7 @@ private static void ValidateProvenance(BenchmarkProvenance? provenance, ICollect ValidateOptionalHash(provenance.SourceFileHash, "$.provenance.sourceFileHash", issues); ValidateOptionalHash(provenance.CanonicalModelHash, "$.provenance.canonicalModelHash", issues); - if (!BenchmarkSchema.TryParseVersion(provenance.CanonicalizationVersion, out _, out _, out _)) - { - AddError(issues, "provenance.canonicalizationVersion", "$.provenance.canonicalizationVersion", "A semantic canonicalization version is required."); - } + ValidateCanonicalizationVersion(provenance.CanonicalizationVersion, issues); RequireCommit(provenance.SamCommit, "$.provenance.samCommit", issues); RequireCommit(provenance.RunnerCommit, "$.provenance.runnerCommit", issues); @@ -125,6 +122,21 @@ private static void ValidateProvenance(BenchmarkProvenance? provenance, ICollect } + private static void ValidateCanonicalizationVersion(string? version, ICollection issues) + { + // Applies to every document, including failures: a document that reports no canonical hash + // must still declare which rules it would have used. + switch (BenchmarkCanonicalization.GetCompatibility(version)) + { + case SchemaCompatibility.Malformed: + AddError(issues, "provenance.canonicalizationVersion", "$.provenance.canonicalizationVersion", "A semantic canonicalization version is required."); + break; + case SchemaCompatibility.IncompatibleMajor: + AddError(issues, "provenance.canonicalizationVersion.major", "$.provenance.canonicalizationVersion", $"Canonicalization major version must match {BenchmarkCanonicalization.CurrentVersion}."); + break; + } + } + private static void ValidateEngine(BenchmarkEngine? engine, ICollection issues) { if (engine == null) diff --git a/SAM.Analytical.Benchmark/CanonicalJsonWriter.cs b/SAM.Analytical.Benchmark/CanonicalJsonWriter.cs new file mode 100644 index 0000000..0e07bbb --- /dev/null +++ b/SAM.Analytical.Benchmark/CanonicalJsonWriter.cs @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; + +namespace SAM.Analytical.Benchmark +{ + /// + /// Recursive traversal that emits canonical JSON v1 for a parsed . + /// + /// + /// Internal on purpose: the canonicalization contract is exposed through + /// only, so the traversal and writer settings can be corrected + /// under a new canonicalization version without changing the public API. + /// + internal static class CanonicalJsonWriter + { + internal static void Write(JsonElement element, Utf8JsonWriter writer) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + WriteObject(element, writer); + break; + case JsonValueKind.Array: + WriteArray(element, writer); + break; + case JsonValueKind.String: + writer.WriteStringValue(ReadString(element)); + break; + case JsonValueKind.Number: + WriteNumber(element, writer); + break; + case JsonValueKind.True: + writer.WriteBooleanValue(true); + break; + case JsonValueKind.False: + writer.WriteBooleanValue(false); + break; + case JsonValueKind.Null: + writer.WriteNullValue(); + break; + default: + throw new JsonException($"Canonical JSON does not support the '{element.ValueKind}' value kind."); + } + } + + private static void WriteObject(JsonElement element, Utf8JsonWriter writer) + { + // Property names are materialised first so duplicates are rejected before anything is + // written, and so ordering is decided on the decoded names rather than their escaped form. + var properties = new List>(); + var names = new HashSet(StringComparer.Ordinal); + foreach (JsonProperty property in element.EnumerateObject()) + { + string name = ReadName(property); + if (!names.Add(name)) + { + throw new JsonException($"Canonical JSON rejects the duplicate object property name '{name}'."); + } + + properties.Add(new KeyValuePair(name, property.Value)); + } + + writer.WriteStartObject(); + foreach (KeyValuePair property in properties.OrderBy(x => x.Key, StringComparer.Ordinal)) + { + writer.WritePropertyName(property.Key); + Write(property.Value, writer); + } + + writer.WriteEndObject(); + } + + private static void WriteArray(JsonElement element, Utf8JsonWriter writer) + { + writer.WriteStartArray(); + foreach (JsonElement item in element.EnumerateArray()) + { + Write(item, writer); + } + + writer.WriteEndArray(); + } + + private static void WriteNumber(JsonElement element, Utf8JsonWriter writer) + { + // Integral tokens are emitted exactly. Everything else is emitted through the shortest + // round-trippable binary64 form, so 1, 1.0, 1.00 and 1e0 all canonicalize to 1. + if (element.TryGetInt64(out long signed)) + { + writer.WriteNumberValue(signed); + return; + } + + if (element.TryGetUInt64(out ulong unsigned)) + { + writer.WriteNumberValue(unsigned); + return; + } + + if (!element.TryGetDouble(out double value) || double.IsNaN(value) || double.IsInfinity(value)) + { + throw new JsonException($"Canonical JSON rejects the non-finite or unrepresentable number '{element.GetRawText()}'."); + } + + // Negative zero compares equal to zero, so it must not produce a different canonical hash. + writer.WriteNumberValue(value == 0d ? 0d : value); + } + + private static string ReadName(JsonProperty property) + { + try + { + return property.Name; + } + catch (InvalidOperationException exception) + { + throw new JsonException("Canonical JSON rejects an object property name that is not well-formed UTF-8 text.", exception); + } + } + + private static string ReadString(JsonElement element) + { + try + { + return element.GetString()!; + } + catch (InvalidOperationException exception) + { + throw new JsonException("Canonical JSON rejects a string value that is not well-formed UTF-8 text.", exception); + } + } + } +} diff --git a/docs/benchmark/METHODOLOGY.md b/docs/benchmark/METHODOLOGY.md index 06f6878..3b83fe7 100644 --- a/docs/benchmark/METHODOLOGY.md +++ b/docs/benchmark/METHODOLOGY.md @@ -57,6 +57,27 @@ Each producer records: Absolute machine paths are runtime details and must not be committed or used as identities. Files are identified by portable names and content hashes. +### The two model hashes + +`sourceFileHash` is the SHA-256 of the exact original input-file bytes. It proves the producers read the identical file and nothing more; re-saving the same model with different formatting changes it. + +`canonicalModelHash` is the SHA-256 of the canonical representation of the neutral loaded SAM model. It proves the producers simulated the same model once non-semantic ordering and formatting variation has been removed. + +Each producer computes the canonical hash by loading the source JSON into an `AnalyticalModel`, converting that model back to its normal SAM JSON representation through the shared SAM serialization API **before applying any engine-specific modification**, canonicalizing that JSON with the engine-neutral helper in `SAM.Analytical.Benchmark`, and hashing the canonical UTF-8 bytes: + +```text +source bytes + → sourceFileHash + → deserialize AnalyticalModel + → SAM ToJsonObject + → canonical JSON v1 + → canonicalModelHash +``` + +The canonical hash always describes the neutral loaded model, never an engine artefact. Hashing an OpenStudio model, gbXML, a Tas T3D/TBD/TSD file, simulation results, or an engine working directory yields a value that cannot be compared across engines; that is a contract violation, not an approximation. + +Producers must not invent their own canonicalization. Both engines call the same helper, so a canonical-hash difference means a model difference rather than an implementation difference. The rules are versioned as `canonicalizationVersion`, currently `1.0.0`, and are specified exactly in [SCHEMA.md](SCHEMA.md). Changing them requires a new canonicalization version, because a changed algorithm changes every hash it produces; a comparator that sees two different canonicalization versions reports the mismatch instead of treating the hashes as comparable evidence. + ## Result extraction Producers read the existing SAM result model. Whole-model values come from `AnalyticalModelSimulationResult`; space values come from `SpaceSimulationResult` separated by `LoadType.Heating` and `LoadType.Cooling`. Engine-specific `Query.Source()` values are retained as provenance rather than treated as metric values. diff --git a/docs/benchmark/SCHEMA.md b/docs/benchmark/SCHEMA.md index 891045f..1fecca8 100644 --- a/docs/benchmark/SCHEMA.md +++ b/docs/benchmark/SCHEMA.md @@ -41,7 +41,7 @@ Unknown fields do not change the meaning of known fields. A writer must not emit Hashes use `sha256:` followed by 64 lowercase hexadecimal characters. `sourceFileHash` proves byte equality of the input file. `canonicalModelHash` proves equality under the named canonicalization rules; it must be calculated independently of the source-file bytes. One hash must never be copied into the other merely because the current serialization happens to match. -For a successful run, both model hashes and the weather hash are required. A failure document may use `null` only for a hash that could not be computed, and must explain the reason in `warnings` or `notes`. Canonicalization rules must remove non-semantic ordering and formatting variation without removing model content that can affect simulation. +For a successful run, both model hashes and the weather hash are required. A failure document may use `null` only for a hash that could not be computed, and must explain the reason in `warnings` or `notes`. `canonicalizationVersion` is required on every document, including failures: a document that could not compute a canonical hash must still declare which rules it would have used. Canonicalization rules must remove non-semantic ordering and formatting variation without removing model content that can affect simulation; they are specified in full under [Canonical model hashing](#canonical-model-hashing). Engine fields are: @@ -56,6 +56,72 @@ Weather fields are `identity` and `hash`. `identity` is a portable station/file v1 route tokens are `Native-OpenStudio`, `Native-TAS`, `SharedGbXML-OpenStudio`, and `SharedGbXML-TAS`. `TPD-TAS` is reserved for the later, informational production-HVAC comparison; a v1 primary-gate producer must not emit it. The two shared-gbXML tokens identify the paired documents generated from the same exact interchange file. +## Canonical model hashing + +The two model hashes answer different questions and must never be copied into one another: + +| Hash | Covers | Answers | +|---|---|---| +| `sourceFileHash` | the exact original input-file bytes, unmodified | Did both runs read the identical file? | +| `canonicalModelHash` | the canonical JSON v1 form of the neutral loaded SAM model | Did both runs simulate the same model, ignoring non-semantic formatting? | + +The same model re-saved with different formatting keeps its `canonicalModelHash` and changes its `sourceFileHash`, so a `sourceFileHash` difference alone is not evidence of a different model. + +### Canonical model procedure + +For canonicalization version `1.0.0`, a producer must: + +1. Load the source SAM JSON into a SAM `AnalyticalModel`. +2. Before applying any engine-specific modification, convert that model back to its normal SAM JSON representation using the shared SAM serialization API. +3. Pass that JSON representation to the engine-neutral canonical JSON helper in `SAM.Analytical.Benchmark`. +4. Compute SHA-256 over the resulting canonical UTF-8 bytes. +5. Store the result as `canonicalModelHash`. +6. Record `1.0.0` in `canonicalizationVersion`. + +As a pipeline: + +```text +source bytes + → sourceFileHash + → deserialize AnalyticalModel + → SAM ToJsonObject + → canonical JSON v1 + → canonicalModelHash +``` + +The canonical hash must represent the neutral loaded SAM model **before** any engine-specific translation or mutation. It must never be taken from an OpenStudio model, gbXML, a Tas T3D/TBD/TSD file, simulation results, or an engine working directory: those are engine artefacts and cannot be compared across engines. Round-tripping through `AnalyticalModel` is what makes the hash comparable between producers, because it discards exactly the writer-formatting variation that `sourceFileHash` deliberately keeps. + +Both producers call the same helper. A canonical-hash difference therefore means a model difference, not an implementation difference; a producer must not invent its own canonicalization. + +### Canonical JSON v1 + +The canonical form is produced by `SAM.Analytical.Benchmark.BenchmarkCanonicalJson`. The rules are exactly: + +- Input must be a single valid JSON value, parsed with `System.Text.Json`, encoded as UTF-8 without a byte order mark. +- Object properties are written in ascending ordinal property-name order, comparing decoded names by UTF-16 code unit as .NET `StringComparer.Ordinal` does. +- Array order is preserved exactly. +- Property names and string values are emitted using `System.Text.Json` UTF-8 escaping through `JavaScriptEncoder.Default`. Non-ASCII characters are therefore emitted as `\uXXXX` escapes and the canonical form is always ASCII. +- Boolean and null values retain their normal JSON representations. +- Numeric values are emitted deterministically through the parsed `System.Text.Json` value representation: a token representable as `Int64` or `UInt64` is written exactly, and every other number is written through the shortest round-trippable binary64 form. `1`, `1.0`, `1.00` and `1e0` all canonicalize to `1`, and negative zero canonicalizes to `0`. +- No indentation or insignificant whitespace is emitted. +- Output is UTF-8 without a byte order mark. +- Only LF would be emitted if a line ending were ever emitted; v1 emits one compact JSON value with no trailing newline. +- Duplicate object-property names are rejected, including names that differ only in escape spelling. +- Comments and trailing commas are rejected. +- Non-finite values are rejected, including numeric tokens that overflow binary64. +- Text that is not well-formed UTF-8 is rejected, as is nesting deeper than 256 levels. +- No property is removed or ignored in version 1. +- No engine-specific or machine-specific normalization is allowed. + +This is a versioned SAM benchmark canonicalization contract, not a claim of full semantic JSON equivalence. Numeric precision beyond binary64 is not preserved, so two distinct integers above 2^53 can canonicalize identically. The guarantee is narrower and sufficient for the benchmark: the same neutral SAM model produces byte-identical canonical output on any machine, in any operating-system culture, whatever the property order or formatting of the input. + +### Changing the algorithm + +> **Warning** +> Every rule above is load-bearing: changing any of them changes every hash the algorithm produces. A changed algorithm must never masquerade as a changed model. Any change to canonical JSON v1 requires a new `canonicalizationVersion` published with the code change, and hashes carrying different canonicalization major versions must not be compared. + +`SAM.Analytical.Benchmark.BenchmarkCanonicalization.CurrentVersion` is the single authoritative source of the version string. DTOs, validation, and tests read it rather than repeating the literal. + ## MetricValue Every metric has the same shape: @@ -197,6 +263,6 @@ Native routes must emit the GUID and name from the common source SAM `Space`. Th Comparator and document major versions must be equal. A major mismatch is rejected before metric comparison. An equal major with a different minor version produces a visible compatibility warning; comparison may continue only when all required v1 fields and understood enum values validate. Patch drift does not warn. Producers must not emit a version newer than the contract they actually implement. -`canonicalizationVersion` is versioned independently. A canonicalization-version mismatch does not prove different models: the comparator reports it and does not directly compare the canonical hashes as equivalent evidence. +`canonicalizationVersion` is versioned independently and follows the same major/minor/patch meanings. Validation requires a well-formed version on every document and rejects a major version other than the implemented `1`; drift within major `1` is accepted. A canonicalization-version mismatch does not prove different models: the comparator reports it and does not directly compare the canonical hashes as equivalent evidence. The comparator-owned `comparison-summary.json` is a separate contract and carries its own `summarySchemaVersion`. From 658beb5651264816a283bdc2cfe86a43e76a6191 Mon Sep 17 00:00:00 2001 From: SAM-BIM-AI Date: Fri, 24 Jul 2026 10:33:06 +0200 Subject: [PATCH 04/11] =?UTF-8?q?B1=20build=20order=20=E2=80=94=20output?= =?UTF-8?q?=20benchmark=20schema=20assembly=20to=20build\=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build: output benchmark schema assembly to build\ Decision D3-build requires the schema project to be in SAM_Validation's solution and to output to SAM_Validation\build\. B1a delivered the first half but not the second: with no OutputPath the assembly landed in bin\Release\netstandard2.0\ and no build\ folder existed. B1b and B2 reference this assembly across repositories by HintPath, following the pattern SAM_OpenStudio already uses for SAM itself (..\..\..\SAM\build\SAM.Core.dll), so the output has to be a flat, unsuffixed folder. Without this the producers cannot resolve the reference at all. Set OutputPath to ..\build\ with AppendTargetFrameworkToOutputPath and AppendRuntimeIdentifierToOutputPath disabled, matching SAM\SAM\SAM.Core\SAM.Core.csproj. build\ is already covered by .gitignore, so the output stays untracked. Verified: Debug and Release both emit build\SAM.Analytical.Benchmark.dll with no framework subfolder; the solution builds clean; all 77 benchmark tests pass; and a net8.0 console exe referencing only the built DLL by HintPath runs without a System.Text.Json package reference, resolving it from the Microsoft.NETCore.App shared framework. Co-Authored-By: Claude Opus 4.8 * docs: correct benchmark producer HintPath depth in plan The B1b/B2 HintPath in IMPLEMENTATION-PLAN.md read ..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll, one level short. The producer projects sit three directories deep in their repos (e.g. SAM_OpenStudio\benchmark\SAM.Analytical.OpenStudio.Benchmark\), the same depth as the existing ..\..\..\SAM\build\SAM.Core.dll references in SAM.Analytical.OpenStudio.csproj. The correct relative path therefore has three ..\ segments. Verified empirically: a net8.0 exe referencing the built DLL at ..\..\..\SAM_Validation\build\ compiles and runs. Corrected all three occurrences (dependency diagram, B1b, B2) and noted the reason inline so the producer sessions do not rediscover it. Co-Authored-By: Claude Opus 4.8 --------- Co-authored-by: Claude Opus 4.8 --- .../SAM.Analytical.Benchmark.csproj | 11 +++++++++++ docs/benchmark/IMPLEMENTATION-PLAN.md | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/SAM.Analytical.Benchmark/SAM.Analytical.Benchmark.csproj b/SAM.Analytical.Benchmark/SAM.Analytical.Benchmark.csproj index 73e518f..e42b951 100644 --- a/SAM.Analytical.Benchmark/SAM.Analytical.Benchmark.csproj +++ b/SAM.Analytical.Benchmark/SAM.Analytical.Benchmark.csproj @@ -7,6 +7,17 @@ disable + + + false + false + ..\build\ + + diff --git a/docs/benchmark/IMPLEMENTATION-PLAN.md b/docs/benchmark/IMPLEMENTATION-PLAN.md index b321071..5d1d5ef 100644 --- a/docs/benchmark/IMPLEMENTATION-PLAN.md +++ b/docs/benchmark/IMPLEMENTATION-PLAN.md @@ -32,7 +32,7 @@ This plan is grounded in direct repository inspection (findings in §2). Three a │(netstd2.0) │ │(Interop.T*) │ │ │ │ │ │ STJ only, ZERO deps. │ └──┬───────────┘ └────┬────────┘ └───────────┘ └────────┬────────┘ │ → SAM_Validation\build\ │ │ ref (HintPath) │ ref (HintPath) │ ProjectRef └──────────┬─────────────┘ - │ ..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll │ (built FIRST) + │ ..\..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll │ (built FIRST) └───────────┬──────┴───────────────────────┬──────────┴───────────┬───────────┘ producer writes DTO producer writes DTO comparator + reports read DTO (SAM_OpenStudio CLI) (SAM_Tas CLI, TAS laptop) (SAM_Validation CLI, any laptop) @@ -205,7 +205,7 @@ If B1a's build-order verification finds the change must touch a repo not listed - `Modify\ToBenchmark.cs`: `BenchmarkDocument ToBenchmark(this AnalyticalModel, OpenStudioBenchmarkContext)` — reads model + space results, maps to DTOs (Guid+Name per space). - `Classes\OpenStudioBenchmarkContext.cs`: engine/weather/design-day/route metadata (`OpenStudioVersion()`, `EnergyPlusVersion()`, epw hash, `RuntimeSeconds`, warning counts). - `Program.cs`: `BenchmarkCli.Run` → load model → `ToOpenStudio(epw, outDir, runOptions{AssignIdealLoads=true})` (or `RunAsync`+timeout) → `ToBenchmark` → write JSON. - - HintPath ref `..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll`. + - HintPath ref `..\..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll` (three `..`: the producer project sits three levels deep, matching the repo's existing `..\..\..\SAM\build\SAM.Core.dll` references). - **CI (`build.yml`):** add a step to clone `SAM_Validation` @ matching branch and `msbuild` **only** `SAM.Analytical.Benchmark.csproj` → `SAM_Validation\build\` before building SAM_OpenStudio. - NUnit tests (`benchmark\` folder): **offline** producer test on a synthetic-SQLite model (reuse `C5`) → committed golden JSON; one `[Category("Simulation")]` live end-to-end on `SingleBox()` that also **captures the raw SQL consumption value to pin the unit**. - **Reused APIs:** `ToOpenStudio/ToOpenStudioAsync`, `OpenStudioSimulationRunner`, `Convert.ToSAM`/`ToSAM_SpaceSimulationResults`, `AddResults`, `Query.Source()`, `OpenStudioVersion/EnergyPlusVersion`, `IntervalHourOfYear`. @@ -225,7 +225,7 @@ If B1a's build-order verification finds the change must touch a repo not listed - `Modify\ToBenchmark.cs`: `BenchmarkDocument ToBenchmark(this AnalyticalModel, TasBenchmarkContext)`. - `Classes\TasBenchmarkContext.cs`: TAS provenance (`Query\TasVersion.cs` reading TAS exe FileVersion from the registry install dir; weather/design-day identity; `Timings` duration; `successful`/exception capture). - `Program.cs`: `BenchmarkCli.Run` → load model → ensure shared gbXML (via `SAM.Analytical.gbXML` export if absent) → `WorkflowSettings{ AddIZAMs=true, Sizing=true, Simulate=true, UnmetHours=true, … }` → `new WorkflowCalculator(settings).Calculate(model)` → **explicit building-results read** for annual energy → `ToBenchmark` → write JSON. Watchdog hard-timeout (no cancellation). - - HintPath ref `..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll`. + - HintPath ref `..\..\..\SAM_Validation\build\SAM.Analytical.Benchmark.dll` (three `..`: the producer project sits three levels deep, matching the repo's existing `..\..\..\SAM\build\SAM.Core.dll` references). - **CI (`build.yml`):** same clone + build-only-schema step as B1b. - **Docs:** `SAM_Tas\benchmark\README.md` — TAS-laptop prerequisites (licensed EDSL install, registry keys), CLI, outputs. - **Reused APIs:** `WorkflowCalculator.Calculate`, `WorkflowSettings`, `Modify.AddResults`, `Query.Sizing`, `Query.UnmetHours`, `Modify.UpdateDesignLoads`, `Convert.ToSAM` (building results), `SAM.Analytical.gbXML` export, `Query.Source()`. From c3eec2cafaf42267a506579a9efd078cb7fcc473 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Sat, 25 Jul 2026 16:32:13 +0200 Subject: [PATCH 05/11] =?UTF-8?q?B3=20=E2=80=94=20benchmark=20comparator?= =?UTF-8?q?=20+=20reports=20(#8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: B3 benchmark comparator + reports (SAM.Analytical.Benchmark.Compare) Add the headless, engine-independent comparator for the TAS<->OpenStudio benchmark programme. It reads two neutral benchmark JSON documents, aligns and diffs them, assigns provisional tolerance bands, and emits deterministic Markdown + CSV + summary-JSON reports plus a gate status. It references ONLY the neutral schema library (no engine assembly), enforced by a CI reference-check. New project SAM.Analytical.Benchmark.Compare (net8.0, exe `benchmark-compare`): - Classes: ComparisonResult, MetricComparison, SpaceComparison, SpaceMatchDiagnostics, ReconciliationResult, ToleranceProfile, enums, SchemaIncompatibleException. - Query: Compare (schema-compat check + model/space diff + reconciliation), AlignSpaces (Guid-first, Name-fallback; duplicates/split diagnosed), Band (combined near-zero-floor + relative limit; either side unavailable or unit-mismatch => N/A, never a failure), CircularHourDiff (year-boundary wrap), GateStatus (worst applicable band), Reconcile (additive model-total vs sum-of-spaces). - Modify: WriteMarkdown/WriteCsv/WriteSummaryJson + WriteReports — deterministic, invariant culture, full finite double precision (no rounding), sorted keys, LF newlines, UTF-8 no BOM. comparison-summary.json stamps summarySchemaVersion=1.0.0. - Program: shared BenchmarkCliHost/BenchmarkArguments/BenchmarkCliPaths; exit codes 0/2/3/4/5, plus exit 1 only under --fail-on-gate. New MSTest project SAM.Analytical.Benchmark.Compare.Tests (44 tests, all portable, fabricated in-memory pairs): exact/warn/fail/beyond bands, near-zero, null/unavailable => N/A, unit-mismatch guard, missing/duplicate/split spaces, circular peak-hour, model-total vs sum reconciliation, schema-version drift, CLI exit codes, and a byte-stable golden of all three reports (regenerate with SAM_BENCHMARK_UPDATE_GOLDEN=1). Both projects added to SAM_Validation.sln. build.yml gains an engine-reference guard and a dotnet-test step for the comparator. .gitattributes pins the golden fixtures to eol=lf (repo uses core.autocrlf=true). Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 4.8 * fix: align comparator with TOLERANCES.md contract (Codex review) Addresses the three P2 findings from the Codex review of PR #8, all grounded in the authoritative docs/benchmark/TOLERANCES.md contract, plus a related contract gap found while fixing them. 1. Banding scale (Band + Reconcile): the near-zero floor is the lower bound of the SCALE (denominator), not an additive allowance. Now scale = max(|tas|, |os|, nearZeroFloor(unit)) and the band is taken from relativeDifference = abs/scale against the strict boundaries (<5% Match, <15% Warn, else Fail). Previously `floor + relative*magnitude` false-passed near-zero disagreements (e.g. 0 W vs 10 W with a 10 W floor reported Match instead of Fail). 2. Persist tolerance values in reports: comparison-summary.json gains a `toleranceProfileDetail` object (warn/fail relative, hour warn/fail, default + per-unit near-zero floors) and comparison.md gains a "Tolerance profile" section, so a changed profile can never hide behind an unchanged name. 3. Reconcile only uniquely-matched spaces: reconciliation now sums only the spaces uniquely matched across both documents; one-sided/ambiguous spaces are excluded and reported (Used/Missing/Excluded) with a `complete` flag that is false whenever anything was left out. Also (contract, not flagged by Codex): peak-hour differences are informational and "cannot produce a numerical Fail" per TOLERANCES.md, so hour-of-year metrics are now excluded from GateStatus. Their Match/Warn/Fail bands are still shown in the reports for information. ComparisonResult now carries the full ToleranceProfile (not just its name). Tests updated: near-zero rewritten to the correct semantics + the TOLERANCES.md near-zero table reproduced; new tests for the peak-hour gate exclusion and reconciliation completeness. Goldens regenerated. 50/50 pass. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 4.8 * fix: enforce provenance + coverage gating, stricter alignment (Codex re-review) Addresses the "not ready to merge" Codex review of PR #8. The comparator now enforces the frozen B0 contracts it previously skipped, so it can no longer issue a reassuring Pass for an invalid comparison pair. Provenance compatibility (P1): Query.Compare now checks the two runs are actually comparable via a new ProvenanceCompatibility result — expected engine kind + route family on each CLI side, both state=Success, compatible canonicalization version, equal canonical model hash, equal weather hash/ identity, equal design-day source, equal source-model GUID, and native<->native / shared<->shared route pairing. Any mismatch makes ProvenanceStatus=Fail and prevents an overall Pass. Reported in both md and summary JSON. Multi-dimensional gate (P1): ComparisonResult now exposes NumericalStatus, CoverageStatus, ProvenanceStatus and ReconciliationStatus plus an overall Gate = worst of the four. Unmatched/ambiguous/duplicate spaces or zero comparable metrics make coverage incomplete (Warn); an incomplete/out-of-band reconciliation is Warn; provenance incompatibility is Fail. Missing data can no longer present as a clean pass. Compare self-validates (contract errors): Query.Compare validates both documents with BenchmarkValidator, so a wrong unit, broken availability invariant, out-of-range hour, or malformed/major-incompatible schema is a contract error (BenchmarkValidationException -> exit 4) rather than a silently-normalised N/A. Removed the bespoke SchemaIncompatibleException. Stricter alignment: a duplicated GUID now excludes every member of that group from matching (no arbitrary first-match); name fallback runs only when at least one side lacks a usable GUID (two different valid GUIDs are never merged by name); one-sided outputs are explicitly sorted for determinism. Peak-hour band capped at Warn (never Fail) and still excluded from the numerical gate, per the contract's "informational" rule. Schema-drift note now warns on a minor drift (including equal-but-newer minors) but not on a patch-only difference. Packaging: benchmark-compare now outputs to the repo build\ folder so CI's `**\build\*` artifact upload includes the executable, matching the producers. Tests updated + new coverage: provenance mismatch (weather / swapped engine), duplicate-GUID exclusion, GUID-conflict-no-name-merge, equal-newer-minor drift, patch-only no-drift, unit-mismatch-is-contract-error, coverage-incomplete gate. Goldens regenerated. 57/57 pass. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 4.8 * fix: reject unknown CLI flags; keep gate/caveat wording accurate (Codex) Addresses the three P2s from Codex's third review of PR #8: - Reject misspelled optional flags: Program validates every supplied option against the comparator's known set (tas, openstudio, out, tolerance-profile, fail-on-gate) and returns a usage error (exit 2) for anything else, so a typo like `--fail-on-gtae` can no longer be silently ignored and disable gate enforcement. - Correct the reconciliation description in comparison.md: it is a separate within-document diagnostic status that ALSO contributes to the overall gate (the overall gate is the worst of numerical/coverage/provenance/reconciliation), instead of claiming it is kept separate. - Keep methodological caveats visible: the model-metrics section now prints the whole-model peak-load semantics caveat (OpenStudio coincident-total vs TAS building-profile max) and the candidate conditioning-pairing limitation even when values fall in Pass, as TOLERANCES.md/METRICS.md require. New CLI test for the misspelled-flag rejection. Goldens regenerated. 58/58 pass. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 4.8 * docs: record first real cross-engine comparison (same model + same weather) Runs the B3 comparator end-to-end over two real engine documents produced from the SAME source model and the SAME weather file — the numeric pairing the B2/D10 checkpoint had to defer (it used a geographically mismatched Boston EPW). Run: HungaryHouse-WeahterDDY.sam (14 spaces, embedded design days) + HUN_CEN_Budapest.Met.Center.128400_TMYx.epw, Tas 9.5.7.0 and EnergyPlus 25.1.0 on the licensed workstation. Both producers exited 0 with state=Success; the comparator wrote all three reports. Verbatim artefacts committed alongside. Comparator validated on real data: 14/14 spaces matched by GUID (coverage Pass); floor area agrees to a 2.3e-7 relative difference between the two engines; reconciliation complete 14/0/0 and Match for both engines; the circular peak-hour rule correctly reported hour 0 vs 7974 as 786 h apart; 66 unavailable metrics all N/A without contributing a failure; output deterministic. Finding 1 — the provenance gate caught a real producer defect. It reported INCOMPATIBLE on designDaySource (TAS EmbeddedModel vs OpenStudio None). That is not a genuine input difference: the OpenStudio producer hardcodes DesignDaySource.None (SAM_OpenStudio .../Program.cs:86) instead of deriving it. The gate did exactly its job. Fix belongs in SAM_OpenStudio, separate PR; nothing to change in B3. Finding 2 — the D10 candidate conditioning pairing is NOT yet numerically equivalent: annual heating 4331.96 vs 1751.18 kWh (59.6%), peak heating 3.934 vs 5.721 kW (31.2%), per-space peaks 29-52%. It must stay labelled a candidate; the design-day defect is a plausible contributor, so re-assess after that fix and against the B4 corpus rather than by tuning tolerances. Also noted: TAS emits peakCoolingHour=0 available for a measured-zero cooling load, and the engines disagree on which spaces are conditioned. .gitattributes pins docs/benchmark/reports/** to eol=lf so recorded artefacts stay byte-exact under core.autocrlf. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 4.8 * fix: count unavailable required metrics in coverage (Codex R5) Codex reviewed 9ff04ca and found that partial metric coverage could still report CoverageStatus=Pass: the check only asked whether the aggregate comparable count was non-zero, so a run where a required metric was N/A on one side — while spaces aligned and reconciliation completed — produced an overall Pass despite missing results. N/A metrics do not affect the numerical status either, so nothing else caught the gap. TOLERANCES.md requires unavailable required metrics to count towards coverage precisely so poor coverage cannot appear complete. Coverage is now Warn when any required metric is unavailable, any space is unmatched/duplicated/ambiguous, or nothing was comparable at all. "Required" means the whole-model metrics plus the metrics of uniquely matched spaces: one-sided and ambiguous spaces are deliberately excluded, because every one of their metrics is N/A by construction and that gap is already reported by the unmatched-space diagnostics — counting them again would double-count it. - ComparisonResult exposes RequiredMetrics/RequiredMetricCount and UnavailableRequiredMetricCount; ComparableMetricCount is now scoped to the same comparable set (same value as before, since one-sided metrics were always N/A). - comparison-summary.json gains a "coverage" object: status, requiredMetricCount, comparableMetricCount, unavailableRequiredMetricCount plus unmatched/ambiguous/ duplicate space counts. Additive; summarySchemaVersion stays 1.0.0 (never shipped). - comparison.md gains a "## Coverage" section spelling out the counts and every reason coverage is incomplete, and two rows in the summary table. Two further findings from the same review round, both doc-backed: - Band applied only HourWarnAbsolute, so a 100 h peak-hour gap was reported identically to a 10 h gap and the configured 24 h fail threshold was dead. TOLERANCES.md requires both absolute hour thresholds. Peak-hour differences stay informational the way they already were — hour-of-year metrics are excluded from NumericalStatus — rather than by suppressing the reporting band. - CheckProvenanceCompatibility compared canonicalModelHash even when the two documents declared different canonicalizationVersions. SCHEMA.md says hashes from different canonicalizers must not be compared as equivalent evidence and that a version mismatch does not prove different models, so the hash check is now skipped in that case; the version mismatch remains the reported finding. Corrected the stale additive-limit wording (floor + relative * max) left on ToleranceProfile, Band and BandTests: the floor bounds the SCALE (max(|tas|,|os|,floor)) and the band comes from the relative difference. Tests: 65 comparator + 77 schema pass. New coverage cases for an unavailable whole-model metric, an unavailable metric in a uniquely matched space, everything available, nothing comparable, and one-sided space metrics not being double-counted; plus the two provenance canonicalization cases and the peak-hour fail band. The test that asserted an unavailable model metric preserved an overall Pass now expects Numerical=Pass, Coverage=Warn, Gate=Warn. Goldens regenerated. docs/benchmark/reports/2026-07-25-cross-engine-smoke reports regenerated from the same committed neutral inputs (no re-simulation, no input byte changed): that comparison now correctly reports Coverage=Warn for its 66 N/A metrics; its overall gate stays Fail on the existing numerical and provenance findings. TOLERANCES.md states the coverage and peak-hour rules explicitly. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * fix: exclude only the whole-model peak loads from the numerical gate (Codex P1) Adopts the remaining Codex P1 narrowly, per Michal's decision. METRICS.md keeps the whole-model peaks informational because OpenStudio reports a coincident total while TAS takes the maximum of the building profile, so a difference there can reflect aggregation semantics rather than engine physics — it should not fail an experiment. Excluded from NumericalStatus: model.peakHeatingLoad and model.peakCoolingLoad only. Still gating: annual heating/cooling, per-space peak and design loads, unmet hours, and all geometry. hourOfYear metrics stay excluded as before. The exclusion is deliberately narrow — the same "differing semantics" argument would otherwise extend to the candidate conditioning pairing (annual energy) and leave nothing to gate on. - New explicit predicate Query.IsInformationalForNumericalGate(metric): hour-of-year by unit, plus the two whole-model peak-load keys held in one set, matched by scope + key. Query.ModelScope replaces the repeated "model" literal in CompareModel, so there are no scattered string checks. Per-space peaks use the distinct keys heating.peakLoad/cooling.peakLoad, so a space NAMED "model" cannot be captured. - NumericalStatus now skips whatever that predicate designates. Informational status is expressed by exclusion, never by hiding evidence. The bands are still calculated, still counted in the report totals, and now marked on the row itself: Format.MetricNote appends "informational (excluded from the numerical status)" to any metric that carries a real band but cannot move the gate (Markdown + CSV note column), and comparison-summary.json gains an explicit per-metric "informational" boolean so a downstream consumer never has to infer why a Fail band did not gate. The Markdown intro and the model-metrics caveat now name the two excluded metrics and state that everything else gates normally. Docs reconciled: TOLERANCES.md gate aggregation gains an "Informational metrics" section (the two designated families, why the designation is narrow, and that promotion into the gate needs the B4 corpus plus energy-modeller review); METRICS.md's whole-model peak caveat now says concretely which two keys are excluded and that per-space peaks are not. Tests: 86 comparator + 77 schema pass. New cases prove a failing whole-model peak load alone leaves NumericalStatus=Pass while its band stays Fail and is still counted; a failing annual-energy metric still fails; a failing per-space peak load still fails; unmet hours and geometry still fail; and a mixed case takes the worst NON-informational band (informational Fails present, gating Warn -> Warn). A 14-row data test pins exactly which scope/key/unit combinations are informational. Two existing tests that used a whole-model peak load as their failing/warning metric now use annual energy, which is what they were actually asserting about. Goldens and the recorded cross-engine reports regenerated from the same committed neutral inputs — no re-simulation, no input byte changed. Worth recording: narrowing the exclusion did NOT rescue that comparison. Its numerical status is still Fail on the gating metrics alone (annual heating 59.6%, per-space peaks and unmet hours), with the peak loads now visibly marked informational. The B4 corpus and energy-modeller review still own any promotion of these metrics into the gate. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * fix: keep the CI SAMVersion when running the comparator tests (Codex) Codex reviewed 4eb04d2 and found that the `dotnet test` step this PR added silently un-stamps the uploaded assemblies. Confirmed by reproduction, not just by reading: dotnet test ... -c Release /p:SAMVersion=2026.3.9999.0 -> build\benchmark-compare.dll = 2026.3.9999.0 build\SAM.Analytical.Benchmark.dll = 2026.3.9999.0 dotnet test ... -c Release (as the workflow had it) -> build\benchmark-compare.dll = 1.0.0.0 build\SAM.Analytical.Benchmark.dll = 1.0.0.0 The test project references the schema and comparator projects, and both output to the shared flat `build\` folder, so `dotnet test` rebuilds them straight into the folder the msbuild step had just stamped. Without the property, Directory.Build.props falls back to its local-dev default 1.0.0.0 (line 5) and the correctly versioned assemblies are overwritten immediately before `Upload build folders` uploads `**\build\*` — so every CI artefact from this workflow would have carried 1.0.0.0. Fixed by passing the computed `/p:SAMVersion` to the test step as well, with a comment explaining why it is not optional. Preferred over `dotnet test --no-build`, which would depend on the msbuild and dotnet builds resolving identical output paths. Comparator tests still 86 pass under both invocations. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * fix: carry the tolerance profile and its values in comparison.csv (Codex) TOLERANCES.md:15 requires every report to name the tolerance profile AND include its actual values, so a changed profile cannot hide behind an unchanged name. The Markdown and summary JSON did; the CSV carried bands with no way to interpret or reproduce them. Codex flagged it on d355df7, and the CSV is precisely the artefact most likely to be archived or imported on its own. Seven columns appended (existing column positions unchanged): toleranceProfile, warnRelative, failRelative, hourWarnAbsolute, hourFailAbsolute, nearZeroFloor and informational. Repeated per row rather than written as a preamble, which would break strict RFC-4180 parsing. nearZeroFloor is the floor for THAT row's unit, so every row is independently reproducible: scale = max(|tas|, |openStudio|, nearZeroFloor), then band from warnRelative/failRelative — and the informational column says whether the row's band fed the numerical status, matching the marker already in the note column. New test asserts the header tail, the profile values on a kWh row (floor 1) and a W row (floor 10), and that no row omits the profile. 87 comparator + 77 schema pass. Goldens and the recorded cross-engine CSV regenerated from the same committed inputs; no re-simulation. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 --------- Co-authored-by: Claude Opus 4.8 --- .gitattributes | 8 + .github/workflows/build.yml | 45 + .../AlignSpacesTests.cs | 133 + .../BandTests.cs | 195 + .../Builders.cs | 205 + .../CliTests.cs | 149 + .../CompareTests.cs | 482 +++ .../Fixtures/benchmark-OpenStudio.json | 251 ++ .../Fixtures/benchmark-TAS.json | 251 ++ .../Fixtures/comparison-summary.json | 1342 ++++++ .../Fixtures/comparison.csv | 49 + .../Fixtures/comparison.md | 195 + .../MSTestSettings.cs | 6 + .../ReportTests.cs | 209 + ....Analytical.Benchmark.Compare.Tests.csproj | 36 + .../ScenarioDocuments.cs | 63 + .../Classes/ComparisonResult.cs | 124 + .../Classes/Enums.cs | 85 + .../Classes/MetricComparison.cs | 111 + .../Classes/ProvenanceCompatibility.cs | 46 + .../Classes/ReconciliationResult.cs | 87 + .../Classes/SpaceComparison.cs | 44 + .../Classes/SpaceMatchDiagnostics.cs | 73 + .../Classes/ToleranceProfile.cs | 167 + .../Modify/Format.cs | 130 + .../Modify/WriteCsv.cs | 126 + .../Modify/WriteMarkdown.cs | 445 ++ .../Modify/WriteReports.cs | 71 + .../Modify/WriteSummaryJson.cs | 341 ++ SAM.Analytical.Benchmark.Compare/Program.cs | 165 + .../Query/AlignSpaces.cs | 261 ++ .../Query/Band.cs | 154 + .../Query/CheckProvenance.cs | 170 + .../Query/Compare.cs | 204 + .../Query/Gate.cs | 180 + .../Query/Reconcile.cs | 87 + .../SAM.Analytical.Benchmark.Compare.csproj | 38 + SAM_Validation.sln | 74 +- docs/benchmark/METRICS.md | 2 + docs/benchmark/TOLERANCES.md | 21 +- .../2026-07-25-cross-engine-smoke/README.md | 118 + .../benchmark-OpenStudio.json | 888 ++++ .../benchmark-TAS.json | 890 ++++ .../comparison-summary.json | 3734 +++++++++++++++++ .../comparison.csv | 149 + .../comparison.md | 296 ++ 46 files changed, 12896 insertions(+), 4 deletions(-) create mode 100644 .gitattributes create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/AlignSpacesTests.cs create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/BandTests.cs create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/Builders.cs create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/CliTests.cs create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/CompareTests.cs create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/Fixtures/benchmark-OpenStudio.json create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/Fixtures/benchmark-TAS.json create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison-summary.json create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison.csv create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison.md create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/MSTestSettings.cs create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/ReportTests.cs create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/SAM.Analytical.Benchmark.Compare.Tests.csproj create mode 100644 SAM.Analytical.Benchmark.Compare.Tests/ScenarioDocuments.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Classes/ComparisonResult.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Classes/Enums.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Classes/MetricComparison.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Classes/ProvenanceCompatibility.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Classes/ReconciliationResult.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Classes/SpaceComparison.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Classes/SpaceMatchDiagnostics.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Classes/ToleranceProfile.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Modify/Format.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Modify/WriteCsv.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Modify/WriteMarkdown.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Modify/WriteReports.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Modify/WriteSummaryJson.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Program.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Query/AlignSpaces.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Query/Band.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Query/CheckProvenance.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Query/Compare.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Query/Gate.cs create mode 100644 SAM.Analytical.Benchmark.Compare/Query/Reconcile.cs create mode 100644 SAM.Analytical.Benchmark.Compare/SAM.Analytical.Benchmark.Compare.csproj create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/README.md create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-OpenStudio.json create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-TAS.json create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison-summary.json create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.csv create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2eafc78 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +# Benchmark golden fixtures are asserted byte-for-byte, so they MUST stay LF on every platform. +# This repository uses core.autocrlf=true, which would otherwise rewrite these files to CRLF on +# checkout and break the byte-stability comparisons. Pin the fixture Markdown/CSV/JSON to LF. +SAM.Analytical.Benchmark.Compare.Tests/Fixtures/** text eol=lf +SAM.Analytical.Benchmark.Tests/Fixtures/** text eol=lf + +# Recorded benchmark run artefacts are verbatim tool output (LF, UTF-8 no BOM) and must stay byte-exact. +docs/benchmark/reports/** text eol=lf diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 337c092..4f641d7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,6 +35,36 @@ jobs: - name: Setup MSBuild uses: microsoft/setup-msbuild@v2 + + - name: Ensure the benchmark comparator references no engine assembly + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $proj = "${{ env.REPO_NAME }}/SAM.Analytical.Benchmark.Compare/SAM.Analytical.Benchmark.Compare.csproj" + [xml]$xml = Get-Content $proj + $allowedProjectReference = 'SAM.Analytical.Benchmark.csproj' + $violations = @() + + foreach ($node in $xml.SelectNodes('//ProjectReference')) { + $include = [string]$node.Include + if ($include -notlike "*$allowedProjectReference") { $violations += "ProjectReference '$include'" } + } + # The comparator carries NO binary and NO NuGet of its own; the only + # dependency permitted is the neutral schema project. Anything else (an engine DLL HintPath, the + # OpenStudio/TAS interop packages, ...) would couple the comparator to an engine and must fail here. + foreach ($node in $xml.SelectNodes('//Reference')) { + $violations += "binary Reference '$([string]$node.Include)'" + } + foreach ($node in $xml.SelectNodes('//PackageReference')) { + $violations += "PackageReference '$([string]$node.Include)'" + } + + if ($violations.Count -gt 0) { + Write-Error ("The benchmark comparator must reference ONLY $allowedProjectReference. Offending references: " + ($violations -join '; ')) + exit 1 + } + Write-Host "Comparator references only the neutral schema project." + - name: Compute SAMVersion id: ver shell: pwsh @@ -164,6 +194,21 @@ jobs: & msbuild $sln /t:Rebuild @common } + - name: Test benchmark comparator (dotnet test) + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + # Portable, engine-free MSTest suite for the B3 comparator (depends only on the neutral schema). + # + # SAMVersion MUST be passed here too. This test project references the schema and comparator + # projects, both of which output to the shared flat `build\` folder, so `dotnet test` rebuilds + # them into the very folder the msbuild step just stamped. Without the property, + # Directory.Build.props falls back to its local-dev default 1.0.0.0 and the correctly versioned + # assemblies are silently overwritten immediately before `Upload build folders` runs. + dotnet test "${{ env.REPO_NAME }}/SAM.Analytical.Benchmark.Compare.Tests/SAM.Analytical.Benchmark.Compare.Tests.csproj" ` + -c Release ` + "/p:SAMVersion=${{ steps.ver.outputs.samversion }}" + - name: Upload build folders (optional) uses: actions/upload-artifact@v4 with: diff --git a/SAM.Analytical.Benchmark.Compare.Tests/AlignSpacesTests.cs b/SAM.Analytical.Benchmark.Compare.Tests/AlignSpacesTests.cs new file mode 100644 index 0000000..2eb7776 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/AlignSpacesTests.cs @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Collections.Generic; +using System.Linq; +using SAM.Analytical.Benchmark; +using SAM.Analytical.Benchmark.Compare; + +namespace SAM.Analytical.Benchmark.Compare.Tests +{ + [TestClass] + public sealed class AlignSpacesTests + { + private const string GuidA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + private const string GuidB = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; + private const string GuidC = "cccccccccccccccccccccccccccccccc"; + + [TestMethod] + public void MatchesByGuidEvenWhenNamesDiffer() + { + SpaceAlignment alignment = Query.AlignSpaces( + new[] { Builders.Space(GuidA, "Office", 40, 120) }, + new[] { Builders.Space(GuidA, "Office renamed by OpenStudio", 40, 120) }); + + Assert.AreEqual(1, alignment.Pairs.Count); + Assert.AreEqual(SpaceMatchKind.Guid, alignment.Pairs[0].MatchKind); + Assert.AreEqual(1, alignment.Diagnostics.MatchedByGuid); + Assert.AreEqual(0, alignment.Diagnostics.MatchedByName); + } + + [TestMethod] + public void FallsBackToNormalizedNameWhenNoGuid() + { + SpaceAlignment alignment = Query.AlignSpaces( + new[] { Builders.Space(null, "Office 1", 40, 120) }, + new[] { Builders.Space(null, " office 1 ", 40, 120) }); + + Assert.AreEqual(1, alignment.Pairs.Count); + Assert.AreEqual(SpaceMatchKind.Name, alignment.Pairs[0].MatchKind); + Assert.AreEqual(1, alignment.Diagnostics.MatchedByName); + } + + [TestMethod] + public void ReportsSpacesPresentOnOnlyOneSide() + { + SpaceAlignment alignment = Query.AlignSpaces( + new[] { Builders.Space(GuidA, "Shared", 40, 120), Builders.Space(GuidB, "TasOnly", 10, 30) }, + new[] { Builders.Space(GuidA, "Shared", 40, 120), Builders.Space(GuidC, "OsOnly", 12, 36) }); + + CollectionAssert.AreEqual(new[] { GuidB }, alignment.Diagnostics.OnlyInTas.ToArray()); + CollectionAssert.AreEqual(new[] { GuidC }, alignment.Diagnostics.OnlyInOpenStudio.ToArray()); + Assert.AreEqual(1, alignment.Pairs.Count); + } + + [TestMethod] + public void DuplicateGuidExcludesEveryMemberFromMatching() + { + SpaceAlignment alignment = Query.AlignSpaces( + new[] { Builders.Space(GuidA, "First", 40, 120), Builders.Space(GuidA, "DuplicateGuid", 41, 121) }, + new[] { Builders.Space(GuidA, "Match", 40, 120) }); + + CollectionAssert.AreEqual(new[] { GuidA }, alignment.Diagnostics.DuplicateTasGuids.ToArray()); + // A duplicated GUID is not uniquely identifiable, so NOTHING is matched — not even the OpenStudio + // side that has a single GuidA — rather than arbitrarily aligning the first duplicate. + Assert.AreEqual(0, alignment.Pairs.Count); + Assert.AreEqual(2, alignment.OnlyInTas.Count); + Assert.AreEqual(1, alignment.OnlyInOpenStudio.Count); + } + + [TestMethod] + public void DoesNotNameMatchWhenBothSidesHaveDifferentValidGuids() + { + // Same name but two different valid GUIDs are explicit, conflicting identities: they must not be + // merged by name. + SpaceAlignment alignment = Query.AlignSpaces( + new[] { Builders.Space(GuidB, "Office", 40, 120) }, + new[] { Builders.Space(GuidC, "Office", 40, 120) }); + + Assert.AreEqual(0, alignment.Pairs.Count); + CollectionAssert.Contains(alignment.Diagnostics.AmbiguousNameMatches.ToArray(), "OFFICE"); + Assert.AreEqual(1, alignment.OnlyInTas.Count); + Assert.AreEqual(1, alignment.OnlyInOpenStudio.Count); + } + + [TestMethod] + public void NameMatchesWhenOneSideLacksAGuid() + { + // One side lacks a usable GUID, so name fallback is allowed and adopts the available GUID. + SpaceAlignment alignment = Query.AlignSpaces( + new[] { Builders.Space(null, "Office", 40, 120) }, + new[] { Builders.Space(GuidC, "Office", 40, 120) }); + + Assert.AreEqual(1, alignment.Pairs.Count); + Assert.AreEqual(SpaceMatchKind.Name, alignment.Pairs[0].MatchKind); + Assert.AreEqual(GuidC, alignment.Pairs[0].Guid); + } + + [TestMethod] + public void DiagnosesAmbiguousSplitNameInsteadOfGuessing() + { + SpaceAlignment alignment = Query.AlignSpaces( + new[] { Builders.Space(null, "Room", 40, 120) }, + new[] { Builders.Space(null, "Room", 20, 60), Builders.Space(null, "room", 20, 60) }); + + Assert.AreEqual(0, alignment.Pairs.Count); + CollectionAssert.Contains(alignment.Diagnostics.AmbiguousNameMatches.ToArray(), "ROOM"); + CollectionAssert.Contains(alignment.Diagnostics.DuplicateOpenStudioNames.ToArray(), "ROOM"); + Assert.AreEqual(1, alignment.OnlyInTas.Count); + Assert.AreEqual(2, alignment.OnlyInOpenStudio.Count); + } + + [TestMethod] + public void AlignmentIsDeterministicRegardlessOfInputOrder() + { + List tas = new() + { + Builders.Space(GuidB, "B", 10, 30), + Builders.Space(GuidA, "A", 40, 120) + }; + List openStudio = new() + { + Builders.Space(GuidA, "A", 40, 120), + Builders.Space(GuidB, "B", 10, 30) + }; + + SpaceAlignment alignment = Query.AlignSpaces(tas, openStudio); + + // GUID matches are ordered by ordinal GUID, so A precedes B irrespective of the source order. + Assert.AreEqual(GuidA, alignment.Pairs[0].Guid); + Assert.AreEqual(GuidB, alignment.Pairs[1].Guid); + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/BandTests.cs b/SAM.Analytical.Benchmark.Compare.Tests/BandTests.cs new file mode 100644 index 0000000..b06d9b4 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/BandTests.cs @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using SAM.Analytical.Benchmark; +using SAM.Analytical.Benchmark.Compare; + +namespace SAM.Analytical.Benchmark.Compare.Tests +{ + [TestClass] + public sealed class BandTests + { + private static readonly ToleranceProfile Profile = ToleranceProfile.Default; + + private static MetricComparison Band(MetricValue? tas, MetricValue? openStudio) + { + return Query.Band("model", "metric", tas, openStudio, Profile); + } + + [TestMethod] + public void ExactMatchIsMatchWithZeroDifference() + { + MetricComparison metric = Band(Builders.Value(100, MetricUnit.KilowattHour), Builders.Value(100, MetricUnit.KilowattHour)); + + Assert.AreEqual(ComparisonBand.Match, metric.Band); + Assert.AreEqual(0d, metric.AbsoluteDifference); + Assert.AreEqual(0d, metric.RelativeDifference); + Assert.AreEqual(ComparisonAvailability.Both, metric.Availability); + } + + [TestMethod] + public void WithinWarnBandIsMatch() + { + // 100 -> 104: scale = max(100, 104, floor 1) = 104, so rel = 4/104 = 3.85% < 5% warn => match. + MetricComparison metric = Band(Builders.Value(100, MetricUnit.KilowattHour), Builders.Value(104, MetricUnit.KilowattHour)); + + Assert.AreEqual(ComparisonBand.Match, metric.Band); + Assert.AreEqual(4d, metric.AbsoluteDifference); + Assert.AreEqual(4d, metric.SignedDifference); + } + + [TestMethod] + public void BetweenWarnAndFailIsWarn() + { + // 100 -> 112: scale = 112, so rel = 12/112 = 10.7% => between the 5% warn and 15% fail bands. + MetricComparison metric = Band(Builders.Value(100, MetricUnit.KilowattHour), Builders.Value(112, MetricUnit.KilowattHour)); + + Assert.AreEqual(ComparisonBand.Warn, metric.Band); + } + + [TestMethod] + public void BeyondFailBandIsFail() + { + // 100 -> 140: scale = 140, so rel = 40/140 = 28.6% >= the 15% fail band. + MetricComparison metric = Band(Builders.Value(100, MetricUnit.KilowattHour), Builders.Value(140, MetricUnit.KilowattHour)); + + Assert.AreEqual(ComparisonBand.Fail, metric.Band); + } + + [TestMethod] + public void NearZeroFloorBoundsTheDenominatorWithoutMaskingDisagreement() + { + // The floor is the SCALE lower bound, not an absolute allowance. With the 10 W default floor: + // 0.1 vs 0.3 W is a negligible 0.2 W difference -> 0.2/10 = 2% -> Match (the floor tames what + // would otherwise be a 200% relative difference)... + MetricComparison negligible = Band(Builders.Value(0.1, MetricUnit.Watt), Builders.Value(0.3, MetricUnit.Watt)); + Assert.AreEqual(ComparisonBand.Match, negligible.Band); + + // ...but 0 W vs 8 W is 8/10 = 80% and must NOT be masked into a match by the floor. + MetricComparison disagreement = Band(Builders.Value(0, MetricUnit.Watt), Builders.Value(8, MetricUnit.Watt)); + Assert.AreEqual(ComparisonBand.Fail, disagreement.Band); + } + + [DataTestMethod] + [DataRow(0d, 0d, ComparisonBand.Match)] + [DataRow(0d, 4d, ComparisonBand.Match)] + [DataRow(0d, 10d, ComparisonBand.Warn)] + [DataRow(0d, 20d, ComparisonBand.Fail)] + public void NearZeroRuleMatchesToleranceContractExamples(double tas, double openStudio, ComparisonBand expected) + { + // Reproduces the TOLERANCES.md near-zero table for a hypothetical 100 W floor: the effective + // relative difference is measured against the floor, so 4/10/20 W land in Pass/Warn/Fail. + var floors = new Dictionary { [MetricUnit.Watt] = 100 }; + var profile = new ToleranceProfile("contract-example", 0.05, 0.15, 1, 24, floors, 0); + + MetricComparison metric = Query.Band("model", "metric", Builders.Value(tas, MetricUnit.Watt), Builders.Value(openStudio, MetricUnit.Watt), profile); + + Assert.AreEqual(expected, metric.Band); + } + + [TestMethod] + public void UnavailableOnOneSideIsNotApplicableNeverFail() + { + MetricComparison metric = Band(Builders.Value(100, MetricUnit.KilowattHour), MetricValue.Unavailable(MetricUnit.KilowattHour)); + + Assert.AreEqual(ComparisonBand.NotApplicable, metric.Band); + Assert.AreEqual(NotApplicableReason.Unavailable, metric.NotApplicableReason); + Assert.AreEqual(ComparisonAvailability.TasOnly, metric.Availability); + Assert.IsNull(metric.AbsoluteDifference); + Assert.IsNull(metric.RelativeDifference); + } + + [TestMethod] + public void UnavailableOnBothSidesIsNotApplicable() + { + MetricComparison metric = Band(MetricValue.Unavailable(MetricUnit.Watt), MetricValue.Unavailable(MetricUnit.Watt)); + + Assert.AreEqual(ComparisonBand.NotApplicable, metric.Band); + Assert.AreEqual(NotApplicableReason.Unavailable, metric.NotApplicableReason); + Assert.AreEqual(ComparisonAvailability.Neither, metric.Availability); + } + + [TestMethod] + public void UnitMismatchIsNotApplicableEvenWhenBothAvailable() + { + MetricComparison metric = Band(Builders.Value(100, MetricUnit.KilowattHour), Builders.Value(100, MetricUnit.WattHour)); + + Assert.AreEqual(ComparisonBand.NotApplicable, metric.Band); + Assert.AreEqual(NotApplicableReason.UnitMismatch, metric.NotApplicableReason); + Assert.AreEqual(MetricUnit.KilowattHour, metric.TasUnit); + Assert.AreEqual(MetricUnit.WattHour, metric.OpenStudioUnit); + } + + [TestMethod] + public void CircularPeakHourAcrossYearBoundaryMatches() + { + // Hour 8759 (last hour) vs hour 0 (first hour) are one hour apart, not 8759. + MetricComparison metric = Band(Builders.Value(8759, MetricUnit.HourOfYear), Builders.Value(0, MetricUnit.HourOfYear)); + + Assert.IsTrue(metric.Circular); + Assert.AreEqual(1d, metric.AbsoluteDifference); + Assert.AreEqual(ComparisonBand.Match, metric.Band); + Assert.IsNull(metric.RelativeDifference); + Assert.IsNull(metric.SignedDifference); + } + + [TestMethod] + public void PeakHourWithinFailWindowWarns() + { + // 10h apart: beyond the profile's 1h warn threshold, within its 24h fail threshold. + MetricComparison metric = Band(Builders.Value(100, MetricUnit.HourOfYear), Builders.Value(110, MetricUnit.HourOfYear)); + + Assert.AreEqual(10d, metric.AbsoluteDifference); + Assert.AreEqual(ComparisonBand.Warn, metric.Band); + } + + [TestMethod] + public void PeakHourBeyondFailWindowReportsTheFailBand() + { + // TOLERANCES.md requires absolute warn/fail hour thresholds, so a 100h gap must NOT be reported + // identically to a 10h gap. Peak-hour differences stay informational because hour-of-year metrics + // are excluded from the numerical gate (Query.NumericalStatus), not by suppressing this band. + MetricComparison metric = Band(Builders.Value(100, MetricUnit.HourOfYear), Builders.Value(200, MetricUnit.HourOfYear)); + + Assert.AreEqual(100d, metric.AbsoluteDifference); + Assert.AreEqual(ComparisonBand.Fail, metric.Band); + Assert.AreEqual(GateStatus.Pass, Query.NumericalStatus(new[] { metric })); + } + + [DataTestMethod] + // Informational: every hour-of-year metric, plus the two whole-model peak loads. + [DataRow("model", "peakHeatingLoad", MetricUnit.Kilowatt, true)] + [DataRow("model", "peakCoolingLoad", MetricUnit.Kilowatt, true)] + [DataRow("model", "peakHeatingHour", MetricUnit.HourOfYear, true)] + [DataRow("model", "peakCoolingHour", MetricUnit.HourOfYear, true)] + [DataRow("a-space-guid", "heating.peakHour", MetricUnit.HourOfYear, true)] + // Gating: annual energy, per-space peak/design loads, unmet hours, geometry. + [DataRow("model", "consumptionHeating", MetricUnit.KilowattHour, false)] + [DataRow("model", "consumptionCooling", MetricUnit.KilowattHour, false)] + [DataRow("model", "floorArea", MetricUnit.SquareMetre, false)] + [DataRow("model", "volume", MetricUnit.CubicMetre, false)] + [DataRow("a-space-guid", "heating.peakLoad", MetricUnit.Watt, false)] + [DataRow("a-space-guid", "cooling.peakLoad", MetricUnit.Watt, false)] + [DataRow("a-space-guid", "heating.designLoad", MetricUnit.Watt, false)] + [DataRow("a-space-guid", "cooling.unmetHours", MetricUnit.Hour, false)] + // A space that happens to be NAMED "model" is not captured: its keys are the dotted per-space ones. + [DataRow("model", "heating.peakLoad", MetricUnit.Watt, false)] + public void OnlyDesignatedMetricsAreInformationalForTheNumericalGate(string scope, string key, MetricUnit unit, bool expected) + { + MetricComparison metric = Query.Band(scope, key, Builders.Value(10, unit), Builders.Value(11, unit), Profile); + + Assert.AreEqual(expected, Query.IsInformationalForNumericalGate(metric)); + } + + [DataTestMethod] + [DataRow(8759d, 0d, 1d)] + [DataRow(0d, 8759d, 1d)] + [DataRow(10d, 20d, 10d)] + [DataRow(100d, 8750d, 110d)] + [DataRow(0d, 4380d, 4380d)] + public void CircularHourDiffWrapsAtYearBoundary(double a, double b, double expected) + { + Assert.AreEqual(expected, Query.CircularHourDiff(a, b)); + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/Builders.cs b/SAM.Analytical.Benchmark.Compare.Tests/Builders.cs new file mode 100644 index 0000000..054925d --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/Builders.cs @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Compare.Tests +{ + /// + /// Fabricates in-memory, schema-valid benchmark documents for portable comparator tests. No real + /// files, engines or simulation are involved. + /// + internal static class Builders + { + internal const string HashModel = "sha256:1111111111111111111111111111111111111111111111111111111111111111"; + internal const string HashCanonical = "sha256:2222222222222222222222222222222222222222222222222222222222222222"; + internal const string HashWeather = "sha256:3333333333333333333333333333333333333333333333333333333333333333"; + internal const string SamCommit = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + internal const string RunnerCommit = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; + + internal static MetricValue Value(double value, MetricUnit unit) => MetricValue.AvailableValue(value, unit); + + internal static MetricValue Missing(MetricUnit unit) => MetricValue.Unavailable(unit); + + internal static BenchmarkDocument Document( + EngineKind engine, + BenchmarkRoute route, + BenchmarkModelResult model, + IEnumerable spaces, + string schemaVersion = BenchmarkSchema.CurrentVersion) + { + return new BenchmarkDocument + { + SchemaVersion = schemaVersion, + Provenance = Provenance(engine, route), + Model = model, + Spaces = new List(spaces) + }; + } + + internal static BenchmarkProvenance Provenance(EngineKind engine, BenchmarkRoute route) + { + return new BenchmarkProvenance + { + SourceModelName = "SingleBox", + SourceModelGuid = "0123456789abcdef0123456789abcdef", + SourceFileHash = HashModel, + CanonicalModelHash = HashCanonical, + CanonicalizationVersion = BenchmarkCanonicalization.CurrentVersion, + SamCommit = SamCommit, + RunnerCommit = RunnerCommit, + Engine = new BenchmarkEngine + { + Kind = engine, + Name = engine == EngineKind.OpenStudio ? "EnergyPlus" : "TAS", + Version = engine == EngineKind.OpenStudio ? "24.1.0" : "9.5.3", + SdkVersion = engine == EngineKind.OpenStudio ? "3.10.0" : null + }, + Route = route, + Weather = new BenchmarkWeather { Identity = "London-Gatwick.epw", Hash = HashWeather }, + DesignDaySource = DesignDaySource.Ddy, + RunTimestampUtc = new DateTimeOffset(2026, 7, 21, 12, 0, 0, TimeSpan.Zero), + DurationSeconds = 12.5, + State = RunState.Success, + ResultSources = new List { "AnalyticalModelSimulationResult", "SpaceSimulationResult" }, + Warnings = new List(), + Notes = new List() + }; + } + + internal static BenchmarkModelResult Model( + MetricValue? consumptionHeating = null, + MetricValue? consumptionCooling = null, + MetricValue? peakHeatingLoad = null, + MetricValue? peakHeatingHour = null, + MetricValue? peakCoolingLoad = null, + MetricValue? peakCoolingHour = null, + MetricValue? floorArea = null, + MetricValue? volume = null) + { + return new BenchmarkModelResult + { + ConsumptionHeating = consumptionHeating ?? Value(1000, MetricUnit.KilowattHour), + ConsumptionCooling = consumptionCooling ?? Value(500, MetricUnit.KilowattHour), + PeakHeatingLoad = peakHeatingLoad ?? Value(10, MetricUnit.Kilowatt), + PeakHeatingHour = peakHeatingHour ?? Value(200, MetricUnit.HourOfYear), + PeakCoolingLoad = peakCoolingLoad ?? Value(8, MetricUnit.Kilowatt), + PeakCoolingHour = peakCoolingHour ?? Value(4600, MetricUnit.HourOfYear), + // Defaults line up with a single 200 m2 / 600 m3 space so a one-space document reconciles + // cleanly (model total == sum of spaces) unless a test overrides them. + FloorArea = floorArea ?? Value(200, MetricUnit.SquareMetre), + Volume = volume ?? Value(600, MetricUnit.CubicMetre) + }; + } + + /// + /// A space with EVERY v1 per-space metric available, so a comparison built from it has complete + /// metric coverage. deliberately leaves the design loads and the cooling group + /// unavailable (the realistic OpenStudio-native shape), which now makes coverage incomplete. + /// + internal static BenchmarkSpaceResult CompleteSpace( + string? guid, + string name, + double area, + double volume, + MetricValue? heatingPeakLoad = null, + MetricValue? heatingPeakHour = null) + { + return new BenchmarkSpaceResult + { + Guid = guid, + Name = name, + Area = Value(area, MetricUnit.SquareMetre), + Volume = Value(volume, MetricUnit.CubicMetre), + Heating = new BenchmarkConditionResult + { + DesignLoad = Value(2400, MetricUnit.Watt), + PeakLoad = heatingPeakLoad ?? Value(2000, MetricUnit.Watt), + PeakHour = heatingPeakHour ?? Value(205, MetricUnit.HourOfYear), + UnmetHours = Value(0, MetricUnit.Hour) + }, + Cooling = new BenchmarkConditionResult + { + DesignLoad = Value(1800, MetricUnit.Watt), + PeakLoad = Value(1500, MetricUnit.Watt), + PeakHour = Value(4600, MetricUnit.HourOfYear), + UnmetHours = Value(0, MetricUnit.Hour) + } + }; + } + + /// A space whose every metric is unavailable, for the no-comparable-coverage case. + internal static BenchmarkSpaceResult EmptySpace(string? guid, string name) + { + return new BenchmarkSpaceResult + { + Guid = guid, + Name = name, + Area = Missing(MetricUnit.SquareMetre), + Volume = Missing(MetricUnit.CubicMetre), + Heating = MissingCondition(), + Cooling = MissingCondition() + }; + } + + private static BenchmarkConditionResult MissingCondition() + { + return new BenchmarkConditionResult + { + DesignLoad = Missing(MetricUnit.Watt), + PeakLoad = Missing(MetricUnit.Watt), + PeakHour = Missing(MetricUnit.HourOfYear), + UnmetHours = Missing(MetricUnit.Hour) + }; + } + + /// A model result whose every metric is unavailable, for the no-comparable-coverage case. + internal static BenchmarkModelResult EmptyModel() + { + return new BenchmarkModelResult + { + ConsumptionHeating = Missing(MetricUnit.KilowattHour), + ConsumptionCooling = Missing(MetricUnit.KilowattHour), + PeakHeatingLoad = Missing(MetricUnit.Kilowatt), + PeakHeatingHour = Missing(MetricUnit.HourOfYear), + PeakCoolingLoad = Missing(MetricUnit.Kilowatt), + PeakCoolingHour = Missing(MetricUnit.HourOfYear), + FloorArea = Missing(MetricUnit.SquareMetre), + Volume = Missing(MetricUnit.CubicMetre) + }; + } + + internal static BenchmarkSpaceResult Space( + string? guid, + string name, + double area, + double volume, + MetricValue? heatingPeakLoad = null, + MetricValue? heatingPeakHour = null) + { + return new BenchmarkSpaceResult + { + Guid = guid, + Name = name, + Area = Value(area, MetricUnit.SquareMetre), + Volume = Value(volume, MetricUnit.CubicMetre), + Heating = new BenchmarkConditionResult + { + DesignLoad = Missing(MetricUnit.Watt), + PeakLoad = heatingPeakLoad ?? Value(2000, MetricUnit.Watt), + PeakHour = heatingPeakHour ?? Value(205, MetricUnit.HourOfYear), + UnmetHours = Value(0, MetricUnit.Hour) + }, + Cooling = new BenchmarkConditionResult + { + DesignLoad = Missing(MetricUnit.Watt), + PeakLoad = Missing(MetricUnit.Watt), + PeakHour = Missing(MetricUnit.HourOfYear), + UnmetHours = Missing(MetricUnit.Hour) + } + }; + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/CliTests.cs b/SAM.Analytical.Benchmark.Compare.Tests/CliTests.cs new file mode 100644 index 0000000..123a830 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/CliTests.cs @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.IO; +using SAM.Analytical.Benchmark; +using SAM.Analytical.Benchmark.Compare; + +namespace SAM.Analytical.Benchmark.Compare.Tests +{ + [TestClass] + public sealed class CliTests + { + private const string GuidA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + + [TestMethod] + public void WritesThreeReportsAndReturnsSuccessForAMatchingPair() + { + using var workspace = new Workspace(); + string tas = workspace.WriteDocument("benchmark-TAS.json", Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) })); + string openStudio = workspace.WriteDocument("benchmark-OpenStudio.json", Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) })); + string outDir = Path.Combine(workspace.Root, "report"); + + int exit = Program.Run( + new[] { "--tas", tas, "--openstudio", openStudio, "--out", outDir, "--tolerance-profile", "default" }, + new StringWriter(), + new StringWriter()); + + Assert.AreEqual((int)BenchmarkExitCode.Success, exit); + Assert.IsTrue(File.Exists(Path.Combine(outDir, "comparison.md"))); + Assert.IsTrue(File.Exists(Path.Combine(outDir, "comparison.csv"))); + Assert.IsTrue(File.Exists(Path.Combine(outDir, "comparison-summary.json"))); + } + + [TestMethod] + public void GateFailureWithoutFlagStillSucceedsAndWritesReports() + { + using var workspace = new Workspace(); + string tas = workspace.WriteDocument("benchmark-TAS.json", ScenarioDocuments.Tas()); + string openStudio = workspace.WriteDocument("benchmark-OpenStudio.json", ScenarioDocuments.OpenStudio()); + string outDir = Path.Combine(workspace.Root, "report"); + + int exit = Program.Run(new[] { "--tas", tas, "--openstudio", openStudio, "--out", outDir }, new StringWriter(), new StringWriter()); + + Assert.AreEqual((int)BenchmarkExitCode.Success, exit); + Assert.IsTrue(File.Exists(Path.Combine(outDir, "comparison-summary.json"))); + } + + [TestMethod] + public void FailOnGateReturnsGateExitCodeButStillWritesReports() + { + using var workspace = new Workspace(); + string tas = workspace.WriteDocument("benchmark-TAS.json", ScenarioDocuments.Tas()); + string openStudio = workspace.WriteDocument("benchmark-OpenStudio.json", ScenarioDocuments.OpenStudio()); + string outDir = Path.Combine(workspace.Root, "report"); + + int exit = Program.Run(new[] { "--tas", tas, "--openstudio", openStudio, "--out", outDir, "--fail-on-gate", "fail" }, new StringWriter(), new StringWriter()); + + Assert.AreEqual(Program.GateFailureExitCode, exit); + Assert.IsTrue(File.Exists(Path.Combine(outDir, "comparison-summary.json"))); + } + + [TestMethod] + public void MissingInputFileReturnsInputOutputExitCode() + { + using var workspace = new Workspace(); + string openStudio = workspace.WriteDocument("benchmark-OpenStudio.json", Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) })); + string outDir = Path.Combine(workspace.Root, "report"); + + int exit = Program.Run( + new[] { "--tas", Path.Combine(workspace.Root, "does-not-exist.json"), "--openstudio", openStudio, "--out", outDir }, + new StringWriter(), + new StringWriter()); + + Assert.AreEqual((int)BenchmarkExitCode.InputOutputOrSerializationFailure, exit); + } + + [TestMethod] + public void MisspelledOptionalFlagReturnsUsageExitCodeInsteadOfSilentlyIgnoring() + { + using var workspace = new Workspace(); + string tas = workspace.WriteDocument("benchmark-TAS.json", ScenarioDocuments.Tas()); + string openStudio = workspace.WriteDocument("benchmark-OpenStudio.json", ScenarioDocuments.OpenStudio()); + string outDir = Path.Combine(workspace.Root, "report"); + + // A typo in an optional flag must be rejected, not silently ignored (which would disable the + // caller's intended gate enforcement). + int exit = Program.Run( + new[] { "--tas", tas, "--openstudio", openStudio, "--out", outDir, "--fail-on-gtae", "fail" }, + new StringWriter(), + new StringWriter()); + + Assert.AreEqual((int)BenchmarkExitCode.InvalidUsage, exit); + } + + [TestMethod] + public void MissingRequiredOptionReturnsUsageExitCode() + { + int exit = Program.Run(new[] { "--tas", "a.json" }, new StringWriter(), new StringWriter()); + + Assert.AreEqual((int)BenchmarkExitCode.InvalidUsage, exit); + } + + [TestMethod] + public void UnknownToleranceProfileReturnsUsageExitCode() + { + using var workspace = new Workspace(); + string tas = workspace.WriteDocument("benchmark-TAS.json", Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) })); + string openStudio = workspace.WriteDocument("benchmark-OpenStudio.json", Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) })); + string outDir = Path.Combine(workspace.Root, "report"); + + int exit = Program.Run( + new[] { "--tas", tas, "--openstudio", openStudio, "--out", outDir, "--tolerance-profile", "strict" }, + new StringWriter(), + new StringWriter()); + + Assert.AreEqual((int)BenchmarkExitCode.InvalidUsage, exit); + } + + private sealed class Workspace : IDisposable + { + public Workspace() + { + Root = Path.Combine(Path.GetTempPath(), "benchmark-compare-" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(Root); + } + + public string Root { get; } + + public string WriteDocument(string name, BenchmarkDocument document) + { + string path = Path.Combine(Root, name); + BenchmarkSerializer.Write(path, document); + return path; + } + + public void Dispose() + { + try + { + Directory.Delete(Root, recursive: true); + } + catch (IOException) + { + } + } + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/CompareTests.cs b/SAM.Analytical.Benchmark.Compare.Tests/CompareTests.cs new file mode 100644 index 0000000..871d332 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/CompareTests.cs @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Linq; +using SAM.Analytical.Benchmark; +using SAM.Analytical.Benchmark.Compare; + +namespace SAM.Analytical.Benchmark.Compare.Tests +{ + [TestClass] + public sealed class CompareTests + { + private const string GuidA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + private const string GuidB = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; + + private static readonly ToleranceProfile Profile = ToleranceProfile.Default; + + private static BenchmarkDocument Tas(BenchmarkModelResult model, params BenchmarkSpaceResult[] spaces) + { + return Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, model, spaces); + } + + private static BenchmarkDocument OpenStudio(BenchmarkModelResult model, params BenchmarkSpaceResult[] spaces) + { + return Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, model, spaces); + } + + [TestMethod] + public void IdenticalDocumentsPassTheGate() + { + // Complete spaces: an overall Pass requires complete metric coverage as well as matching numbers. + BenchmarkModelResult model = Builders.Model(); + ComparisonResult result = Query.Compare( + Tas(model, Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + Assert.AreEqual(GateStatus.Pass, result.Gate); + Assert.AreEqual(GateStatus.Pass, result.NumericalStatus); + Assert.AreEqual(GateStatus.Pass, result.CoverageStatus); + Assert.AreEqual(GateStatus.Pass, result.ProvenanceStatus); + Assert.AreEqual(GateStatus.Pass, result.ReconciliationStatus); + Assert.AreEqual(0, result.FailCount); + Assert.AreEqual(0, result.WarnCount); + Assert.IsNull(result.SchemaDriftNote); + } + + [TestMethod] + public void EveryMetricAvailableGivesCompleteCoverage() + { + ComparisonResult result = Query.Compare( + Tas(Builders.Model(), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + // 8 model metrics + 10 metrics on the single matched space, all comparable. + Assert.AreEqual(18, result.RequiredMetricCount); + Assert.AreEqual(18, result.ComparableMetricCount); + Assert.AreEqual(0, result.UnavailableRequiredMetricCount); + Assert.AreEqual(GateStatus.Pass, result.CoverageStatus); + } + + [TestMethod] + public void FailingWholeModelPeakHeatingLoadIsReportedButDoesNotFailTheNumericalStatus() + { + // 10 -> 30 kW is far beyond the fail band, but METRICS.md keeps whole-model peaks informational + // (OpenStudio reports a coincident total, TAS a building-profile maximum), so the band is shown + // and the numerical status ignores it. + ComparisonResult result = Query.Compare( + Tas(Builders.Model(peakHeatingLoad: Builders.Value(10, MetricUnit.Kilowatt)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(peakHeatingLoad: Builders.Value(30, MetricUnit.Kilowatt)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + MetricComparison peakLoad = result.ModelMetrics.Single(metric => metric.Key == "peakHeatingLoad"); + Assert.AreEqual(ComparisonBand.Fail, peakLoad.Band); // still banded and displayed... + Assert.AreEqual(1, result.FailCount); // ...and still counted in the report... + Assert.AreEqual(GateStatus.Pass, result.NumericalStatus); // ...but it cannot fail the experiment. + Assert.AreEqual(GateStatus.Pass, result.Gate); + } + + [TestMethod] + public void FailingWholeModelPeakCoolingLoadIsAlsoInformational() + { + ComparisonResult result = Query.Compare( + Tas(Builders.Model(peakCoolingLoad: Builders.Value(8, MetricUnit.Kilowatt)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(peakCoolingLoad: Builders.Value(24, MetricUnit.Kilowatt)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + Assert.AreEqual(ComparisonBand.Fail, result.ModelMetrics.Single(metric => metric.Key == "peakCoolingLoad").Band); + Assert.AreEqual(GateStatus.Pass, result.NumericalStatus); + } + + [TestMethod] + public void FailingAnnualEnergyStillFailsTheNumericalStatus() + { + // The exclusion is narrow: annual delivered energy is NOT informational and must still gate. + ComparisonResult result = Query.Compare( + Tas(Builders.Model(consumptionHeating: Builders.Value(1000, MetricUnit.KilowattHour)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(consumptionHeating: Builders.Value(400, MetricUnit.KilowattHour)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + Assert.AreEqual(ComparisonBand.Fail, result.ModelMetrics.Single(metric => metric.Key == "consumptionHeating").Band); + Assert.AreEqual(GateStatus.Fail, result.NumericalStatus); + Assert.AreEqual(GateStatus.Fail, result.Gate); + } + + [TestMethod] + public void FailingPerSpacePeakLoadStillFailsTheNumericalStatus() + { + // Only the WHOLE-MODEL peaks are informational; a per-space peak load gates normally. + ComparisonResult result = Query.Compare( + Tas(Builders.Model(), Builders.CompleteSpace(GuidA, "Office", 200, 600, Builders.Value(2000, MetricUnit.Watt))), + OpenStudio(Builders.Model(), Builders.CompleteSpace(GuidA, "Office", 200, 600, Builders.Value(6000, MetricUnit.Watt))), + Profile); + + MetricComparison spacePeak = result.Spaces.Single().Metrics.Single(metric => metric.Key == "heating.peakLoad"); + Assert.AreEqual(ComparisonBand.Fail, spacePeak.Band); + Assert.AreEqual(GateStatus.Fail, result.NumericalStatus); + } + + [TestMethod] + public void GeometryAndUnmetHoursAreNotInformational() + { + BenchmarkSpaceResult openStudioSpace = Builders.CompleteSpace(GuidA, "Office", 200, 600); + openStudioSpace.Cooling!.UnmetHours = Builders.Value(500, MetricUnit.Hour); + + ComparisonResult result = Query.Compare( + Tas(Builders.Model(), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(), openStudioSpace), + Profile); + + Assert.AreEqual(ComparisonBand.Fail, result.Spaces.Single().Metrics.Single(metric => metric.Key == "cooling.unmetHours").Band); + Assert.AreEqual(GateStatus.Fail, result.NumericalStatus); + } + + [TestMethod] + public void NumericalStatusTakesTheWorstNonInformationalBand() + { + // A whole-model peak load fails and a peak hour fails (both informational), while the worst + // GATING metric only warns: 100 -> 112 kWh is 10.7%, inside the 15% fail band. + ComparisonResult result = Query.Compare( + Tas( + Builders.Model( + consumptionCooling: Builders.Value(100, MetricUnit.KilowattHour), + peakHeatingLoad: Builders.Value(10, MetricUnit.Kilowatt), + peakHeatingHour: Builders.Value(100, MetricUnit.HourOfYear)), + Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio( + Builders.Model( + consumptionCooling: Builders.Value(112, MetricUnit.KilowattHour), + peakHeatingLoad: Builders.Value(30, MetricUnit.Kilowatt), + peakHeatingHour: Builders.Value(900, MetricUnit.HourOfYear)), + Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + Assert.AreEqual(ComparisonBand.Fail, result.ModelMetrics.Single(metric => metric.Key == "peakHeatingLoad").Band); + Assert.AreEqual(ComparisonBand.Fail, result.ModelMetrics.Single(metric => metric.Key == "peakHeatingHour").Band); + Assert.AreEqual(ComparisonBand.Warn, result.ModelMetrics.Single(metric => metric.Key == "consumptionCooling").Band); + Assert.AreEqual(2, result.FailCount); + Assert.AreEqual(GateStatus.Warn, result.NumericalStatus); // the worst non-informational band + Assert.AreEqual(GateStatus.Warn, result.Gate); + } + + [TestMethod] + public void OneFailingMetricFailsTheGate() + { + // Uses annual heating, not a whole-model peak load: the peaks are informational and cannot fail + // the gate (see FailingWholeModelPeakHeatingLoadIsReportedButDoesNotFailTheNumericalStatus). + ComparisonResult result = Query.Compare( + Tas(Builders.Model(consumptionHeating: Builders.Value(1000, MetricUnit.KilowattHour)), Builders.Space(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(consumptionHeating: Builders.Value(3000, MetricUnit.KilowattHour)), Builders.Space(GuidA, "Office", 200, 600)), + Profile); + + Assert.AreEqual(GateStatus.Fail, result.Gate); + Assert.IsTrue(result.FailCount >= 1); + } + + [TestMethod] + public void OnlyWarnMetricsGiveWarnGate() + { + // 100 -> 112 kWh: 10.7% => between the 5% warn and 15% fail bands. Annual energy gates (unlike the + // whole-model peaks), and coverage is complete, so the Warn can only come from the numerical status. + ComparisonResult result = Query.Compare( + Tas(Builders.Model(consumptionCooling: Builders.Value(100, MetricUnit.KilowattHour)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(consumptionCooling: Builders.Value(112, MetricUnit.KilowattHour)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + Assert.AreEqual(GateStatus.Warn, result.Gate); + Assert.AreEqual(GateStatus.Warn, result.NumericalStatus); + Assert.AreEqual(GateStatus.Pass, result.CoverageStatus); + Assert.AreEqual(0, result.FailCount); + } + + [TestMethod] + public void UnavailableModelMetricIsNotApplicableButLeavesCoverageIncomplete() + { + BenchmarkModelResult osModel = Builders.Model(consumptionHeating: MetricValue.Unavailable(MetricUnit.KilowattHour)); + + ComparisonResult result = Query.Compare( + Tas(Builders.Model(), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(osModel, Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + // A legitimately-absent metric is N/A and never a numerical failure... + MetricComparison unavailable = result.ModelMetrics.Single(metric => metric.Key == "consumptionHeating"); + Assert.AreEqual(NotApplicableReason.Unavailable, unavailable.NotApplicableReason); + Assert.AreEqual(GateStatus.Pass, result.NumericalStatus); + // ...but TOLERANCES.md requires it to count against coverage, so an incomplete result cannot + // present as a complete Pass. + Assert.AreEqual(1, result.UnavailableRequiredMetricCount); + Assert.AreEqual(17, result.ComparableMetricCount); + Assert.AreEqual(GateStatus.Warn, result.CoverageStatus); + Assert.AreEqual(GateStatus.Warn, result.Gate); + } + + [TestMethod] + public void UnavailableMetricInAMatchedSpaceLeavesCoverageIncomplete() + { + // The space aligns by GUID and every whole-model metric is comparable; only one per-space metric + // is missing on the OpenStudio side (the documented design-load gap on the native route). + BenchmarkSpaceResult openStudioSpace = Builders.CompleteSpace(GuidA, "Office", 200, 600); + openStudioSpace.Heating!.DesignLoad = MetricValue.Unavailable(MetricUnit.Watt); + + ComparisonResult result = Query.Compare( + Tas(Builders.Model(), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(), openStudioSpace), + Profile); + + Assert.AreEqual(GateStatus.Pass, result.NumericalStatus); + Assert.AreEqual(1, result.UnavailableRequiredMetricCount); + Assert.AreEqual(GateStatus.Warn, result.CoverageStatus); + Assert.AreEqual(GateStatus.Warn, result.Gate); + } + + [TestMethod] + public void NoComparableMetricsLeavesCoverageIncomplete() + { + // Both runs produced identities but no measurements at all: nothing is comparable, so the + // numerical status has nothing to fail on and coverage is the only thing preventing a Pass. + ComparisonResult result = Query.Compare( + Tas(Builders.EmptyModel(), Builders.EmptySpace(GuidA, "Office")), + OpenStudio(Builders.EmptyModel(), Builders.EmptySpace(GuidA, "Office")), + Profile); + + Assert.AreEqual(0, result.ComparableMetricCount); + Assert.AreEqual(18, result.UnavailableRequiredMetricCount); + Assert.AreEqual(GateStatus.Pass, result.NumericalStatus); + Assert.AreEqual(GateStatus.Warn, result.CoverageStatus); + Assert.AreNotEqual(GateStatus.Pass, result.Gate); + } + + [TestMethod] + public void OneSidedSpaceMetricsAreNotCountedAsUnavailableRequiredMetrics() + { + // The TAS-only space contributes 10 always-N/A metrics. They must NOT be counted as unavailable + // required metrics — the unmatched space is already reported by the alignment diagnostics, so + // counting its metrics as well would double-count the same gap. Only the genuinely missing + // matched-scope metric (the OpenStudio model's consumptionCooling) is counted. + BenchmarkModelResult osModel = Builders.Model(consumptionCooling: MetricValue.Unavailable(MetricUnit.KilowattHour)); + + ComparisonResult result = Query.Compare( + Tas(Builders.Model(), Builders.CompleteSpace(GuidA, "Shared", 200, 600), Builders.CompleteSpace(GuidB, "TasOnly", 40, 120)), + OpenStudio(osModel, Builders.CompleteSpace(GuidA, "Shared", 200, 600)), + Profile); + + Assert.AreEqual(18, result.RequiredMetricCount); // 8 model + 10 matched space; the TAS-only space is excluded. + Assert.AreEqual(1, result.UnavailableRequiredMetricCount); + Assert.AreEqual(17, result.ComparableMetricCount); + // The one-sided space's metrics are still reported (and still counted as N/A overall). + Assert.AreEqual(11, result.NotApplicableCount); + Assert.AreEqual(GateStatus.Warn, result.CoverageStatus); + } + + [TestMethod] + public void UnitMismatchIsAContractErrorRejectedByCompare() + { + // A metric carrying the wrong unit is a schema-invalid document; Compare validates both inputs + // itself and rejects it as a contract error rather than normalising it to a passing N/A. + BenchmarkModelResult osModel = Builders.Model(consumptionCooling: Builders.Value(500, MetricUnit.WattHour)); + + Assert.ThrowsException(() => Query.Compare( + Tas(Builders.Model(), Builders.Space(GuidA, "Office", 200, 600)), + OpenStudio(osModel, Builders.Space(GuidA, "Office", 200, 600)), + Profile)); + } + + [TestMethod] + public void PeakHourDisagreementIsInformationalAndDoesNotFailTheGate() + { + // Peak hours differ by 100h but everything else matches. The profile's absolute hour thresholds + // (1h warn / 24h fail) put that in the Fail band, and TOLERANCES.md keeps peak-hour differences + // informational by excluding hour-of-year metrics from the numerical gate — not by hiding the band. + ComparisonResult result = Query.Compare( + Tas(Builders.Model(peakHeatingHour: Builders.Value(100, MetricUnit.HourOfYear)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + OpenStudio(Builders.Model(peakHeatingHour: Builders.Value(200, MetricUnit.HourOfYear)), Builders.CompleteSpace(GuidA, "Office", 200, 600)), + Profile); + + MetricComparison peakHour = result.ModelMetrics.Single(metric => metric.Key == "peakHeatingHour"); + Assert.AreEqual(ComparisonBand.Fail, peakHour.Band); // reported honestly... + Assert.AreEqual(GateStatus.Pass, result.NumericalStatus); // ...but excluded from the numerical gate... + Assert.AreEqual(GateStatus.Pass, result.Gate); // ...so the overall gate stays Pass. + } + + [TestMethod] + public void ReconciliationExcludesOneSidedSpacesAndIsMarkedIncomplete() + { + ComparisonResult result = Query.Compare( + Tas(Builders.Model(floorArea: Builders.Value(240, MetricUnit.SquareMetre)), Builders.Space(GuidA, "A", 200, 600), Builders.Space(GuidB, "TasOnly", 40, 120)), + OpenStudio(Builders.Model(floorArea: Builders.Value(240, MetricUnit.SquareMetre)), Builders.Space(GuidA, "A", 200, 600)), + Profile); + + ReconciliationResult tasFloor = result.Reconciliations.Single(r => r.Engine == "TAS" && r.Key == "floorArea"); + // Only the uniquely-matched space A (200) is summed; the TAS-only space B is excluded. + Assert.AreEqual(200d, tasFloor.SumOfSpaces); + Assert.AreEqual(1, tasFloor.ContributingSpaces); + Assert.AreEqual(1, tasFloor.ExcludedSpaces); + Assert.IsFalse(tasFloor.Complete); + } + + [TestMethod] + public void MissingSpaceProducesTasOnlyComparison() + { + ComparisonResult result = Query.Compare( + Tas(Builders.Model(), Builders.Space(GuidA, "Shared", 200, 600), Builders.Space(GuidB, "TasOnly", 40, 120)), + OpenStudio(Builders.Model(), Builders.Space(GuidA, "Shared", 200, 600)), + Profile); + + SpaceComparison tasOnly = result.Spaces.Single(space => space.MatchKind == SpaceMatchKind.TasOnly); + Assert.AreEqual(GuidB, tasOnly.Guid); + // Every metric of a one-sided space is N/A, so the numerical status stays Pass... + Assert.IsTrue(tasOnly.Metrics.All(metric => metric.Band == ComparisonBand.NotApplicable)); + Assert.AreEqual(GateStatus.Pass, result.NumericalStatus); + // ...but the unmatched space leaves coverage incomplete, so the overall gate is not a clean Pass. + Assert.AreEqual(GateStatus.Warn, result.CoverageStatus); + Assert.AreEqual(GateStatus.Warn, result.Gate); + } + + [TestMethod] + public void ModelTotalMatchingSumOfSpacesReconciles() + { + BenchmarkModelResult model = Builders.Model(floorArea: Builders.Value(240, MetricUnit.SquareMetre)); + ComparisonResult result = Query.Compare( + Tas(model, Builders.Space(GuidA, "A", 200, 600), Builders.Space(GuidB, "B", 40, 120)), + OpenStudio(Builders.Model(floorArea: Builders.Value(240, MetricUnit.SquareMetre)), Builders.Space(GuidA, "A", 200, 600), Builders.Space(GuidB, "B", 40, 120)), + Profile); + + ReconciliationResult tasFloor = result.Reconciliations.Single(r => r.Engine == "TAS" && r.Key == "floorArea"); + Assert.AreEqual(240d, tasFloor.SumOfSpaces); + Assert.AreEqual(ComparisonBand.Match, tasFloor.Band); + Assert.AreEqual(2, tasFloor.ContributingSpaces); + } + + [TestMethod] + public void ModelTotalDivergingFromSumOfSpacesIsFlagged() + { + BenchmarkModelResult model = Builders.Model(floorArea: Builders.Value(1000, MetricUnit.SquareMetre)); + ComparisonResult result = Query.Compare( + Tas(model, Builders.Space(GuidA, "A", 200, 600), Builders.Space(GuidB, "B", 40, 120)), + OpenStudio(Builders.Model(), Builders.Space(GuidA, "A", 200, 600), Builders.Space(GuidB, "B", 40, 120)), + Profile); + + ReconciliationResult tasFloor = result.Reconciliations.Single(r => r.Engine == "TAS" && r.Key == "floorArea"); + Assert.AreEqual(1000d, tasFloor.ModelTotal); + Assert.AreEqual(240d, tasFloor.SumOfSpaces); + Assert.AreEqual(ComparisonBand.Fail, tasFloor.Band); + } + + [TestMethod] + public void MinorSchemaDriftIsRecordedNotFatal() + { + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }, schemaVersion: "1.0.0"); + BenchmarkDocument openStudio = Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }, schemaVersion: "1.1.0"); + + ComparisonResult result = Query.Compare(tas, openStudio, Profile); + + Assert.IsNotNull(result.SchemaDriftNote); + } + + [TestMethod] + public void EqualButNewerMinorSchemaStillWarns() + { + // Both documents are 1.1.0 (newer than the comparator's 1.0.0); even though they agree with each + // other, the newer minor must still be surfaced because unknown additive fields may be ignored. + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }, schemaVersion: "1.1.0"); + BenchmarkDocument openStudio = Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }, schemaVersion: "1.1.0"); + + ComparisonResult result = Query.Compare(tas, openStudio, Profile); + + Assert.IsNotNull(result.SchemaDriftNote); + } + + [TestMethod] + public void PatchOnlySchemaDifferenceDoesNotWarn() + { + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }, schemaVersion: "1.0.0"); + BenchmarkDocument openStudio = Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }, schemaVersion: "1.0.1"); + + ComparisonResult result = Query.Compare(tas, openStudio, Profile); + + Assert.IsNull(result.SchemaDriftNote); + } + + [TestMethod] + public void MajorSchemaIncompatibilityIsAContractError() + { + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }, schemaVersion: "2.0.0"); + BenchmarkDocument openStudio = Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }); + + Assert.ThrowsException(() => Query.Compare(tas, openStudio, Profile)); + } + + [TestMethod] + public void MalformedSchemaVersionIsAContractError() + { + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }, schemaVersion: "banana"); + BenchmarkDocument openStudio = Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }); + + Assert.ThrowsException(() => Query.Compare(tas, openStudio, Profile)); + } + + [TestMethod] + public void MismatchedWeatherMakesProvenanceIncompatibleAndPreventsPass() + { + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }); + BenchmarkDocument openStudio = Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }); + // Same model, but the two runs used a different weather file (the D10 Boston-vs-Gatwick case). + openStudio.Provenance!.Weather!.Hash = "sha256:9999999999999999999999999999999999999999999999999999999999999999"; + openStudio.Provenance.Weather.Identity = "Boston.epw"; + + ComparisonResult result = Query.Compare(tas, openStudio, Profile); + + Assert.IsFalse(result.ProvenanceCompatibility.IsCompatible); + Assert.AreEqual(GateStatus.Fail, result.ProvenanceStatus); + Assert.AreEqual(GateStatus.Fail, result.Gate); + Assert.IsTrue(result.ProvenanceCompatibility.Mismatches.Any(m => m.Field == "weatherHash")); + } + + [TestMethod] + public void DifferentCanonicalizationVersionsAreReportedWithoutComparingTheirHashes() + { + // SCHEMA.md: hashes carrying different canonicalization versions must not be compared, and a + // version mismatch does not prove different models. The version mismatch is the finding; the + // (incomparable) canonical hashes must not add a second, misleading "different model" mismatch. + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.CompleteSpace(GuidA, "Office", 200, 600) }); + BenchmarkDocument openStudio = Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.CompleteSpace(GuidA, "Office", 200, 600) }); + openStudio.Provenance!.CanonicalizationVersion = "1.1.0"; + openStudio.Provenance.CanonicalModelHash = "sha256:4444444444444444444444444444444444444444444444444444444444444444"; + + ComparisonResult result = Query.Compare(tas, openStudio, Profile); + + Assert.IsTrue(result.ProvenanceCompatibility.Mismatches.Any(m => m.Field == "canonicalizationVersion")); + Assert.IsFalse(result.ProvenanceCompatibility.Mismatches.Any(m => m.Field == "canonicalModelHash")); + Assert.AreEqual(GateStatus.Fail, result.ProvenanceStatus); + } + + [TestMethod] + public void SameCanonicalizationVersionStillComparesTheCanonicalHashes() + { + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.CompleteSpace(GuidA, "Office", 200, 600) }); + BenchmarkDocument openStudio = Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, Builders.Model(), new[] { Builders.CompleteSpace(GuidA, "Office", 200, 600) }); + openStudio.Provenance!.CanonicalModelHash = "sha256:4444444444444444444444444444444444444444444444444444444444444444"; + + ComparisonResult result = Query.Compare(tas, openStudio, Profile); + + Assert.IsTrue(result.ProvenanceCompatibility.Mismatches.Any(m => m.Field == "canonicalModelHash")); + Assert.AreEqual(GateStatus.Fail, result.ProvenanceStatus); + } + + [TestMethod] + public void TwoDocumentsFromTheSameEngineAreIncompatible() + { + // Swapped inputs: the "openStudio" argument is actually a second TAS document. + BenchmarkDocument tas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }); + BenchmarkDocument alsoTas = Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, Builders.Model(), new[] { Builders.Space(GuidA, "Office", 200, 600) }); + + ComparisonResult result = Query.Compare(tas, alsoTas, Profile); + + Assert.IsFalse(result.ProvenanceCompatibility.IsCompatible); + Assert.AreNotEqual(GateStatus.Pass, result.Gate); + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/benchmark-OpenStudio.json b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/benchmark-OpenStudio.json new file mode 100644 index 0000000..85dbeec --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/benchmark-OpenStudio.json @@ -0,0 +1,251 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "SingleBox", + "sourceModelGuid": "0123456789abcdef0123456789abcdef", + "sourceFileHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "canonicalModelHash": "sha256:2222222222222222222222222222222222222222222222222222222222222222", + "canonicalizationVersion": "1.0.0", + "samCommit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "runnerCommit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "24.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "London-Gatwick.epw", + "hash": "sha256:3333333333333333333333333333333333333333333333333333333333333333" + }, + "designDaySource": "DDY", + "runTimestampUtc": "2026-07-21T12:00:00Z", + "durationSeconds": 12.5, + "state": "Success", + "resultSources": [ + "AnalyticalModelSimulationResult", + "SpaceSimulationResult" + ], + "warnings": [], + "notes": [] + }, + "model": { + "consumptionHeating": { + "value": 1000, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": null, + "unit": "kWh", + "available": false + }, + "peakHeatingLoad": { + "value": 30, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 205, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 8.8, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 240, + "unit": "m2", + "available": true + }, + "volume": { + "value": 720, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "name": "Office A", + "area": { + "value": 200, + "unit": "m2", + "available": true + }, + "volume": { + "value": 600, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 2000, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 205, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "name": "Office B (OS)", + "area": { + "value": 44, + "unit": "m2", + "available": true + }, + "volume": { + "value": 132, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 2600, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 240, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "name": "Atrium", + "area": { + "value": 18, + "unit": "m2", + "available": true + }, + "volume": { + "value": 54, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 2000, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 205, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + } + ] +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/benchmark-TAS.json b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/benchmark-TAS.json new file mode 100644 index 0000000..b2552ea --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/benchmark-TAS.json @@ -0,0 +1,251 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "SingleBox", + "sourceModelGuid": "0123456789abcdef0123456789abcdef", + "sourceFileHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "canonicalModelHash": "sha256:2222222222222222222222222222222222222222222222222222222222222222", + "canonicalizationVersion": "1.0.0", + "samCommit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "runnerCommit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "engine": { + "kind": "TAS", + "name": "TAS", + "version": "9.5.3", + "sdkVersion": null + }, + "route": "Native-TAS", + "weather": { + "identity": "London-Gatwick.epw", + "hash": "sha256:3333333333333333333333333333333333333333333333333333333333333333" + }, + "designDaySource": "DDY", + "runTimestampUtc": "2026-07-21T12:00:00Z", + "durationSeconds": 12.5, + "state": "Success", + "resultSources": [ + "AnalyticalModelSimulationResult", + "SpaceSimulationResult" + ], + "warnings": [], + "notes": [] + }, + "model": { + "consumptionHeating": { + "value": 1000, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": 500, + "unit": "kWh", + "available": true + }, + "peakHeatingLoad": { + "value": 10, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 200, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 8, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 8759, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 240, + "unit": "m2", + "available": true + }, + "volume": { + "value": 720, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "name": "Office A", + "area": { + "value": 200, + "unit": "m2", + "available": true + }, + "volume": { + "value": 600, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 2000, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 205, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "name": "Office B", + "area": { + "value": 40, + "unit": "m2", + "available": true + }, + "volume": { + "value": 120, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 2100, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 205, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "name": "Plant, level 1", + "area": { + "value": 12, + "unit": "m2", + "available": true + }, + "volume": { + "value": 36, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 2000, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 205, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + } + ] +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison-summary.json b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison-summary.json new file mode 100644 index 0000000..3e74511 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison-summary.json @@ -0,0 +1,1342 @@ +{ + "summarySchemaVersion": "1.0.0", + "toleranceProfile": "default", + "toleranceProfileDetail": { + "name": "default", + "warnRelative": 0.05, + "failRelative": 0.15, + "hourWarnAbsolute": 1, + "hourFailAbsolute": 24, + "defaultNearZeroFloor": 0, + "nearZeroFloors": { + "kWh": 1, + "Wh": 1000, + "kW": 0.01, + "W": 10, + "m2": 0.01, + "m3": 0.01, + "h": 0.5 + } + }, + "gate": "Fail", + "statuses": { + "overall": "Fail", + "numerical": "Fail", + "coverage": "Warn", + "provenance": "Pass", + "reconciliation": "Warn" + }, + "provenanceCompatibility": { + "compatible": true, + "mismatches": [] + }, + "counts": { + "match": 10, + "warn": 4, + "fail": 3, + "notApplicable": 31 + }, + "coverage": { + "status": "Warn", + "requiredMetricCount": 28, + "comparableMetricCount": 17, + "unavailableRequiredMetricCount": 11, + "unmatchedSpaceCount": 2, + "ambiguousSpaceCount": 0, + "duplicateSpaceIdentityCount": 0 + }, + "schema": { + "tas": "1.0.0", + "openStudio": "1.0.0", + "driftNote": null + }, + "provenance": { + "tas": { + "sourceModelName": "SingleBox", + "sourceModelGuid": "0123456789abcdef0123456789abcdef", + "sourceFileHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "canonicalModelHash": "sha256:2222222222222222222222222222222222222222222222222222222222222222", + "canonicalizationVersion": "1.0.0", + "samCommit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "runnerCommit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "engine": { + "kind": "TAS", + "name": "TAS", + "version": "9.5.3", + "sdkVersion": null + }, + "route": "Native-TAS", + "weather": { + "identity": "London-Gatwick.epw", + "hash": "sha256:3333333333333333333333333333333333333333333333333333333333333333" + }, + "designDaySource": "DDY", + "runTimestampUtc": "2026-07-21T12:00:00Z", + "durationSeconds": 12.5, + "state": "Success", + "resultSources": [ + "AnalyticalModelSimulationResult", + "SpaceSimulationResult" + ], + "warnings": [], + "notes": [] + }, + "openStudio": { + "sourceModelName": "SingleBox", + "sourceModelGuid": "0123456789abcdef0123456789abcdef", + "sourceFileHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "canonicalModelHash": "sha256:2222222222222222222222222222222222222222222222222222222222222222", + "canonicalizationVersion": "1.0.0", + "samCommit": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "runnerCommit": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "24.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "London-Gatwick.epw", + "hash": "sha256:3333333333333333333333333333333333333333333333333333333333333333" + }, + "designDaySource": "DDY", + "runTimestampUtc": "2026-07-21T12:00:00Z", + "durationSeconds": 12.5, + "state": "Success", + "resultSources": [ + "AnalyticalModelSimulationResult", + "SpaceSimulationResult" + ], + "warnings": [], + "notes": [] + } + }, + "modelMetrics": [ + { + "scope": "model", + "key": "consumptionHeating", + "unit": "kWh", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 1000, + "available": true, + "unit": "kWh" + }, + "openStudio": { + "value": 1000, + "available": true, + "unit": "kWh" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "model", + "key": "consumptionCooling", + "unit": "kWh", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 500, + "available": true, + "unit": "kWh" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "kWh" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "peakHeatingLoad", + "unit": "kW", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 10, + "available": true, + "unit": "kW" + }, + "openStudio": { + "value": 30, + "available": true, + "unit": "kW" + }, + "absoluteDifference": 20, + "relativeDifference": 0.6666666666666666, + "signedDifference": 20 + }, + { + "scope": "model", + "key": "peakHeatingHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Warn", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 200, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 205, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 5, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "peakCoolingLoad", + "unit": "kW", + "availability": "Both", + "band": "Warn", + "informational": true, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 8, + "available": true, + "unit": "kW" + }, + "openStudio": { + "value": 8.8, + "available": true, + "unit": "kW" + }, + "absoluteDifference": 0.8000000000000007, + "relativeDifference": 0.09090909090909098, + "signedDifference": 0.8000000000000007 + }, + { + "scope": "model", + "key": "peakCoolingHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Match", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 8759, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 1, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "floorArea", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 240, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 240, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "model", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 720, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 720, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ], + "spaces": [ + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "guid": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "tasName": "Office A", + "openStudioName": "Office A", + "matchKind": "Guid", + "metrics": [ + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 200, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 200, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 600, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 600, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "heating.designLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 2000, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 2000, + "available": true, + "unit": "W" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Match", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 205, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 205, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 0, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "cooling.designLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "guid": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "tasName": "Office B", + "openStudioName": "Office B (OS)", + "matchKind": "Guid", + "metrics": [ + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Warn", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 40, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 44, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 4, + "relativeDifference": 0.09090909090909091, + "signedDifference": 4 + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Warn", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 120, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 132, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 12, + "relativeDifference": 0.09090909090909091, + "signedDifference": 12 + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "heating.designLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 2100, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 2600, + "available": true, + "unit": "W" + }, + "absoluteDifference": 500, + "relativeDifference": 0.19230769230769232, + "signedDifference": 500 + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 205, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 240, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 35, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "cooling.designLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "guid": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "tasName": "Plant, level 1", + "openStudioName": null, + "matchKind": "TasOnly", + "metrics": [ + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "area", + "unit": "m2", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 12, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "volume", + "unit": "m3", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 36, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "heating.designLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "heating.peakLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 2000, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "TasOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 205, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "heating.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "cooling.designLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "unknown" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "guid": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "tasName": null, + "openStudioName": "Atrium", + "matchKind": "OpenStudioOnly", + "metrics": [ + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "area", + "unit": "m2", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": 18, + "available": true, + "unit": "m2" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "volume", + "unit": "m3", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": 54, + "available": true, + "unit": "m3" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "heating.designLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "heating.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": 2000, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": 205, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "heating.unmetHours", + "unit": "h", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "cooling.designLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "unknown" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + } + ], + "spaceDiagnostics": { + "tasCount": 3, + "openStudioCount": 3, + "matchedByGuid": 2, + "matchedByName": 0, + "onlyInTas": [ + "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c" + ], + "onlyInOpenStudio": [ + "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d" + ], + "duplicateTasGuids": [], + "duplicateOpenStudioGuids": [], + "duplicateTasNames": [], + "duplicateOpenStudioNames": [], + "ambiguousNameMatches": [] + }, + "reconciliation": [ + { + "engine": "TAS", + "key": "floorArea", + "unit": "m2", + "modelTotal": 240, + "modelTotalAvailable": true, + "sumOfSpaces": 240, + "contributingSpaces": 2, + "spacesMissingValue": 0, + "excludedSpaces": 1, + "complete": false, + "absoluteDifference": 0, + "relativeDifference": 0, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "TAS", + "key": "volume", + "unit": "m3", + "modelTotal": 720, + "modelTotalAvailable": true, + "sumOfSpaces": 720, + "contributingSpaces": 2, + "spacesMissingValue": 0, + "excludedSpaces": 1, + "complete": false, + "absoluteDifference": 0, + "relativeDifference": 0, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "OpenStudio", + "key": "floorArea", + "unit": "m2", + "modelTotal": 240, + "modelTotalAvailable": true, + "sumOfSpaces": 244, + "contributingSpaces": 2, + "spacesMissingValue": 0, + "excludedSpaces": 1, + "complete": false, + "absoluteDifference": 4, + "relativeDifference": 0.01639344262295082, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "OpenStudio", + "key": "volume", + "unit": "m3", + "modelTotal": 720, + "modelTotalAvailable": true, + "sumOfSpaces": 732, + "contributingSpaces": 2, + "spacesMissingValue": 0, + "excludedSpaces": 1, + "complete": false, + "absoluteDifference": 12, + "relativeDifference": 0.01639344262295082, + "band": "Match", + "notApplicableReason": null + } + ] +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison.csv b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison.csv new file mode 100644 index 0000000..a71fa85 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison.csv @@ -0,0 +1,49 @@ +scope,metric,unit,availability,tasValue,openStudioValue,absoluteDifference,relativeDifference,relativePercent,signedDifference,band,note,toleranceProfile,warnRelative,failRelative,hourWarnAbsolute,hourFailAbsolute,nearZeroFloor,informational +model,consumptionHeating,kWh,Both,1000,1000,0,0,0,0,Match,,default,0.05,0.15,1,24,1,false +model,consumptionCooling,kWh,TasOnly,500,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,1,false +model,peakHeatingLoad,kW,Both,10,30,20,0.6666666666666666,66.66666666666666,20,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true +model,peakHeatingHour,hourOfYear,Both,200,205,5,,,,Warn,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +model,peakCoolingLoad,kW,Both,8,8.8,0.8000000000000007,0.09090909090909098,9.090909090909099,0.8000000000000007,Warn,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true +model,peakCoolingHour,hourOfYear,Both,8759,0,1,,,,Match,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +model,floorArea,m2,Both,240,240,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +model,volume,m3,Both,720,720,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,area,m2,Both,200,200,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,volume,m3,Both,600,600,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,heating.designLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,heating.peakLoad,W,Both,2000,2000,0,0,0,0,Match,,default,0.05,0.15,1,24,10,false +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,heating.peakHour,hourOfYear,Both,205,205,0,,,,Match,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,cooling.designLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a,cooling.unmetHours,h,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0.5,false +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,area,m2,Both,40,44,4,0.09090909090909091,9.090909090909092,4,Warn,,default,0.05,0.15,1,24,0.01,false +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,volume,m3,Both,120,132,12,0.09090909090909091,9.090909090909092,12,Warn,,default,0.05,0.15,1,24,0.01,false +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,heating.designLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,heating.peakLoad,W,Both,2100,2600,500,0.19230769230769232,19.230769230769234,500,Fail,,default,0.05,0.15,1,24,10,false +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,heating.peakHour,hourOfYear,Both,205,240,35,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,cooling.designLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b,cooling.unmetHours,h,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0.5,false +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,area,m2,TasOnly,12,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.01,false +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,volume,m3,TasOnly,36,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.01,false +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,heating.designLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,heating.peakLoad,W,TasOnly,2000,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,heating.peakHour,hourOfYear,TasOnly,205,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0,true +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,cooling.designLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c,cooling.unmetHours,h,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0.5,false +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,area,m2,OpenStudioOnly,,18,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0.01,false +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,volume,m3,OpenStudioOnly,,54,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0.01,false +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,heating.designLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,heating.peakLoad,W,OpenStudioOnly,,2000,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,heating.peakHour,hourOfYear,OpenStudioOnly,,205,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,heating.unmetHours,h,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0.5,false +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,cooling.designLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d,cooling.unmetHours,h,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0.5,false diff --git a/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison.md b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison.md new file mode 100644 index 0000000..f3a9fa1 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/Fixtures/comparison.md @@ -0,0 +1,195 @@ +# Benchmark comparison: TAS vs OpenStudio + +Independent comparison of two engine-neutral benchmark documents. Tolerance bands are +**provisional reporting buckets**, not validated thresholds. The overall gate is the worst of +the numerical, coverage, provenance and reconciliation statuses. Peak-hour bands and the two +whole-model peak-load bands are informational and are excluded from the numerical status — they are +still calculated and shown, and rows carrying such a band are marked in the Note column; +reconciliation is a separate within-document diagnostic status that also contributes to the +overall gate. + +## Summary + +| Field | Value | +| --- | --- | +| Tolerance profile | default | +| Gate status (overall) | Fail | +| Numerical status | Fail | +| Coverage status | Warn | +| Provenance status | Pass | +| Reconciliation status | Warn | +| Metrics matched | 10 | +| Metrics warned | 4 | +| Metrics failed | 3 | +| Metrics not applicable | 31 | +| Required metrics compared | 17 of 28 | +| Required metrics unavailable | 11 | +| TAS schema version | 1.0.0 | +| OpenStudio schema version | 1.0.0 | + +## Coverage + +How much of the two runs was actually compared. Required metrics are the whole-model metrics plus +the metrics of uniquely matched spaces; the metrics of one-sided, duplicated or ambiguous spaces are +not counted here because those spaces are reported under space alignment instead. An unavailable +metric is never a numerical failure, so it is recorded here — otherwise missing results could read as +a clean pass. + +| Field | Value | +| --- | --- | +| Coverage status | Warn | +| Required metrics | 28 | +| Compared | 17 | +| Unavailable (not compared) | 11 | +| Unmatched spaces | 2 | +| Ambiguous (split) space names | 0 | +| Duplicated space identities | 0 | + +**Coverage is INCOMPLETE**, so this comparison must not be described as a complete result: + +- 11 of 28 required metrics were unavailable on at least one side (reported as N/A, never as a numerical failure). +- 2 space(s) exist on only one side. + +## Provenance compatibility + +Compatible: both runs share the same canonical model, weather and design-day basis, with the expected engine and route on each side. + +## Tolerance profile + +Provisional reporting bands (not validated thresholds). Recorded so a changed profile is never hidden behind an unchanged name. + +| Setting | Value | +| --- | --- | +| Name | default | +| Warn (relative) | 0.05 | +| Fail (relative) | 0.15 | +| Peak-hour warn (h) | 1 | +| Peak-hour fail (h) | 24 | +| Default near-zero floor | 0 | +| Near-zero floor (kWh) | 1 | +| Near-zero floor (Wh) | 1000 | +| Near-zero floor (kW) | 0.01 | +| Near-zero floor (W) | 10 | +| Near-zero floor (m2) | 0.01 | +| Near-zero floor (m3) | 0.01 | +| Near-zero floor (h) | 0.5 | + +## Provenance + +| Field | TAS | OpenStudio | +| --- | --- | --- | +| Engine kind | TAS | OpenStudio | +| Engine name | TAS | EnergyPlus | +| Engine version | 9.5.3 | 24.1.0 | +| SDK version | | 3.10.0 | +| Route | Native-TAS | Native-OpenStudio | +| Weather | London-Gatwick.epw | London-Gatwick.epw | +| Design-day source | DDY | DDY | +| Run state | Success | Success | +| Source model | SingleBox | SingleBox | +| Source model GUID | 0123456789abcdef0123456789abcdef | 0123456789abcdef0123456789abcdef | +| SAM commit | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + +## Model metrics + +> **Caveats (apply even when values pass):** whole-model peak-load semantics differ between +> engines — OpenStudio reports a coincident total, TAS a building-profile maximum — so peak-load +> comparisons are informational, not equivalence claims. `peakHeatingLoad` and `peakCoolingLoad` +> are therefore banded and shown below but **excluded from the numerical status**, exactly like the +> peak-hour metrics; promoting them into the gate needs the B4 corpus and energy-modeller review. +> Every other metric — annual energy, per-space peak loads, unmet hours, geometry — gates normally. +> The primary conditioning pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing) +> is a **candidate** pending validation. A tolerance profile cannot remove these limitations. + +| Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | +| --- | --- | --- | --- | --- | --- | --- | --- | +| consumptionHeating | kWh | 1000 | 1000 | 0 | 0 | Match | | +| consumptionCooling | kWh | 500 | | | | N/A | unavailable (TasOnly) | +| peakHeatingLoad | kW | 10 | 30 | 20 | 66.66666666666666 | Fail | informational (excluded from the numerical status) | +| peakHeatingHour | hourOfYear | 200 | 205 | 5 | | Warn | circular hour difference; informational (excluded from the numerical status) | +| peakCoolingLoad | kW | 8 | 8.8 | 0.8000000000000007 | 9.090909090909099 | Warn | informational (excluded from the numerical status) | +| peakCoolingHour | hourOfYear | 8759 | 0 | 1 | | Match | circular hour difference; informational (excluded from the numerical status) | +| floorArea | m2 | 240 | 240 | 0 | 0 | Match | | +| volume | m3 | 720 | 720 | 0 | 0 | Match | | + +## Model-total vs sum-of-spaces reconciliation + +Additive quantities only; non-additive peak loads are not reconciled. Only uniquely-matched spaces are summed; unmatched or ambiguous spaces make a reconciliation incomplete. + +| Engine | Quantity | Unit | Model total | Sum of spaces | Abs diff | Band | Used/Missing/Excluded | Complete | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TAS | floorArea | m2 | 240 | 240 | 0 | Match | 2/0/1 | no | +| TAS | volume | m3 | 720 | 720 | 0 | Match | 2/0/1 | no | +| OpenStudio | floorArea | m2 | 240 | 244 | 4 | Match | 2/0/1 | no | +| OpenStudio | volume | m3 | 720 | 732 | 12 | Match | 2/0/1 | no | + +## Space alignment + +| Field | Value | +| --- | --- | +| TAS spaces | 3 | +| OpenStudio spaces | 3 | +| Matched by GUID | 2 | +| Matched by name | 0 | + +**Only in TAS:** 1 +- 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + +**Only in OpenStudio:** 1 +- 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d + +**Duplicate TAS GUIDs:** none + +**Duplicate OpenStudio GUIDs:** none + +**Duplicate TAS names:** none + +**Duplicate OpenStudio names:** none + +**Ambiguous (split) name matches:** none + +## Space metrics + +| Space | Match | Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | area | m2 | 200 | 200 | 0 | 0 | Match | | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | volume | m3 | 600 | 600 | 0 | 0 | Match | | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | heating.designLoad | W | | | | | N/A | unavailable (Neither) | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | heating.peakLoad | W | 2000 | 2000 | 0 | 0 | Match | | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | heating.peakHour | hourOfYear | 205 | 205 | 0 | | Match | circular hour difference; informational (excluded from the numerical status) | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | cooling.designLoad | W | | | | | N/A | unavailable (Neither) | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a | Guid | cooling.unmetHours | h | | | | | N/A | unavailable (Neither) | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | area | m2 | 40 | 44 | 4 | 9.090909090909092 | Warn | | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | volume | m3 | 120 | 132 | 12 | 9.090909090909092 | Warn | | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | heating.designLoad | W | | | | | N/A | unavailable (Neither) | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | heating.peakLoad | W | 2100 | 2600 | 500 | 19.230769230769234 | Fail | | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | heating.peakHour | hourOfYear | 205 | 240 | 35 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | cooling.designLoad | W | | | | | N/A | unavailable (Neither) | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b | Guid | cooling.unmetHours | h | | | | | N/A | unavailable (Neither) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | area | m2 | 12 | | | | N/A | unavailable (TasOnly) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | volume | m3 | 36 | | | | N/A | unavailable (TasOnly) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | heating.designLoad | W | | | | | N/A | unavailable (Neither) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | heating.peakLoad | W | 2000 | | | | N/A | unavailable (TasOnly) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | heating.peakHour | hourOfYear | 205 | | | | N/A | unavailable (TasOnly) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | cooling.designLoad | W | | | | | N/A | unavailable (Neither) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c | TasOnly | cooling.unmetHours | h | | | | | N/A | unavailable (Neither) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | area | m2 | | 18 | | | N/A | unavailable (OpenStudioOnly) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | volume | m3 | | 54 | | | N/A | unavailable (OpenStudioOnly) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | heating.designLoad | W | | | | | N/A | unavailable (Neither) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | heating.peakLoad | W | | 2000 | | | N/A | unavailable (OpenStudioOnly) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | heating.peakHour | hourOfYear | | 205 | | | N/A | unavailable (OpenStudioOnly) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | heating.unmetHours | h | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | cooling.designLoad | W | | | | | N/A | unavailable (Neither) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d | OpenStudioOnly | cooling.unmetHours | h | | | | | N/A | unavailable (Neither) | + diff --git a/SAM.Analytical.Benchmark.Compare.Tests/MSTestSettings.cs b/SAM.Analytical.Benchmark.Compare.Tests/MSTestSettings.cs new file mode 100644 index 0000000..df16d1c --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/MSTestSettings.cs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/SAM.Analytical.Benchmark.Compare.Tests/ReportTests.cs b/SAM.Analytical.Benchmark.Compare.Tests/ReportTests.cs new file mode 100644 index 0000000..708dd2c --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/ReportTests.cs @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using SAM.Analytical.Benchmark; +using SAM.Analytical.Benchmark.Compare; + +namespace SAM.Analytical.Benchmark.Compare.Tests +{ + [TestClass] + public sealed class ReportTests + { + private static readonly ToleranceProfile Profile = ToleranceProfile.Default; + + private static ComparisonResult Compare() + { + return Query.Compare(ScenarioDocuments.Tas(), ScenarioDocuments.OpenStudio(), Profile); + } + + [TestMethod] + public void MarkdownIsByteStableAcrossRepeatedRenders() + { + AssertRepeatable(Modify.WriteMarkdown(Compare()), Modify.WriteMarkdown(Compare())); + } + + [TestMethod] + public void CsvIsByteStableAcrossRepeatedRenders() + { + AssertRepeatable(Modify.WriteCsv(Compare()), Modify.WriteCsv(Compare())); + } + + [TestMethod] + public void SummaryIsByteStableAcrossRepeatedRenders() + { + AssertRepeatable(Modify.WriteSummaryJson(Compare()), Modify.WriteSummaryJson(Compare())); + } + + [TestMethod] + public void ReportsUseLfNewlinesAndNoBom() + { + byte[] markdown = Modify.ToUtf8(Modify.WriteMarkdown(Compare())); + byte[] csv = Modify.ToUtf8(Modify.WriteCsv(Compare())); + byte[] summary = Modify.ToUtf8(Modify.WriteSummaryJson(Compare())); + + foreach (byte[] bytes in new[] { markdown, csv, summary }) + { + Assert.IsFalse(bytes.Length >= 3 && bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf, "unexpected BOM"); + Assert.IsFalse(Encoding.UTF8.GetString(bytes).Contains('\r'), "unexpected CR"); + } + } + + [TestMethod] + public void SummarySchemaVersionIsStamped() + { + StringAssert.Contains(Modify.WriteSummaryJson(Compare()), "\"summarySchemaVersion\": \"1.0.0\""); + } + + [TestMethod] + public void CsvCarriesTheToleranceProfileAndItsValuesOnEveryRow() + { + // TOLERANCES.md: a report must name the profile AND carry its actual values. The CSV is often + // archived on its own, so every row must be interpretable and reproducible without the siblings. + ComparisonResult result = Compare(); + string csv = Modify.WriteCsv(result); + string[] lines = csv.TrimEnd('\n').Split('\n'); + + StringAssert.EndsWith(lines[0], "toleranceProfile,warnRelative,failRelative,hourWarnAbsolute,hourFailAbsolute,nearZeroFloor,informational"); + + // A kWh row: the default profile's 5%/15% bands, 1h/24h hour limits and the 1 kWh floor. + string consumption = lines.Single(line => line.StartsWith("model,consumptionHeating,", StringComparison.Ordinal)); + StringAssert.EndsWith(consumption, "default,0.05,0.15,1,24,1,false"); + + // A W row carries the same profile but ITS unit's floor (10 W), and no row may omit the profile. + string spacePeak = lines.First(line => line.Contains(",heating.peakLoad,W,", StringComparison.Ordinal)); + StringAssert.EndsWith(spacePeak, "default,0.05,0.15,1,24,10,false"); + foreach (string line in lines.Skip(1)) + { + StringAssert.Contains(line, ",default,"); + } + } + + [TestMethod] + public void ReportsRecordCoverageDiagnostics() + { + // TOLERANCES.md: unavailable required metrics must be visible in the reports, not just implied by + // the coverage status. The shared scenario has an unavailable model metric and one-sided spaces. + ComparisonResult result = Compare(); + Assert.IsTrue(result.UnavailableRequiredMetricCount > 0); + Assert.AreEqual(GateStatus.Warn, result.CoverageStatus); + + string summary = Modify.WriteSummaryJson(result); + StringAssert.Contains(summary, "\"requiredMetricCount\": " + result.RequiredMetricCount); + StringAssert.Contains(summary, "\"comparableMetricCount\": " + result.ComparableMetricCount); + StringAssert.Contains(summary, "\"unavailableRequiredMetricCount\": " + result.UnavailableRequiredMetricCount); + + string markdown = Modify.WriteMarkdown(result); + StringAssert.Contains(markdown, "| Required metrics unavailable | " + result.UnavailableRequiredMetricCount + " |"); + StringAssert.Contains(markdown, "**Coverage is INCOMPLETE**"); + } + + [TestMethod] + public void ReportsMarkInformationalMetricsThatCarryABand() + { + // The whole-model peak loads and the peak hours are excluded from the numerical status, so every + // report must say so on the row itself — otherwise a Fail band reads as a gate failure. + ComparisonResult result = Compare(); + MetricComparison peakLoad = result.ModelMetrics.Single(metric => metric.Key == "peakHeatingLoad"); + Assert.AreNotEqual(ComparisonBand.NotApplicable, peakLoad.Band); + Assert.IsTrue(Query.IsInformationalForNumericalGate(peakLoad)); + + string markdown = Modify.WriteMarkdown(result); + StringAssert.Contains(markdown, "| peakHeatingLoad | kW | 10 | 30 | 20 | 66.66666666666666 | Fail | informational (excluded from the numerical status) |"); + StringAssert.Contains(markdown, "**excluded from the numerical status**"); + + StringAssert.Contains(Modify.WriteCsv(result), "model,peakHeatingLoad,kW,Both,10,30,20,"); + StringAssert.Contains(Modify.WriteCsv(result), "Fail,informational (excluded from the numerical status),default,"); + + // Annual energy is NOT informational and must not be marked. + MetricComparison consumption = result.ModelMetrics.Single(metric => metric.Key == "consumptionHeating"); + Assert.IsFalse(Query.IsInformationalForNumericalGate(consumption)); + StringAssert.Contains(Modify.WriteSummaryJson(result), "\"key\": \"consumptionHeating\","); + StringAssert.Contains(Modify.WriteSummaryJson(result), "\"informational\": true"); + StringAssert.Contains(Modify.WriteSummaryJson(result), "\"informational\": false"); + } + + [TestMethod] + public void GoldenReportsMatchCommittedFixturesByteForByte() + { + // Regenerate goldens (and the two input fixtures) when explicitly requested; otherwise assert. + if (string.Equals(Environment.GetEnvironmentVariable("SAM_BENCHMARK_UPDATE_GOLDEN"), "1", StringComparison.Ordinal)) + { + RegenerateGoldens(); + } + + BenchmarkDocument tas = BenchmarkSerializer.Read(RuntimeFixture("benchmark-TAS.json")); + BenchmarkDocument openStudio = BenchmarkSerializer.Read(RuntimeFixture("benchmark-OpenStudio.json")); + ComparisonResult result = Query.Compare(tas, openStudio, Profile); + + AssertMatchesFixture("comparison.md", Modify.WriteMarkdown(result)); + AssertMatchesFixture("comparison.csv", Modify.WriteCsv(result)); + AssertMatchesFixture("comparison-summary.json", Modify.WriteSummaryJson(result)); + } + + private static void RegenerateGoldens() + { + BenchmarkDocument tas = ScenarioDocuments.Tas(); + BenchmarkDocument openStudio = ScenarioDocuments.OpenStudio(); + + // Serialize the neutral inputs exactly as a producer would (LF, UTF-8 no BOM, trailing newline). + byte[] tasBytes = Utf8(BenchmarkSerializer.Serialize(tas) + "\n"); + byte[] openStudioBytes = Utf8(BenchmarkSerializer.Serialize(openStudio) + "\n"); + + ComparisonResult result = Query.Compare( + BenchmarkSerializer.Deserialize(BenchmarkSerializer.Serialize(tas)), + BenchmarkSerializer.Deserialize(BenchmarkSerializer.Serialize(openStudio)), + Profile); + + byte[] markdown = Modify.ToUtf8(Modify.WriteMarkdown(result)); + byte[] csv = Modify.ToUtf8(Modify.WriteCsv(result)); + byte[] summary = Modify.ToUtf8(Modify.WriteSummaryJson(result)); + + // Write to the committed source fixtures AND the runtime copy, so the assertion below passes in + // the same run without a rebuild. + foreach (string dir in new[] { SourceFixturesDirectory(), Path.Combine(AppContext.BaseDirectory, "Fixtures") }) + { + Directory.CreateDirectory(dir); + File.WriteAllBytes(Path.Combine(dir, "benchmark-TAS.json"), tasBytes); + File.WriteAllBytes(Path.Combine(dir, "benchmark-OpenStudio.json"), openStudioBytes); + File.WriteAllBytes(Path.Combine(dir, "comparison.md"), markdown); + File.WriteAllBytes(Path.Combine(dir, "comparison.csv"), csv); + File.WriteAllBytes(Path.Combine(dir, "comparison-summary.json"), summary); + } + } + + private static void AssertMatchesFixture(string name, string actual) + { + byte[] expected = File.ReadAllBytes(RuntimeFixture(name)); + byte[] actualBytes = Modify.ToUtf8(actual); + CollectionAssert.AreEqual( + expected, + actualBytes, + $"Golden report '{name}' does not match. Re-run with SAM_BENCHMARK_UPDATE_GOLDEN=1 if the change is intended."); + } + + private static void AssertRepeatable(string first, string second) + { + CollectionAssert.AreEqual(Modify.ToUtf8(first), Modify.ToUtf8(second)); + } + + private static string RuntimeFixture(string name) + { + return Path.Combine(AppContext.BaseDirectory, "Fixtures", name); + } + + private static string SourceFixturesDirectory([CallerFilePath] string? sourceFilePath = null) + { + return Path.Combine(Path.GetDirectoryName(sourceFilePath!)!, "Fixtures"); + } + + private static byte[] Utf8(string text) + { + return new UTF8Encoding(false).GetBytes(text); + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare.Tests/SAM.Analytical.Benchmark.Compare.Tests.csproj b/SAM.Analytical.Benchmark.Compare.Tests/SAM.Analytical.Benchmark.Compare.Tests.csproj new file mode 100644 index 0000000..634d8cc --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/SAM.Analytical.Benchmark.Compare.Tests.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + latest + enable + enable + true + Exe + true + true + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + + + diff --git a/SAM.Analytical.Benchmark.Compare.Tests/ScenarioDocuments.cs b/SAM.Analytical.Benchmark.Compare.Tests/ScenarioDocuments.cs new file mode 100644 index 0000000..aa89781 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare.Tests/ScenarioDocuments.cs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Collections.Generic; +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Compare.Tests +{ + /// + /// A single fixed, feature-rich pair of documents shared by the byte-stability and golden report + /// tests: GUID-matched spaces spanning match/warn/fail bands, a peak-hour that wraps the year boundary, + /// an unavailable model metric, and spaces present on only one side. Both documents use native routes + /// so they are fully schema-valid and round-trip through the serializer (native routes require GUIDs, + /// so name-fallback matching is exercised separately in AlignSpacesTests). + /// + internal static class ScenarioDocuments + { + private const string GuidOfficeA = "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"; + private const string GuidOfficeB = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"; + private const string GuidPlant = "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"; + private const string GuidAtrium = "0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d"; + + internal static BenchmarkDocument Tas() + { + BenchmarkModelResult model = Builders.Model( + consumptionHeating: Builders.Value(1000, MetricUnit.KilowattHour), + consumptionCooling: Builders.Value(500, MetricUnit.KilowattHour), + peakHeatingLoad: Builders.Value(10, MetricUnit.Kilowatt), + peakHeatingHour: Builders.Value(200, MetricUnit.HourOfYear), + peakCoolingLoad: Builders.Value(8, MetricUnit.Kilowatt), + peakCoolingHour: Builders.Value(8759, MetricUnit.HourOfYear), + floorArea: Builders.Value(240, MetricUnit.SquareMetre), + volume: Builders.Value(720, MetricUnit.CubicMetre)); + + return Builders.Document(EngineKind.Tas, BenchmarkRoute.NativeTas, model, new[] + { + Builders.Space(GuidOfficeA, "Office A", 200, 600, Builders.Value(2000, MetricUnit.Watt), Builders.Value(205, MetricUnit.HourOfYear)), + Builders.Space(GuidOfficeB, "Office B", 40, 120, Builders.Value(2100, MetricUnit.Watt), Builders.Value(205, MetricUnit.HourOfYear)), + Builders.Space(GuidPlant, "Plant, level 1", 12, 36) + }); + } + + internal static BenchmarkDocument OpenStudio() + { + BenchmarkModelResult model = Builders.Model( + consumptionHeating: Builders.Value(1000, MetricUnit.KilowattHour), + consumptionCooling: MetricValue.Unavailable(MetricUnit.KilowattHour), + peakHeatingLoad: Builders.Value(30, MetricUnit.Kilowatt), + peakHeatingHour: Builders.Value(205, MetricUnit.HourOfYear), + peakCoolingLoad: Builders.Value(8.8, MetricUnit.Kilowatt), + peakCoolingHour: Builders.Value(0, MetricUnit.HourOfYear), + floorArea: Builders.Value(240, MetricUnit.SquareMetre), + volume: Builders.Value(720, MetricUnit.CubicMetre)); + + return Builders.Document(EngineKind.OpenStudio, BenchmarkRoute.NativeOpenStudio, model, new[] + { + Builders.Space(GuidOfficeA, "Office A", 200, 600, Builders.Value(2000, MetricUnit.Watt), Builders.Value(205, MetricUnit.HourOfYear)), + Builders.Space(GuidOfficeB, "Office B (OS)", 44, 132, Builders.Value(2600, MetricUnit.Watt), Builders.Value(240, MetricUnit.HourOfYear)), + Builders.Space(GuidAtrium, "Atrium", 18, 54) + }); + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Classes/ComparisonResult.cs b/SAM.Analytical.Benchmark.Compare/Classes/ComparisonResult.cs new file mode 100644 index 0000000..aa56934 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Classes/ComparisonResult.cs @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Collections.Generic; +using System.Linq; + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// The full, engine-independent outcome of comparing a TAS benchmark document against an OpenStudio + /// benchmark document: per-metric bands, per-space comparisons, alignment diagnostics, within-engine + /// reconciliation, a schema-drift note and the overall gate status. Everything the report writers + /// need is here; the writers add no wall-clock or machine-specific data, so reports are reproducible. + /// + public sealed class ComparisonResult + { + public ComparisonResult( + ToleranceProfile toleranceProfile, + string? tasSchemaVersion, + string? openStudioSchemaVersion, + string? schemaDriftNote, + ProvenanceCompatibility provenanceCompatibility, + BenchmarkProvenance? tasProvenance, + BenchmarkProvenance? openStudioProvenance, + IReadOnlyList modelMetrics, + IReadOnlyList spaces, + SpaceMatchDiagnostics spaceDiagnostics, + IReadOnlyList reconciliations) + { + ToleranceProfile = toleranceProfile; + TasSchemaVersion = tasSchemaVersion; + OpenStudioSchemaVersion = openStudioSchemaVersion; + SchemaDriftNote = schemaDriftNote; + ProvenanceCompatibility = provenanceCompatibility; + TasProvenance = tasProvenance; + OpenStudioProvenance = openStudioProvenance; + ModelMetrics = modelMetrics; + Spaces = spaces; + SpaceDiagnostics = spaceDiagnostics; + Reconciliations = reconciliations; + } + + public ToleranceProfile ToleranceProfile { get; } + + public string ToleranceProfileName => ToleranceProfile.Name; + + public string? TasSchemaVersion { get; } + + public string? OpenStudioSchemaVersion { get; } + + /// A human-readable note when the two documents declare different schema minor versions, else null. + public string? SchemaDriftNote { get; } + + /// Whether the two runs are actually comparable (same model, weather, design-day, engines/routes). + public ProvenanceCompatibility ProvenanceCompatibility { get; } + + public BenchmarkProvenance? TasProvenance { get; } + + public BenchmarkProvenance? OpenStudioProvenance { get; } + + public IReadOnlyList ModelMetrics { get; } + + public IReadOnlyList Spaces { get; } + + public SpaceMatchDiagnostics SpaceDiagnostics { get; } + + public IReadOnlyList Reconciliations { get; } + + /// Every metric comparison (model then spaces) in report order. + public IEnumerable AllMetrics => ModelMetrics.Concat(Spaces.SelectMany(space => space.Metrics)); + + public int MatchCount => AllMetrics.Count(metric => metric.Band == ComparisonBand.Match); + + public int WarnCount => AllMetrics.Count(metric => metric.Band == ComparisonBand.Warn); + + public int FailCount => AllMetrics.Count(metric => metric.Band == ComparisonBand.Fail); + + public int NotApplicableCount => AllMetrics.Count(metric => metric.Band == ComparisonBand.NotApplicable); + + /// + /// Every metric that this comparison could reasonably have compared: the whole-model metrics plus + /// the metrics of uniquely matched spaces. Metrics of one-sided, duplicated or ambiguous spaces are + /// excluded because they are N/A by construction — their gap is already reported by the space + /// alignment diagnostics, so counting them here would double-count the same missing data. + /// + public IEnumerable RequiredMetrics => ModelMetrics.Concat( + Spaces.Where(space => space.MatchKind == SpaceMatchKind.Guid || space.MatchKind == SpaceMatchKind.Name) + .SelectMany(space => space.Metrics)); + + /// The number of required metrics (whole-model plus uniquely matched spaces). + public int RequiredMetricCount => RequiredMetrics.Count(); + + /// Required metrics that were actually compared (a real band was assigned). + public int ComparableMetricCount => RequiredMetrics.Count(metric => metric.Band != ComparisonBand.NotApplicable); + + /// + /// Required metrics that could NOT be compared, i.e. the coverage gap. In practice the reason is + /// always an unavailable value: a unit mismatch is a contract error that + /// rejects before banding, so it cannot reach a comparison result. + /// + public int UnavailableRequiredMetricCount => RequiredMetrics.Count(metric => metric.Band == ComparisonBand.NotApplicable); + + /// The worst comparable metric band (hour-of-year excluded, informational). + public GateStatus NumericalStatus => Query.NumericalStatus(AllMetrics); + + /// + /// Whether every space aligned AND every required metric was actually compared (else Warn), so an + /// incomplete run can never present as complete. + /// + public GateStatus CoverageStatus => Query.CoverageStatus(SpaceDiagnostics, ComparableMetricCount, UnavailableRequiredMetricCount); + + /// Whether the two runs are compatible enough to compare (else Fail). + public GateStatus ProvenanceStatus => Query.ProvenanceStatus(ProvenanceCompatibility); + + /// Whether within-document model totals reconcile against their matched spaces (else Warn). + public GateStatus ReconciliationStatus => Query.ReconciliationStatus(Reconciliations); + + /// + /// The overall gate: the worst of the numerical, coverage, provenance and reconciliation statuses, so + /// missing data, mismatched inputs or incomplete coverage can never present as a clean pass. + /// + public GateStatus Gate => Query.OverallGate(NumericalStatus, CoverageStatus, ProvenanceStatus, ReconciliationStatus); + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Classes/Enums.cs b/SAM.Analytical.Benchmark.Compare/Classes/Enums.cs new file mode 100644 index 0000000..1804cf9 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Classes/Enums.cs @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// The tolerance band a single metric comparison lands in. Bands are provisional reporting + /// buckets driven by a , never validated scientific thresholds. + /// + public enum ComparisonBand + { + /// The two values agree within the profile's warn band (the tightest, "green" band). + Match = 0, + + /// The two values disagree beyond the warn band but within the fail band ("amber"). + Warn, + + /// The two values disagree beyond the fail band ("red"). + Fail, + + /// + /// The metric cannot be compared (a value is unavailable on one/both sides, or the units differ). + /// N/A never counts as a failure. + /// + NotApplicable + } + + /// Why a comparison is . + public enum NotApplicableReason + { + /// The comparison is applicable (a real band was assigned). + None = 0, + + /// A value is unavailable on one or both sides. + Unavailable, + + /// Both values are available but their declared units differ, so they are not comparable. + UnitMismatch + } + + /// Which sides carry an available value for a metric. + public enum ComparisonAvailability + { + /// Neither side has an available value. + Neither = 0, + + /// Only the TAS side has an available value. + TasOnly, + + /// Only the OpenStudio side has an available value. + OpenStudioOnly, + + /// Both sides have an available value. + Both + } + + /// How a space was aligned across the two documents. + public enum SpaceMatchKind + { + /// Matched on a shared 32-hex source GUID (the strongest identity). + Guid = 0, + + /// Matched on a normalized name after GUID matching left it unmatched. + Name, + + /// Present only in the TAS document. + TasOnly, + + /// Present only in the OpenStudio document. + OpenStudioOnly + } + + /// The overall gate outcome for a comparison, derived from the worst applicable band. + public enum GateStatus + { + /// Every applicable metric landed in the match band. + Pass = 0, + + /// At least one applicable metric warned, but none failed. + Warn, + + /// At least one applicable metric failed. + Fail + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Classes/MetricComparison.cs b/SAM.Analytical.Benchmark.Compare/Classes/MetricComparison.cs new file mode 100644 index 0000000..85cd525 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Classes/MetricComparison.cs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// The comparison of a single metric across the two documents: the raw values, their absolute and + /// relative differences, the assigned tolerance band, and availability/unit diagnostics. + /// + /// + /// Underlying / and the differences are stored at + /// full binary64 precision — reports format them without rounding so the output is deterministic and + /// exactly reproducible. + /// + public sealed class MetricComparison + { + public MetricComparison( + string scope, + string key, + MetricUnit unit, + double? tasValue, + bool tasAvailable, + double? openStudioValue, + bool openStudioAvailable, + bool circular, + double? absoluteDifference, + double? relativeDifference, + double? signedDifference, + ComparisonBand band, + NotApplicableReason notApplicableReason, + MetricUnit tasUnit, + MetricUnit openStudioUnit) + { + Scope = scope; + Key = key; + Unit = unit; + TasValue = tasValue; + TasAvailable = tasAvailable; + OpenStudioValue = openStudioValue; + OpenStudioAvailable = openStudioAvailable; + Circular = circular; + AbsoluteDifference = absoluteDifference; + RelativeDifference = relativeDifference; + SignedDifference = signedDifference; + Band = band; + NotApplicableReason = notApplicableReason; + TasUnit = tasUnit; + OpenStudioUnit = openStudioUnit; + } + + /// The owning scope: model for whole-model metrics, or a space identity. + public string Scope { get; } + + /// A stable, dotted metric key (e.g. consumptionHeating, heating.peakLoad). + public string Key { get; } + + /// The canonical unit the comparison is expressed in (the shared unit when both agree). + public MetricUnit Unit { get; } + + public double? TasValue { get; } + + public bool TasAvailable { get; } + + public double? OpenStudioValue { get; } + + public bool OpenStudioAvailable { get; } + + /// True when the metric is an hour-of-year and its difference is computed circularly. + public bool Circular { get; } + + /// The non-negative difference used for banding (|os-tas|, or the circular hour diff). + public double? AbsoluteDifference { get; } + + /// + /// The relative difference |os-tas| / max(|tas|,|os|), or null when it is not meaningful + /// (an hour-of-year metric, an unavailable/mismatched metric, or both magnitudes exactly zero). + /// + public double? RelativeDifference { get; } + + /// The signed difference os-tas (direction), or null for circular hour metrics. + public double? SignedDifference { get; } + + public ComparisonBand Band { get; } + + public NotApplicableReason NotApplicableReason { get; } + + /// The unit declared on the TAS side (used to explain a unit mismatch). + public MetricUnit TasUnit { get; } + + /// The unit declared on the OpenStudio side (used to explain a unit mismatch). + public MetricUnit OpenStudioUnit { get; } + + public ComparisonAvailability Availability + { + get + { + if (TasAvailable && OpenStudioAvailable) + { + return ComparisonAvailability.Both; + } + + if (TasAvailable) + { + return ComparisonAvailability.TasOnly; + } + + return OpenStudioAvailable ? ComparisonAvailability.OpenStudioOnly : ComparisonAvailability.Neither; + } + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Classes/ProvenanceCompatibility.cs b/SAM.Analytical.Benchmark.Compare/Classes/ProvenanceCompatibility.cs new file mode 100644 index 0000000..f0b1f6f --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Classes/ProvenanceCompatibility.cs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Collections.Generic; + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// A single provenance field that disagrees between the two documents (e.g. a different canonical + /// model hash or weather), reported so an incompatible comparison cannot masquerade as a clean pass. + /// + public sealed class ProvenanceMismatch + { + public ProvenanceMismatch(string field, string? tas, string? openStudio) + { + Field = field; + Tas = tas; + OpenStudio = openStudio; + } + + public string Field { get; } + + public string? Tas { get; } + + public string? OpenStudio { get; } + } + + /// + /// Whether the two documents were produced from compatible inputs — the same canonical source model, + /// weather and design-day source. TOLERANCES.md treats provenance compatibility as a distinct gate + /// concern that must be checked before a numerical gate is meaningful: a numerical Pass over + /// mismatched inputs (different model or weather) is not evidence of engine agreement. + /// + public sealed class ProvenanceCompatibility + { + public ProvenanceCompatibility(IReadOnlyList mismatches) + { + Mismatches = mismatches; + } + + public IReadOnlyList Mismatches { get; } + + /// True when the two documents share compatible source-model, weather and design-day inputs. + public bool IsCompatible => Mismatches.Count == 0; + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Classes/ReconciliationResult.cs b/SAM.Analytical.Benchmark.Compare/Classes/ReconciliationResult.cs new file mode 100644 index 0000000..e678e53 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Classes/ReconciliationResult.cs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// A within-engine reconciliation of an additive whole-model total against the sum of the same + /// quantity across that engine's spaces (floor area and volume). Non-additive quantities such as peak + /// loads are deliberately NOT reconciled, because a coincident model peak is not the sum of the + /// per-space peaks. + /// + public sealed class ReconciliationResult + { + public ReconciliationResult( + string engine, + string key, + MetricUnit unit, + double? modelTotal, + bool modelTotalAvailable, + double sumOfSpaces, + int contributingSpaces, + int spacesMissingValue, + int excludedSpaces, + bool complete, + double? absoluteDifference, + double? relativeDifference, + ComparisonBand band, + NotApplicableReason notApplicableReason) + { + Engine = engine; + Key = key; + Unit = unit; + ModelTotal = modelTotal; + ModelTotalAvailable = modelTotalAvailable; + SumOfSpaces = sumOfSpaces; + ContributingSpaces = contributingSpaces; + SpacesMissingValue = spacesMissingValue; + ExcludedSpaces = excludedSpaces; + Complete = complete; + AbsoluteDifference = absoluteDifference; + RelativeDifference = relativeDifference; + Band = band; + NotApplicableReason = notApplicableReason; + } + + /// The engine whose internal totals are being reconciled (TAS or OpenStudio). + public string Engine { get; } + + /// The reconciled quantity key (floorArea or volume). + public string Key { get; } + + public MetricUnit Unit { get; } + + public double? ModelTotal { get; } + + public bool ModelTotalAvailable { get; } + + public double SumOfSpaces { get; } + + /// How many uniquely-matched spaces contributed an available value to the sum. + public int ContributingSpaces { get; } + + /// How many uniquely-matched spaces lacked an available value and were skipped. + public int SpacesMissingValue { get; } + + /// + /// How many of this engine's spaces were excluded from the sum because they were not uniquely + /// matched across both documents (one-sided, duplicated or ambiguous). + /// + public int ExcludedSpaces { get; } + + /// + /// True only when the reconciliation is complete: the model total is available, every one of this + /// engine's spaces was uniquely matched, and every matched space contributed a value. Missing or + /// unmatched spaces make a reconciliation incomplete even when the numbers happen to agree. + /// + public bool Complete { get; } + + public double? AbsoluteDifference { get; } + + public double? RelativeDifference { get; } + + public ComparisonBand Band { get; } + + public NotApplicableReason NotApplicableReason { get; } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Classes/SpaceComparison.cs b/SAM.Analytical.Benchmark.Compare/Classes/SpaceComparison.cs new file mode 100644 index 0000000..f375385 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Classes/SpaceComparison.cs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Collections.Generic; + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// The comparison of a single aligned space (or a space present on only one side) together with the + /// per-metric comparisons that belong to it. + /// + public sealed class SpaceComparison + { + public SpaceComparison( + string? guid, + string? tasName, + string? openStudioName, + SpaceMatchKind matchKind, + IReadOnlyList metrics) + { + Guid = guid; + TasName = tasName; + OpenStudioName = openStudioName; + MatchKind = matchKind; + Metrics = metrics; + } + + /// The shared GUID when the space was matched by GUID, otherwise the available side's GUID. + public string? Guid { get; } + + /// The name carried by the TAS document (null if the space is OpenStudio-only). + public string? TasName { get; } + + /// The name carried by the OpenStudio document (null if the space is TAS-only). + public string? OpenStudioName { get; } + + public SpaceMatchKind MatchKind { get; } + + public IReadOnlyList Metrics { get; } + + /// A stable scope label for reports: the GUID when present, else the best available name. + public string Scope => Guid ?? TasName ?? OpenStudioName ?? "(unnamed)"; + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Classes/SpaceMatchDiagnostics.cs b/SAM.Analytical.Benchmark.Compare/Classes/SpaceMatchDiagnostics.cs new file mode 100644 index 0000000..c141fec --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Classes/SpaceMatchDiagnostics.cs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System.Collections.Generic; + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// Diagnostics describing how the two documents' space lists lined up: counts by match kind plus the + /// specific identities that failed to align cleanly (present on one side only, duplicated, or an + /// ambiguous "split" where one name matched several candidates). + /// + public sealed class SpaceMatchDiagnostics + { + public SpaceMatchDiagnostics( + int tasCount, + int openStudioCount, + int matchedByGuid, + int matchedByName, + IReadOnlyList onlyInTas, + IReadOnlyList onlyInOpenStudio, + IReadOnlyList duplicateTasGuids, + IReadOnlyList duplicateOpenStudioGuids, + IReadOnlyList duplicateTasNames, + IReadOnlyList duplicateOpenStudioNames, + IReadOnlyList ambiguousNameMatches) + { + TasCount = tasCount; + OpenStudioCount = openStudioCount; + MatchedByGuid = matchedByGuid; + MatchedByName = matchedByName; + OnlyInTas = onlyInTas; + OnlyInOpenStudio = onlyInOpenStudio; + DuplicateTasGuids = duplicateTasGuids; + DuplicateOpenStudioGuids = duplicateOpenStudioGuids; + DuplicateTasNames = duplicateTasNames; + DuplicateOpenStudioNames = duplicateOpenStudioNames; + AmbiguousNameMatches = ambiguousNameMatches; + } + + public int TasCount { get; } + + public int OpenStudioCount { get; } + + public int MatchedByGuid { get; } + + public int MatchedByName { get; } + + /// Identities (GUID or name) present only in the TAS document. + public IReadOnlyList OnlyInTas { get; } + + /// Identities (GUID or name) present only in the OpenStudio document. + public IReadOnlyList OnlyInOpenStudio { get; } + + /// GUIDs that appeared on more than one TAS space (only the first is aligned). + public IReadOnlyList DuplicateTasGuids { get; } + + /// GUIDs that appeared on more than one OpenStudio space (only the first is aligned). + public IReadOnlyList DuplicateOpenStudioGuids { get; } + + /// Normalized names duplicated among GUID-less TAS spaces (ambiguous for name fallback). + public IReadOnlyList DuplicateTasNames { get; } + + /// Normalized names duplicated among GUID-less OpenStudio spaces (ambiguous for name fallback). + public IReadOnlyList DuplicateOpenStudioNames { get; } + + /// + /// Names that could not be matched by the name fallback because the same normalized name would + /// match several candidates on the other side (a "split" space). + /// + public IReadOnlyList AmbiguousNameMatches { get; } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Classes/ToleranceProfile.cs b/SAM.Analytical.Benchmark.Compare/Classes/ToleranceProfile.cs new file mode 100644 index 0000000..9128b55 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Classes/ToleranceProfile.cs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// A named, configurable set of provisional tolerance bands. These are reporting buckets only — + /// they are explicitly NOT validated thresholds and must never be presented as such. + /// + /// + /// + /// Per TOLERANCES.md, a metric's band comes from its RELATIVE difference + /// |a-b| / max(|a|,|b|, floor(unit)) compared against and + /// . The per-unit is a lower bound on the SCALE + /// (the denominator), not an additive allowance: it stops a tiny denominator turning a negligible + /// absolute difference into an extreme percentage, while still letting a genuine near-zero + /// disagreement (0 versus the floor is 100%) show up. Hour-of-year metrics ignore the relative term and + /// use the absolute / limits with a + /// circular (year-boundary-wrapping) difference instead. + /// + /// + public sealed class ToleranceProfile + { + /// The default profile name recognised by the CLI. + public const string DefaultName = "default"; + + private readonly Dictionary nearZeroFloors; + + public ToleranceProfile( + string name, + double warnRelative, + double failRelative, + double hourWarnAbsolute, + double hourFailAbsolute, + IReadOnlyDictionary nearZeroFloors, + double defaultNearZeroFloor) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("A tolerance profile name is required.", nameof(name)); + } + + if (!IsFiniteNonNegative(warnRelative) || !IsFiniteNonNegative(failRelative)) + { + throw new ArgumentException("Relative bands must be finite and non-negative."); + } + + if (failRelative < warnRelative) + { + throw new ArgumentException("The fail band must be at least as wide as the warn band."); + } + + if (!IsFiniteNonNegative(hourWarnAbsolute) || !IsFiniteNonNegative(hourFailAbsolute) || hourFailAbsolute < hourWarnAbsolute) + { + throw new ArgumentException("Hour bands must be finite, non-negative and ordered warn <= fail."); + } + + if (!IsFiniteNonNegative(defaultNearZeroFloor)) + { + throw new ArgumentException("The default near-zero floor must be finite and non-negative.", nameof(defaultNearZeroFloor)); + } + + Name = name; + WarnRelative = warnRelative; + FailRelative = failRelative; + HourWarnAbsolute = hourWarnAbsolute; + HourFailAbsolute = hourFailAbsolute; + DefaultNearZeroFloor = defaultNearZeroFloor; + this.nearZeroFloors = new Dictionary(); + if (nearZeroFloors != null) + { + foreach (KeyValuePair entry in nearZeroFloors) + { + if (!IsFiniteNonNegative(entry.Value)) + { + throw new ArgumentException("Near-zero floors must be finite and non-negative.", nameof(nearZeroFloors)); + } + + this.nearZeroFloors[entry.Key] = entry.Value; + } + } + } + + /// The profile name, echoed into every report for provenance. + public string Name { get; } + + /// The relative half-width of the warn ("green") band, e.g. 0.05 for ±5%. + public double WarnRelative { get; } + + /// The relative half-width of the fail ("amber/red" boundary) band, e.g. 0.15 for ±15%. + public double FailRelative { get; } + + /// The absolute hour-of-year difference within which a peak-hour comparison matches. + public double HourWarnAbsolute { get; } + + /// The absolute hour-of-year difference within which a peak-hour comparison only warns. + public double HourFailAbsolute { get; } + + /// The near-zero absolute floor used when a metric's unit has no explicit floor. + public double DefaultNearZeroFloor { get; } + + /// The provisional default profile: ±5% warn, ±15% fail, ±1h warn / ±24h fail on peak hours. + public static ToleranceProfile Default { get; } = CreateDefault(); + + /// The per-unit near-zero absolute floor (falls back to ). + public double NearZeroFloor(MetricUnit unit) + { + return nearZeroFloors.TryGetValue(unit, out double floor) ? floor : DefaultNearZeroFloor; + } + + /// + /// The explicitly-configured per-unit near-zero floors, in ascending unit order, so reports can + /// record the exact profile values (TOLERANCES.md requires the actual values, not just the name). + /// + public IReadOnlyList> ConfiguredNearZeroFloors() + { + var floors = new List>(nearZeroFloors); + floors.Sort((left, right) => ((int)left.Key).CompareTo((int)right.Key)); + return floors; + } + + /// Resolves a profile by (case-insensitive) name. Currently only default exists. + public static ToleranceProfile Resolve(string? name) + { + if (string.IsNullOrWhiteSpace(name) || string.Equals(name, DefaultName, StringComparison.OrdinalIgnoreCase)) + { + return Default; + } + + throw new ArgumentException($"Unknown tolerance profile '{name}'. The only supported profile is '{DefaultName}'.", nameof(name)); + } + + private static ToleranceProfile CreateDefault() + { + // Provisional per-unit floors: magnitudes at or below these are treated as effectively zero, + // so a benign difference between two near-zero readings does not explode into a large relative + // difference. They are NOT validated significance thresholds. + var floors = new Dictionary + { + [MetricUnit.KilowattHour] = 1.0, // 1 kWh + [MetricUnit.WattHour] = 1000.0, // 1 kWh expressed in Wh + [MetricUnit.Kilowatt] = 0.01, // 10 W + [MetricUnit.Watt] = 10.0, // 10 W + [MetricUnit.SquareMetre] = 0.01, // 0.01 m2 + [MetricUnit.CubicMetre] = 0.01, // 0.01 m3 + [MetricUnit.Hour] = 0.5 // half an unmet hour + }; + + return new ToleranceProfile( + DefaultName, + warnRelative: 0.05, + failRelative: 0.15, + hourWarnAbsolute: 1.0, + hourFailAbsolute: 24.0, + nearZeroFloors: floors, + defaultNearZeroFloor: 0.0); + } + + private static bool IsFiniteNonNegative(double value) + { + return !double.IsNaN(value) && !double.IsInfinity(value) && value >= 0; + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Modify/Format.cs b/SAM.Analytical.Benchmark.Compare/Modify/Format.cs new file mode 100644 index 0000000..3d35ed5 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Modify/Format.cs @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Globalization; + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// Deterministic, invariant-culture formatting shared by every report writer. Numeric values are + /// emitted at full binary64 precision (the shortest round-trippable form) with NO rounding, so the + /// reports are byte-stable and exactly reproducible. + /// + internal static class Format + { + internal static string Number(double? value) + { + return value.HasValue ? value.Value.ToString(CultureInfo.InvariantCulture) : string.Empty; + } + + internal static string Percent(double? relative) + { + // The relative difference is a fraction; a percentage is a convenience view of the same + // underlying value and is likewise emitted without rounding. + return relative.HasValue ? (relative.Value * 100d).ToString(CultureInfo.InvariantCulture) : string.Empty; + } + + internal static string Unit(MetricUnit unit) + { + switch (unit) + { + case MetricUnit.KilowattHour: return "kWh"; + case MetricUnit.WattHour: return "Wh"; + case MetricUnit.Kilowatt: return "kW"; + case MetricUnit.Watt: return "W"; + case MetricUnit.SquareMetre: return "m2"; + case MetricUnit.CubicMetre: return "m3"; + case MetricUnit.HourOfYear: return "hourOfYear"; + case MetricUnit.Hour: return "h"; + default: return "unknown"; + } + } + + internal static string Band(ComparisonBand band) + { + switch (band) + { + case ComparisonBand.Match: return "Match"; + case ComparisonBand.Warn: return "Warn"; + case ComparisonBand.Fail: return "Fail"; + default: return "N/A"; + } + } + + internal static string Gate(GateStatus gate) + { + switch (gate) + { + case GateStatus.Pass: return "Pass"; + case GateStatus.Warn: return "Warn"; + default: return "Fail"; + } + } + + internal static string Availability(ComparisonAvailability availability) + { + switch (availability) + { + case ComparisonAvailability.Both: return "Both"; + case ComparisonAvailability.TasOnly: return "TasOnly"; + case ComparisonAvailability.OpenStudioOnly: return "OpenStudioOnly"; + default: return "Neither"; + } + } + + internal static string MatchKind(SpaceMatchKind kind) + { + switch (kind) + { + case SpaceMatchKind.Guid: return "Guid"; + case SpaceMatchKind.Name: return "Name"; + case SpaceMatchKind.TasOnly: return "TasOnly"; + default: return "OpenStudioOnly"; + } + } + + internal static string NotApplicableReason(NotApplicableReason reason) + { + switch (reason) + { + case Compare.NotApplicableReason.Unavailable: return "unavailable"; + case Compare.NotApplicableReason.UnitMismatch: return "unit-mismatch"; + default: return string.Empty; + } + } + + /// The marker appended to a metric that carries a band which cannot move the numerical status. + internal const string InformationalNote = "informational (excluded from the numerical status)"; + + internal static string NotApplicableNote(MetricComparison metric) + { + switch (metric.NotApplicableReason) + { + case Compare.NotApplicableReason.Unavailable: + return "unavailable (" + Availability(metric.Availability) + ")"; + case Compare.NotApplicableReason.UnitMismatch: + return "unit-mismatch (tas=" + Unit(metric.TasUnit) + ", openStudio=" + Unit(metric.OpenStudioUnit) + ")"; + default: + return metric.Circular ? "circular hour difference" : string.Empty; + } + } + + /// + /// The per-row note shown in the Markdown and CSV tables: the N/A explanation plus, for a metric + /// that DOES carry a real band but is designated informational, an explicit marker. Without it a + /// reader would see a Fail band on a whole-model peak load or a peak hour and reasonably assume it + /// contributed to the gate. + /// + internal static string MetricNote(MetricComparison metric) + { + string note = NotApplicableNote(metric); + if (metric.Band == ComparisonBand.NotApplicable || !Query.IsInformationalForNumericalGate(metric)) + { + return note; + } + + return note.Length == 0 ? InformationalNote : note + "; " + InformationalNote; + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Modify/WriteCsv.cs b/SAM.Analytical.Benchmark.Compare/Modify/WriteCsv.cs new file mode 100644 index 0000000..7b2680c --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Modify/WriteCsv.cs @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Text; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Modify + { + private static readonly string[] CsvHeader = + { + "scope", + "metric", + "unit", + "availability", + "tasValue", + "openStudioValue", + "absoluteDifference", + "relativeDifference", + "relativePercent", + "signedDifference", + "band", + "note", + // TOLERANCES.md: every report must name the tolerance profile AND carry its actual values, so a + // changed profile can never hide behind an unchanged name. The CSV is routinely archived or + // imported on its own, without its sibling Markdown/JSON, so the values are repeated per row + // rather than written as a preamble (which would break strict RFC-4180 parsing). With + // nearZeroFloor being the floor for THAT row's unit, each row is independently reproducible: + // scale = max(|tas|, |openStudio|, nearZeroFloor), then band from warnRelative/failRelative. + "toleranceProfile", + "warnRelative", + "failRelative", + "hourWarnAbsolute", + "hourFailAbsolute", + "nearZeroFloor", + "informational" + }; + + /// + /// Renders the per-metric comparison table (model metrics followed by every space's metrics, in + /// the deterministic order produced by ) as RFC-4180 CSV with LF line + /// endings. Numeric fields are full-precision and unrounded. Each row also carries the tolerance + /// profile that classified it, so the file remains interpretable and reproducible on its own. + /// + public static string WriteCsv(ComparisonResult result) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + var builder = new StringBuilder(); + AppendRow(builder, CsvHeader); + + foreach (MetricComparison metric in result.ModelMetrics) + { + AppendMetricRow(builder, metric, result.ToleranceProfile); + } + + foreach (SpaceComparison space in result.Spaces) + { + foreach (MetricComparison metric in space.Metrics) + { + AppendMetricRow(builder, metric, result.ToleranceProfile); + } + } + + return builder.ToString(); + } + + private static void AppendMetricRow(StringBuilder builder, MetricComparison metric, ToleranceProfile profile) + { + AppendRow(builder, new[] + { + metric.Scope, + metric.Key, + Format.Unit(metric.Unit), + Format.Availability(metric.Availability), + Format.Number(metric.TasValue), + Format.Number(metric.OpenStudioValue), + Format.Number(metric.AbsoluteDifference), + Format.Number(metric.RelativeDifference), + Format.Percent(metric.RelativeDifference), + Format.Number(metric.SignedDifference), + Format.Band(metric.Band), + Format.MetricNote(metric), + profile.Name, + Format.Number(profile.WarnRelative), + Format.Number(profile.FailRelative), + Format.Number(profile.HourWarnAbsolute), + Format.Number(profile.HourFailAbsolute), + Format.Number(profile.NearZeroFloor(metric.Unit)), + Query.IsInformationalForNumericalGate(metric) ? "true" : "false" + }); + } + + private static void AppendRow(StringBuilder builder, IReadOnlyList fields) + { + for (int index = 0; index < fields.Count; index++) + { + if (index > 0) + { + builder.Append(','); + } + + builder.Append(EscapeCsv(fields[index])); + } + + builder.Append('\n'); + } + + private static string EscapeCsv(string? field) + { + string value = field ?? string.Empty; + bool mustQuote = value.IndexOf(',') >= 0 || value.IndexOf('"') >= 0 || value.IndexOf('\n') >= 0 || value.IndexOf('\r') >= 0; + if (!mustQuote) + { + return value; + } + + return "\"" + value.Replace("\"", "\"\"") + "\""; + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Modify/WriteMarkdown.cs b/SAM.Analytical.Benchmark.Compare/Modify/WriteMarkdown.cs new file mode 100644 index 0000000..a8dfc1f --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Modify/WriteMarkdown.cs @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Text; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Modify + { + /// + /// Renders a human-readable Markdown report. The content is derived entirely from the comparison + /// (no wall-clock or machine-specific data is added), so the output is deterministic and + /// byte-stable. Provisional tolerance bands are labelled as such. + /// + public static string WriteMarkdown(ComparisonResult result) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + var builder = new StringBuilder(); + Line(builder, "# Benchmark comparison: TAS vs OpenStudio"); + Line(builder, string.Empty); + Line(builder, "Independent comparison of two engine-neutral benchmark documents. Tolerance bands are"); + Line(builder, "**provisional reporting buckets**, not validated thresholds. The overall gate is the worst of"); + Line(builder, "the numerical, coverage, provenance and reconciliation statuses. Peak-hour bands and the two"); + Line(builder, "whole-model peak-load bands are informational and are excluded from the numerical status — they are"); + Line(builder, "still calculated and shown, and rows carrying such a band are marked in the Note column;"); + Line(builder, "reconciliation is a separate within-document diagnostic status that also contributes to the"); + Line(builder, "overall gate."); + Line(builder, string.Empty); + + Line(builder, "## Summary"); + Line(builder, string.Empty); + Line(builder, "| Field | Value |"); + Line(builder, "| --- | --- |"); + Line(builder, Row("Tolerance profile", result.ToleranceProfileName)); + Line(builder, Row("Gate status (overall)", Format.Gate(result.Gate))); + Line(builder, Row("Numerical status", Format.Gate(result.NumericalStatus))); + Line(builder, Row("Coverage status", Format.Gate(result.CoverageStatus))); + Line(builder, Row("Provenance status", Format.Gate(result.ProvenanceStatus))); + Line(builder, Row("Reconciliation status", Format.Gate(result.ReconciliationStatus))); + Line(builder, Row("Metrics matched", result.MatchCount.ToString(System.Globalization.CultureInfo.InvariantCulture))); + Line(builder, Row("Metrics warned", result.WarnCount.ToString(System.Globalization.CultureInfo.InvariantCulture))); + Line(builder, Row("Metrics failed", result.FailCount.ToString(System.Globalization.CultureInfo.InvariantCulture))); + Line(builder, Row("Metrics not applicable", result.NotApplicableCount.ToString(System.Globalization.CultureInfo.InvariantCulture))); + Line(builder, Row("Required metrics compared", Count(result.ComparableMetricCount) + " of " + Count(result.RequiredMetricCount))); + Line(builder, Row("Required metrics unavailable", Count(result.UnavailableRequiredMetricCount))); + Line(builder, Row("TAS schema version", result.TasSchemaVersion)); + Line(builder, Row("OpenStudio schema version", result.OpenStudioSchemaVersion)); + Line(builder, string.Empty); + + if (result.SchemaDriftNote != null) + { + Line(builder, "> **Schema drift:** " + EscapeInline(result.SchemaDriftNote)); + Line(builder, string.Empty); + } + + AppendCoverage(builder, result); + AppendProvenanceCompatibility(builder, result); + AppendToleranceProfile(builder, result); + AppendProvenance(builder, result); + AppendModelMetrics(builder, result); + AppendReconciliation(builder, result); + AppendSpaceDiagnostics(builder, result); + AppendSpaceMetrics(builder, result); + + return builder.ToString(); + } + + private static void AppendCoverage(StringBuilder builder, ComparisonResult result) + { + // TOLERANCES.md: unavailable required metrics and unmatched spaces are counted separately, and a + // numerically passing report with poor coverage must not be described as complete. Coverage is + // therefore reported in full, with the reasons it is incomplete spelled out. + SpaceMatchDiagnostics diagnostics = result.SpaceDiagnostics; + int unmatchedSpaces = diagnostics.OnlyInTas.Count + diagnostics.OnlyInOpenStudio.Count; + int duplicateIdentities = diagnostics.DuplicateTasGuids.Count + + diagnostics.DuplicateOpenStudioGuids.Count + + diagnostics.DuplicateTasNames.Count + + diagnostics.DuplicateOpenStudioNames.Count; + + Line(builder, "## Coverage"); + Line(builder, string.Empty); + Line(builder, "How much of the two runs was actually compared. Required metrics are the whole-model metrics plus"); + Line(builder, "the metrics of uniquely matched spaces; the metrics of one-sided, duplicated or ambiguous spaces are"); + Line(builder, "not counted here because those spaces are reported under space alignment instead. An unavailable"); + Line(builder, "metric is never a numerical failure, so it is recorded here — otherwise missing results could read as"); + Line(builder, "a clean pass."); + Line(builder, string.Empty); + Line(builder, "| Field | Value |"); + Line(builder, "| --- | --- |"); + Line(builder, Row("Coverage status", Format.Gate(result.CoverageStatus))); + Line(builder, Row("Required metrics", Count(result.RequiredMetricCount))); + Line(builder, Row("Compared", Count(result.ComparableMetricCount))); + Line(builder, Row("Unavailable (not compared)", Count(result.UnavailableRequiredMetricCount))); + Line(builder, Row("Unmatched spaces", Count(unmatchedSpaces))); + Line(builder, Row("Ambiguous (split) space names", Count(diagnostics.AmbiguousNameMatches.Count))); + Line(builder, Row("Duplicated space identities", Count(duplicateIdentities))); + Line(builder, string.Empty); + + if (result.CoverageStatus == GateStatus.Pass) + { + Line(builder, "Coverage is complete: every space aligned uniquely and every required metric was compared."); + Line(builder, string.Empty); + return; + } + + Line(builder, "**Coverage is INCOMPLETE**, so this comparison must not be described as a complete result:"); + Line(builder, string.Empty); + if (result.ComparableMetricCount == 0) + { + Line(builder, "- no metric could be compared at all."); + } + + if (result.UnavailableRequiredMetricCount > 0) + { + Line(builder, "- " + Count(result.UnavailableRequiredMetricCount) + " of " + Count(result.RequiredMetricCount) + + " required metrics were unavailable on at least one side (reported as N/A, never as a numerical failure)."); + } + + if (unmatchedSpaces > 0) + { + Line(builder, "- " + Count(unmatchedSpaces) + " space(s) exist on only one side."); + } + + if (diagnostics.AmbiguousNameMatches.Count > 0) + { + Line(builder, "- " + Count(diagnostics.AmbiguousNameMatches.Count) + " space name(s) were ambiguous (split) and could not be aligned."); + } + + if (duplicateIdentities > 0) + { + Line(builder, "- " + Count(duplicateIdentities) + " space identity/identities were duplicated and could not be matched uniquely."); + } + + Line(builder, string.Empty); + } + + private static void AppendProvenanceCompatibility(StringBuilder builder, ComparisonResult result) + { + ProvenanceCompatibility compatibility = result.ProvenanceCompatibility; + Line(builder, "## Provenance compatibility"); + Line(builder, string.Empty); + if (compatibility.IsCompatible) + { + Line(builder, "Compatible: both runs share the same canonical model, weather and design-day basis, with the expected engine and route on each side."); + Line(builder, string.Empty); + return; + } + + Line(builder, "**INCOMPATIBLE** — the two documents are not a valid comparison pair, so a numerical pass is not meaningful."); + Line(builder, string.Empty); + Line(builder, "| Field | TAS | OpenStudio |"); + Line(builder, "| --- | --- | --- |"); + foreach (ProvenanceMismatch mismatch in compatibility.Mismatches) + { + Line(builder, Row3(mismatch.Field, mismatch.Tas, mismatch.OpenStudio)); + } + + Line(builder, string.Empty); + } + + private static void AppendToleranceProfile(StringBuilder builder, ComparisonResult result) + { + ToleranceProfile profile = result.ToleranceProfile; + Line(builder, "## Tolerance profile"); + Line(builder, string.Empty); + Line(builder, "Provisional reporting bands (not validated thresholds). Recorded so a changed profile is never hidden behind an unchanged name."); + Line(builder, string.Empty); + Line(builder, "| Setting | Value |"); + Line(builder, "| --- | --- |"); + Line(builder, Row("Name", profile.Name)); + Line(builder, Row("Warn (relative)", Format.Number(profile.WarnRelative))); + Line(builder, Row("Fail (relative)", Format.Number(profile.FailRelative))); + Line(builder, Row("Peak-hour warn (h)", Format.Number(profile.HourWarnAbsolute))); + Line(builder, Row("Peak-hour fail (h)", Format.Number(profile.HourFailAbsolute))); + Line(builder, Row("Default near-zero floor", Format.Number(profile.DefaultNearZeroFloor))); + foreach (System.Collections.Generic.KeyValuePair floor in profile.ConfiguredNearZeroFloors()) + { + Line(builder, Row("Near-zero floor (" + Format.Unit(floor.Key) + ")", Format.Number(floor.Value))); + } + + Line(builder, string.Empty); + } + + private static void AppendProvenance(StringBuilder builder, ComparisonResult result) + { + Line(builder, "## Provenance"); + Line(builder, string.Empty); + Line(builder, "| Field | TAS | OpenStudio |"); + Line(builder, "| --- | --- | --- |"); + BenchmarkProvenance? tas = result.TasProvenance; + BenchmarkProvenance? openStudio = result.OpenStudioProvenance; + Line(builder, Row3("Engine kind", EngineKindToken(tas?.Engine?.Kind), EngineKindToken(openStudio?.Engine?.Kind))); + Line(builder, Row3("Engine name", tas?.Engine?.Name, openStudio?.Engine?.Name)); + Line(builder, Row3("Engine version", tas?.Engine?.Version, openStudio?.Engine?.Version)); + Line(builder, Row3("SDK version", tas?.Engine?.SdkVersion, openStudio?.Engine?.SdkVersion)); + Line(builder, Row3("Route", RouteToken(tas?.Route), RouteToken(openStudio?.Route))); + Line(builder, Row3("Weather", tas?.Weather?.Identity, openStudio?.Weather?.Identity)); + Line(builder, Row3("Design-day source", DesignDayToken(tas?.DesignDaySource), DesignDayToken(openStudio?.DesignDaySource))); + Line(builder, Row3("Run state", RunStateToken(tas?.State), RunStateToken(openStudio?.State))); + Line(builder, Row3("Source model", tas?.SourceModelName, openStudio?.SourceModelName)); + Line(builder, Row3("Source model GUID", tas?.SourceModelGuid, openStudio?.SourceModelGuid)); + Line(builder, Row3("SAM commit", tas?.SamCommit, openStudio?.SamCommit)); + Line(builder, string.Empty); + } + + private static void AppendModelMetrics(StringBuilder builder, ComparisonResult result) + { + Line(builder, "## Model metrics"); + Line(builder, string.Empty); + // TOLERANCES.md / METRICS.md require these methodological limitations to remain visible even when + // the values fall in Pass, so they are printed with the model metrics regardless of band. + Line(builder, "> **Caveats (apply even when values pass):** whole-model peak-load semantics differ between"); + Line(builder, "> engines — OpenStudio reports a coincident total, TAS a building-profile maximum — so peak-load"); + Line(builder, "> comparisons are informational, not equivalence claims. `peakHeatingLoad` and `peakCoolingLoad`"); + Line(builder, "> are therefore banded and shown below but **excluded from the numerical status**, exactly like the"); + Line(builder, "> peak-hour metrics; promoting them into the gate needs the B4 corpus and energy-modeller review."); + Line(builder, "> Every other metric — annual energy, per-space peak loads, unmet hours, geometry — gates normally."); + Line(builder, "> The primary conditioning pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing)"); + Line(builder, "> is a **candidate** pending validation. A tolerance profile cannot remove these limitations."); + Line(builder, string.Empty); + AppendMetricTableHeader(builder); + foreach (MetricComparison metric in result.ModelMetrics) + { + AppendMetricRow(builder, metric.Key, metric); + } + + Line(builder, string.Empty); + } + + private static void AppendReconciliation(StringBuilder builder, ComparisonResult result) + { + Line(builder, "## Model-total vs sum-of-spaces reconciliation"); + Line(builder, string.Empty); + Line(builder, "Additive quantities only; non-additive peak loads are not reconciled. Only uniquely-matched spaces are summed; unmatched or ambiguous spaces make a reconciliation incomplete."); + Line(builder, string.Empty); + Line(builder, "| Engine | Quantity | Unit | Model total | Sum of spaces | Abs diff | Band | Used/Missing/Excluded | Complete |"); + Line(builder, "| --- | --- | --- | --- | --- | --- | --- | --- | --- |"); + foreach (ReconciliationResult reconciliation in result.Reconciliations) + { + Line(builder, "| " + string.Join(" | ", new[] + { + EscapeInline(reconciliation.Engine), + EscapeInline(reconciliation.Key), + Format.Unit(reconciliation.Unit), + EscapeInline(Format.Number(reconciliation.ModelTotal)), + EscapeInline(Format.Number(reconciliation.SumOfSpaces)), + EscapeInline(Format.Number(reconciliation.AbsoluteDifference)), + Format.Band(reconciliation.Band), + reconciliation.ContributingSpaces.ToString(System.Globalization.CultureInfo.InvariantCulture) + + "/" + reconciliation.SpacesMissingValue.ToString(System.Globalization.CultureInfo.InvariantCulture) + + "/" + reconciliation.ExcludedSpaces.ToString(System.Globalization.CultureInfo.InvariantCulture), + reconciliation.Complete ? "yes" : "no" + }) + " |"); + } + + Line(builder, string.Empty); + } + + private static void AppendSpaceDiagnostics(StringBuilder builder, ComparisonResult result) + { + SpaceMatchDiagnostics diagnostics = result.SpaceDiagnostics; + Line(builder, "## Space alignment"); + Line(builder, string.Empty); + Line(builder, "| Field | Value |"); + Line(builder, "| --- | --- |"); + Line(builder, Row("TAS spaces", diagnostics.TasCount.ToString(System.Globalization.CultureInfo.InvariantCulture))); + Line(builder, Row("OpenStudio spaces", diagnostics.OpenStudioCount.ToString(System.Globalization.CultureInfo.InvariantCulture))); + Line(builder, Row("Matched by GUID", diagnostics.MatchedByGuid.ToString(System.Globalization.CultureInfo.InvariantCulture))); + Line(builder, Row("Matched by name", diagnostics.MatchedByName.ToString(System.Globalization.CultureInfo.InvariantCulture))); + Line(builder, string.Empty); + + AppendList(builder, "Only in TAS", diagnostics.OnlyInTas); + AppendList(builder, "Only in OpenStudio", diagnostics.OnlyInOpenStudio); + AppendList(builder, "Duplicate TAS GUIDs", diagnostics.DuplicateTasGuids); + AppendList(builder, "Duplicate OpenStudio GUIDs", diagnostics.DuplicateOpenStudioGuids); + AppendList(builder, "Duplicate TAS names", diagnostics.DuplicateTasNames); + AppendList(builder, "Duplicate OpenStudio names", diagnostics.DuplicateOpenStudioNames); + AppendList(builder, "Ambiguous (split) name matches", diagnostics.AmbiguousNameMatches); + } + + private static void AppendSpaceMetrics(StringBuilder builder, ComparisonResult result) + { + Line(builder, "## Space metrics"); + Line(builder, string.Empty); + if (result.Spaces.Count == 0) + { + Line(builder, "_No spaces._"); + Line(builder, string.Empty); + return; + } + + Line(builder, "| Space | Match | Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note |"); + Line(builder, "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |"); + foreach (SpaceComparison space in result.Spaces) + { + foreach (MetricComparison metric in space.Metrics) + { + Line(builder, "| " + string.Join(" | ", new[] + { + EscapeInline(space.Scope), + Format.MatchKind(space.MatchKind), + EscapeInline(metric.Key), + Format.Unit(metric.Unit), + EscapeInline(Format.Number(metric.TasValue)), + EscapeInline(Format.Number(metric.OpenStudioValue)), + EscapeInline(Format.Number(metric.AbsoluteDifference)), + EscapeInline(Format.Percent(metric.RelativeDifference)), + Format.Band(metric.Band), + EscapeInline(Format.MetricNote(metric)) + }) + " |"); + } + } + + Line(builder, string.Empty); + } + + private static void AppendMetricTableHeader(StringBuilder builder) + { + Line(builder, "| Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note |"); + Line(builder, "| --- | --- | --- | --- | --- | --- | --- | --- |"); + } + + private static void AppendMetricRow(StringBuilder builder, string label, MetricComparison metric) + { + Line(builder, "| " + string.Join(" | ", new[] + { + EscapeInline(label), + Format.Unit(metric.Unit), + EscapeInline(Format.Number(metric.TasValue)), + EscapeInline(Format.Number(metric.OpenStudioValue)), + EscapeInline(Format.Number(metric.AbsoluteDifference)), + EscapeInline(Format.Percent(metric.RelativeDifference)), + Format.Band(metric.Band), + EscapeInline(Format.MetricNote(metric)) + }) + " |"); + } + + private static void AppendList(StringBuilder builder, string title, IReadOnlyList values) + { + Line(builder, "**" + title + ":** " + (values.Count == 0 ? "none" : values.Count.ToString(System.Globalization.CultureInfo.InvariantCulture))); + foreach (string value in values) + { + Line(builder, "- " + EscapeInline(value)); + } + + Line(builder, string.Empty); + } + + private static string Count(int value) + { + return value.ToString(System.Globalization.CultureInfo.InvariantCulture); + } + + private static string Row(string label, string? value) + { + return "| " + EscapeInline(label) + " | " + EscapeInline(value ?? string.Empty) + " |"; + } + + private static string Row3(string label, string? tas, string? openStudio) + { + return "| " + EscapeInline(label) + " | " + EscapeInline(tas ?? string.Empty) + " | " + EscapeInline(openStudio ?? string.Empty) + " |"; + } + + private static void Line(StringBuilder builder, string text) + { + builder.Append(text); + builder.Append('\n'); + } + + private static string EscapeInline(string value) + { + // Keep table cells single-line and pipe-safe. + return value.Replace("\\", "\\\\").Replace("|", "\\|").Replace("\r", " ").Replace("\n", " "); + } + + private static string EngineKindToken(EngineKind? kind) + { + if (!kind.HasValue) + { + return string.Empty; + } + + switch (kind.Value) + { + case EngineKind.OpenStudio: return "OpenStudio"; + case EngineKind.Tas: return "TAS"; + default: return "Unknown"; + } + } + + private static string RouteToken(BenchmarkRoute? route) + { + if (!route.HasValue) + { + return string.Empty; + } + + switch (route.Value) + { + case BenchmarkRoute.NativeOpenStudio: return "Native-OpenStudio"; + case BenchmarkRoute.NativeTas: return "Native-TAS"; + case BenchmarkRoute.SharedGbXmlOpenStudio: return "SharedGbXML-OpenStudio"; + case BenchmarkRoute.SharedGbXmlTas: return "SharedGbXML-TAS"; + default: return "Unknown"; + } + } + + private static string DesignDayToken(DesignDaySource? source) + { + if (!source.HasValue) + { + return string.Empty; + } + + switch (source.Value) + { + case DesignDaySource.Ddy: return "DDY"; + case DesignDaySource.EmbeddedModel: return "EmbeddedModel"; + case DesignDaySource.None: return "None"; + default: return "Unknown"; + } + } + + private static string RunStateToken(RunState? state) + { + if (!state.HasValue) + { + return string.Empty; + } + + switch (state.Value) + { + case RunState.Success: return "Success"; + case RunState.Failure: return "Failure"; + default: return "Unknown"; + } + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Modify/WriteReports.cs b/SAM.Analytical.Benchmark.Compare/Modify/WriteReports.cs new file mode 100644 index 0000000..ac9ab15 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Modify/WriteReports.cs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Modify + { + /// The Markdown report file name written into the output directory. + public const string MarkdownFileName = "comparison.md"; + + /// The CSV report file name written into the output directory. + public const string CsvFileName = "comparison.csv"; + + /// The machine-readable summary file name written into the output directory. + public const string SummaryFileName = "comparison-summary.json"; + + private static readonly UTF8Encoding Utf8WithoutBom = new UTF8Encoding(false, true); + + /// The report text encoded as UTF-8 without a byte order mark (LF newlines preserved). + public static byte[] ToUtf8(string text) + { + if (text == null) + { + throw new ArgumentNullException(nameof(text)); + } + + return Utf8WithoutBom.GetBytes(text); + } + + /// + /// Writes all three reports (comparison.md, comparison.csv, + /// comparison-summary.json) into , creating it if needed, + /// as UTF-8 without BOM and LF newlines. Returns the written file paths in write order. + /// + public static IReadOnlyList WriteReports(ComparisonResult result, string outputDirectory) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + if (string.IsNullOrWhiteSpace(outputDirectory)) + { + throw new ArgumentException("An output directory is required.", nameof(outputDirectory)); + } + + string fullDirectory = Path.GetFullPath(outputDirectory); + if (File.Exists(fullDirectory)) + { + throw new IOException("The output path identifies a file, not a directory: " + fullDirectory); + } + + Directory.CreateDirectory(fullDirectory); + + string markdownPath = Path.Combine(fullDirectory, MarkdownFileName); + string csvPath = Path.Combine(fullDirectory, CsvFileName); + string summaryPath = Path.Combine(fullDirectory, SummaryFileName); + + File.WriteAllBytes(markdownPath, ToUtf8(WriteMarkdown(result))); + File.WriteAllBytes(csvPath, ToUtf8(WriteCsv(result))); + File.WriteAllBytes(summaryPath, ToUtf8(WriteSummaryJson(result))); + + return new[] { markdownPath, csvPath, summaryPath }; + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Modify/WriteSummaryJson.cs b/SAM.Analytical.Benchmark.Compare/Modify/WriteSummaryJson.cs new file mode 100644 index 0000000..e40902d --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Modify/WriteSummaryJson.cs @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Modify + { + /// The version of the comparator-owned comparison-summary document. + public const string SummarySchemaVersion = "1.0.0"; + + private static readonly JsonWriterOptions SummaryWriterOptions = new JsonWriterOptions + { + Indented = true, + Encoder = JavaScriptEncoder.Default, + SkipValidation = false + }; + + /// + /// Renders comparison-summary.json: the comparator-owned, versioned machine-readable + /// summary. Property order is fixed in code and numbers are written at full binary64 precision, so + /// the output is deterministic and byte-stable (LF newlines, UTF-8 without BOM when written). + /// + public static string WriteSummaryJson(ComparisonResult result) + { + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + using var stream = new MemoryStream(); + using (var writer = new Utf8JsonWriter(stream, SummaryWriterOptions)) + { + writer.WriteStartObject(); + writer.WriteString("summarySchemaVersion", SummarySchemaVersion); + writer.WriteString("toleranceProfile", result.ToleranceProfileName); + WriteToleranceProfile(writer, result.ToleranceProfile); + writer.WriteString("gate", Format.Gate(result.Gate)); + + writer.WriteStartObject("statuses"); + writer.WriteString("overall", Format.Gate(result.Gate)); + writer.WriteString("numerical", Format.Gate(result.NumericalStatus)); + writer.WriteString("coverage", Format.Gate(result.CoverageStatus)); + writer.WriteString("provenance", Format.Gate(result.ProvenanceStatus)); + writer.WriteString("reconciliation", Format.Gate(result.ReconciliationStatus)); + writer.WriteEndObject(); + + WriteProvenanceCompatibility(writer, result.ProvenanceCompatibility); + + writer.WriteStartObject("counts"); + writer.WriteNumber("match", result.MatchCount); + writer.WriteNumber("warn", result.WarnCount); + writer.WriteNumber("fail", result.FailCount); + writer.WriteNumber("notApplicable", result.NotApplicableCount); + writer.WriteEndObject(); + + WriteCoverage(writer, result); + + writer.WriteStartObject("schema"); + WriteStringOrNull(writer, "tas", result.TasSchemaVersion); + WriteStringOrNull(writer, "openStudio", result.OpenStudioSchemaVersion); + WriteStringOrNull(writer, "driftNote", result.SchemaDriftNote); + writer.WriteEndObject(); + + writer.WriteStartObject("provenance"); + WriteProvenance(writer, "tas", result.TasProvenance); + WriteProvenance(writer, "openStudio", result.OpenStudioProvenance); + writer.WriteEndObject(); + + writer.WriteStartArray("modelMetrics"); + foreach (MetricComparison metric in result.ModelMetrics) + { + WriteMetric(writer, metric); + } + + writer.WriteEndArray(); + + writer.WriteStartArray("spaces"); + foreach (SpaceComparison space in result.Spaces) + { + WriteSpace(writer, space); + } + + writer.WriteEndArray(); + + WriteDiagnostics(writer, result.SpaceDiagnostics); + + writer.WriteStartArray("reconciliation"); + foreach (ReconciliationResult reconciliation in result.Reconciliations) + { + WriteReconciliation(writer, reconciliation); + } + + writer.WriteEndArray(); + + writer.WriteEndObject(); + } + + string json = Encoding.UTF8.GetString(stream.ToArray()).Replace("\r\n", "\n").Replace('\r', '\n'); + return json + "\n"; + } + + private static void WriteSpace(Utf8JsonWriter writer, SpaceComparison space) + { + writer.WriteStartObject(); + writer.WriteString("scope", space.Scope); + WriteStringOrNull(writer, "guid", space.Guid); + WriteStringOrNull(writer, "tasName", space.TasName); + WriteStringOrNull(writer, "openStudioName", space.OpenStudioName); + writer.WriteString("matchKind", Format.MatchKind(space.MatchKind)); + writer.WriteStartArray("metrics"); + foreach (MetricComparison metric in space.Metrics) + { + WriteMetric(writer, metric); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); + } + + private static void WriteMetric(Utf8JsonWriter writer, MetricComparison metric) + { + writer.WriteStartObject(); + writer.WriteString("scope", metric.Scope); + writer.WriteString("key", metric.Key); + writer.WriteString("unit", Format.Unit(metric.Unit)); + writer.WriteString("availability", Format.Availability(metric.Availability)); + writer.WriteString("band", Format.Band(metric.Band)); + // Explicit so a downstream consumer never has to infer WHY a Fail band did not move the gate. + writer.WriteBoolean("informational", Query.IsInformationalForNumericalGate(metric)); + WriteStringOrNull(writer, "notApplicableReason", NullIfEmpty(Format.NotApplicableReason(metric.NotApplicableReason))); + writer.WriteBoolean("circular", metric.Circular); + + WriteSide(writer, "tas", metric.TasValue, metric.TasAvailable, metric.TasUnit); + WriteSide(writer, "openStudio", metric.OpenStudioValue, metric.OpenStudioAvailable, metric.OpenStudioUnit); + + WriteNumberOrNull(writer, "absoluteDifference", metric.AbsoluteDifference); + WriteNumberOrNull(writer, "relativeDifference", metric.RelativeDifference); + WriteNumberOrNull(writer, "signedDifference", metric.SignedDifference); + writer.WriteEndObject(); + } + + private static void WriteSide(Utf8JsonWriter writer, string name, double? value, bool available, MetricUnit unit) + { + writer.WriteStartObject(name); + WriteNumberOrNull(writer, "value", value); + writer.WriteBoolean("available", available); + writer.WriteString("unit", Format.Unit(unit)); + writer.WriteEndObject(); + } + + private static void WriteReconciliation(Utf8JsonWriter writer, ReconciliationResult reconciliation) + { + writer.WriteStartObject(); + writer.WriteString("engine", reconciliation.Engine); + writer.WriteString("key", reconciliation.Key); + writer.WriteString("unit", Format.Unit(reconciliation.Unit)); + WriteNumberOrNull(writer, "modelTotal", reconciliation.ModelTotal); + writer.WriteBoolean("modelTotalAvailable", reconciliation.ModelTotalAvailable); + writer.WriteNumber("sumOfSpaces", reconciliation.SumOfSpaces); + writer.WriteNumber("contributingSpaces", reconciliation.ContributingSpaces); + writer.WriteNumber("spacesMissingValue", reconciliation.SpacesMissingValue); + writer.WriteNumber("excludedSpaces", reconciliation.ExcludedSpaces); + writer.WriteBoolean("complete", reconciliation.Complete); + WriteNumberOrNull(writer, "absoluteDifference", reconciliation.AbsoluteDifference); + WriteNumberOrNull(writer, "relativeDifference", reconciliation.RelativeDifference); + writer.WriteString("band", Format.Band(reconciliation.Band)); + WriteStringOrNull(writer, "notApplicableReason", NullIfEmpty(Format.NotApplicableReason(reconciliation.NotApplicableReason))); + writer.WriteEndObject(); + } + + private static void WriteProvenanceCompatibility(Utf8JsonWriter writer, ProvenanceCompatibility compatibility) + { + writer.WriteStartObject("provenanceCompatibility"); + writer.WriteBoolean("compatible", compatibility.IsCompatible); + writer.WriteStartArray("mismatches"); + foreach (ProvenanceMismatch mismatch in compatibility.Mismatches) + { + writer.WriteStartObject(); + writer.WriteString("field", mismatch.Field); + WriteStringOrNull(writer, "tas", mismatch.Tas); + WriteStringOrNull(writer, "openStudio", mismatch.OpenStudio); + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); + } + + private static void WriteToleranceProfile(Utf8JsonWriter writer, ToleranceProfile profile) + { + // TOLERANCES.md: reports must include the actual profile values, so a changed profile cannot be + // hidden behind an unchanged name. + writer.WriteStartObject("toleranceProfileDetail"); + writer.WriteString("name", profile.Name); + writer.WriteNumber("warnRelative", profile.WarnRelative); + writer.WriteNumber("failRelative", profile.FailRelative); + writer.WriteNumber("hourWarnAbsolute", profile.HourWarnAbsolute); + writer.WriteNumber("hourFailAbsolute", profile.HourFailAbsolute); + writer.WriteNumber("defaultNearZeroFloor", profile.DefaultNearZeroFloor); + writer.WriteStartObject("nearZeroFloors"); + foreach (System.Collections.Generic.KeyValuePair floor in profile.ConfiguredNearZeroFloors()) + { + writer.WriteNumber(Format.Unit(floor.Key), floor.Value); + } + + writer.WriteEndObject(); + writer.WriteEndObject(); + } + + private static void WriteCoverage(Utf8JsonWriter writer, ComparisonResult result) + { + // TOLERANCES.md: coverage diagnostics count unavailable required metrics and unmatched spaces + // separately, so a numerically passing report with poor coverage cannot be read as complete. + // The counts cover the whole-model metrics and uniquely matched spaces only — one-sided and + // ambiguous spaces are reported by spaceDiagnostics instead of being counted twice here. + SpaceMatchDiagnostics diagnostics = result.SpaceDiagnostics; + writer.WriteStartObject("coverage"); + writer.WriteString("status", Format.Gate(result.CoverageStatus)); + writer.WriteNumber("requiredMetricCount", result.RequiredMetricCount); + writer.WriteNumber("comparableMetricCount", result.ComparableMetricCount); + writer.WriteNumber("unavailableRequiredMetricCount", result.UnavailableRequiredMetricCount); + writer.WriteNumber("unmatchedSpaceCount", diagnostics.OnlyInTas.Count + diagnostics.OnlyInOpenStudio.Count); + writer.WriteNumber("ambiguousSpaceCount", diagnostics.AmbiguousNameMatches.Count); + writer.WriteNumber("duplicateSpaceIdentityCount", + diagnostics.DuplicateTasGuids.Count + + diagnostics.DuplicateOpenStudioGuids.Count + + diagnostics.DuplicateTasNames.Count + + diagnostics.DuplicateOpenStudioNames.Count); + writer.WriteEndObject(); + } + + private static void WriteDiagnostics(Utf8JsonWriter writer, SpaceMatchDiagnostics diagnostics) + { + writer.WriteStartObject("spaceDiagnostics"); + writer.WriteNumber("tasCount", diagnostics.TasCount); + writer.WriteNumber("openStudioCount", diagnostics.OpenStudioCount); + writer.WriteNumber("matchedByGuid", diagnostics.MatchedByGuid); + writer.WriteNumber("matchedByName", diagnostics.MatchedByName); + WriteStringArray(writer, "onlyInTas", diagnostics.OnlyInTas); + WriteStringArray(writer, "onlyInOpenStudio", diagnostics.OnlyInOpenStudio); + WriteStringArray(writer, "duplicateTasGuids", diagnostics.DuplicateTasGuids); + WriteStringArray(writer, "duplicateOpenStudioGuids", diagnostics.DuplicateOpenStudioGuids); + WriteStringArray(writer, "duplicateTasNames", diagnostics.DuplicateTasNames); + WriteStringArray(writer, "duplicateOpenStudioNames", diagnostics.DuplicateOpenStudioNames); + WriteStringArray(writer, "ambiguousNameMatches", diagnostics.AmbiguousNameMatches); + writer.WriteEndObject(); + } + + private static void WriteProvenance(Utf8JsonWriter writer, string name, BenchmarkProvenance? provenance) + { + if (provenance == null) + { + writer.WriteNull(name); + return; + } + + writer.WriteStartObject(name); + WriteStringOrNull(writer, "sourceModelName", provenance.SourceModelName); + WriteStringOrNull(writer, "sourceModelGuid", provenance.SourceModelGuid); + WriteStringOrNull(writer, "sourceFileHash", provenance.SourceFileHash); + WriteStringOrNull(writer, "canonicalModelHash", provenance.CanonicalModelHash); + WriteStringOrNull(writer, "canonicalizationVersion", provenance.CanonicalizationVersion); + WriteStringOrNull(writer, "samCommit", provenance.SamCommit); + WriteStringOrNull(writer, "runnerCommit", provenance.RunnerCommit); + + writer.WriteStartObject("engine"); + WriteStringOrNull(writer, "kind", NullIfEmpty(EngineKindToken(provenance.Engine?.Kind))); + WriteStringOrNull(writer, "name", provenance.Engine?.Name); + WriteStringOrNull(writer, "version", provenance.Engine?.Version); + WriteStringOrNull(writer, "sdkVersion", provenance.Engine?.SdkVersion); + writer.WriteEndObject(); + + writer.WriteString("route", RouteToken(provenance.Route)); + + writer.WriteStartObject("weather"); + WriteStringOrNull(writer, "identity", provenance.Weather?.Identity); + WriteStringOrNull(writer, "hash", provenance.Weather?.Hash); + writer.WriteEndObject(); + + writer.WriteString("designDaySource", DesignDayToken(provenance.DesignDaySource)); + writer.WriteString("runTimestampUtc", provenance.RunTimestampUtc.UtcDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.FFFFFFF'Z'", CultureInfo.InvariantCulture)); + WriteNumberOrNull(writer, "durationSeconds", provenance.DurationSeconds); + writer.WriteString("state", RunStateToken(provenance.State)); + WriteStringArray(writer, "resultSources", provenance.ResultSources); + WriteStringArray(writer, "warnings", provenance.Warnings); + WriteStringArray(writer, "notes", provenance.Notes); + writer.WriteEndObject(); + } + + private static void WriteStringArray(Utf8JsonWriter writer, string name, IReadOnlyList? values) + { + writer.WriteStartArray(name); + if (values != null) + { + foreach (string value in values) + { + writer.WriteStringValue(value); + } + } + + writer.WriteEndArray(); + } + + private static void WriteStringOrNull(Utf8JsonWriter writer, string name, string? value) + { + if (value == null) + { + writer.WriteNull(name); + } + else + { + writer.WriteString(name, value); + } + } + + private static void WriteNumberOrNull(Utf8JsonWriter writer, string name, double? value) + { + if (value.HasValue) + { + writer.WriteNumber(name, value.Value); + } + else + { + writer.WriteNull(name); + } + } + + private static string? NullIfEmpty(string value) + { + return string.IsNullOrEmpty(value) ? null : value; + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Program.cs b/SAM.Analytical.Benchmark.Compare/Program.cs new file mode 100644 index 0000000..ab02591 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Program.cs @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.IO; +using SAM.Analytical.Benchmark; + +namespace SAM.Analytical.Benchmark.Compare +{ + /// + /// benchmark-compare: the B3 headless comparator. Loads two engine-neutral benchmark + /// documents (one per engine), checks their schema compatibility, aligns and diffs them against a + /// configurable , and writes comparison.md, + /// comparison.csv and comparison-summary.json into an output directory. + /// + /// The comparator is independent of both engines — it references only the neutral schema library. + /// Argument parsing, invariant culture and exception mapping are delegated to the shared benchmark + /// CLI host so the exit-code contract matches every other benchmark tool: 0 success, 2 + /// usage, 3 input/IO/serialization, 4 validation/schema-incompatible, 5 internal + /// failure. A tolerance gate is NOT a failure by default: the reports are always written and the gate + /// outcome is recorded in the summary. Only --fail-on-gate makes a warn/fail gate return the + /// dedicated exit code 1. + /// + /// + public static class Program + { + /// The dedicated exit code used when a gate outcome trips --fail-on-gate. + public const int GateFailureExitCode = 1; + + private static readonly string[] RequiredOptions = { "tas", "openstudio", "out" }; + + private static readonly HashSet KnownOptions = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "tas", "openstudio", "out", "tolerance-profile", "fail-on-gate" + }; + + public static int Main(string[] args) + { + return Run(args, Console.Out, Console.Error); + } + + /// + /// Testable entry point: the shared host drives parsing, culture, help and exception mapping; + /// carries the comparator logic. Writers are injectable so CLI tests can + /// capture the output without touching the process . + /// + internal static int Run(string[] args, TextWriter standardOutput, TextWriter standardError) + { + return BenchmarkCliHost.Run( + args, + Usage, + RequiredOptions, + (arguments, _) => Execute(arguments, standardOutput), + standardOutput, + standardError); + } + + private static int Execute(BenchmarkArguments arguments, TextWriter standardOutput) + { + // Reject unknown/misspelled options up front. Otherwise a typo such as `--fail-on-gtae warn` + // would be silently ignored and a Fail gate could still exit 0, disabling the caller's intended + // enforcement. An ArgumentException is mapped to the usage exit code by the shared host. + RejectUnknownOptions(arguments); + + string tasPath = BenchmarkCliPaths.ValidateInputFile(arguments.RequireOption("tas")); + string openStudioPath = BenchmarkCliPaths.ValidateInputFile(arguments.RequireOption("openstudio")); + string outputDirectory = arguments.RequireOption("out"); + + // Unknown profile names surface as an ArgumentException, which the host maps to a usage error (2). + ToleranceProfile profile = ToleranceProfile.Resolve(arguments.GetOption("tolerance-profile")); + FailOnGate failOnGate = ParseFailOnGate(arguments.GetOption("fail-on-gate")); + + // BenchmarkSerializer.Read validates each document individually: a missing/unparseable file is a + // JsonException (exit 3), an individually-invalid document a BenchmarkValidationException (exit 4). + // Query.Compare re-validates and additionally rejects contract errors (e.g. a wrong unit), which + // the shared host maps to the validation exit code. + BenchmarkDocument tas = BenchmarkSerializer.Read(tasPath); + BenchmarkDocument openStudio = BenchmarkSerializer.Read(openStudioPath); + + ComparisonResult result = Query.Compare(tas, openStudio, profile); + + IReadOnlyList written = Modify.WriteReports(result, outputDirectory); + foreach (string path in written) + { + standardOutput.WriteLine("Wrote " + path); + } + + standardOutput.WriteLine( + "Gate=" + Format.Gate(result.Gate) + + " match=" + result.MatchCount + + " warn=" + result.WarnCount + + " fail=" + result.FailCount + + " na=" + result.NotApplicableCount); + + if (GateTrips(result.Gate, failOnGate)) + { + standardOutput.WriteLine("Gate failed under --fail-on-gate=" + failOnGate.ToString().ToLowerInvariant() + "."); + return GateFailureExitCode; + } + + return (int)BenchmarkExitCode.Success; + } + + private static void RejectUnknownOptions(BenchmarkArguments arguments) + { + foreach (string option in arguments.Options.Keys) + { + if (!KnownOptions.Contains(option)) + { + throw new ArgumentException($"Unknown option '--{option}'."); + } + } + } + + private static bool GateTrips(GateStatus gate, FailOnGate failOnGate) + { + switch (failOnGate) + { + case FailOnGate.Fail: + return gate == GateStatus.Fail; + case FailOnGate.Warn: + return gate == GateStatus.Fail || gate == GateStatus.Warn; + default: + return false; + } + } + + private static FailOnGate ParseFailOnGate(string? value) + { + if (string.IsNullOrWhiteSpace(value) || string.Equals(value, "never", StringComparison.OrdinalIgnoreCase)) + { + return FailOnGate.Never; + } + + if (string.Equals(value, "warn", StringComparison.OrdinalIgnoreCase)) + { + return FailOnGate.Warn; + } + + if (string.Equals(value, "fail", StringComparison.OrdinalIgnoreCase)) + { + return FailOnGate.Fail; + } + + throw new ArgumentException("Option '--fail-on-gate' must be one of 'never', 'warn' or 'fail'."); + } + + private enum FailOnGate + { + Never = 0, + Warn, + Fail + } + + private const string Usage = + "Usage: benchmark-compare --tas --openstudio --out \n" + + " [--tolerance-profile ] [--fail-on-gate ]\n" + + "\n" + + "Writes comparison.md, comparison.csv and comparison-summary.json into the output directory.\n" + + "\n" + + "Exit codes: 0 success, 1 gate failed (only with --fail-on-gate), 2 usage error,\n" + + " 3 input/IO/serialization error, 4 validation/schema-incompatible, 5 internal failure."; + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Query/AlignSpaces.cs b/SAM.Analytical.Benchmark.Compare/Query/AlignSpaces.cs new file mode 100644 index 0000000..a6141dc --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Query/AlignSpaces.cs @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Query + { + /// + /// Aligns the two documents' spaces GUID-first then Name-fallback, mirroring + /// AddResults.LookupSpace, but never silently combining conflicting or ambiguous identities: + /// + /// a GUID present exactly once on BOTH sides is matched by GUID; + /// a GUID duplicated on either side excludes EVERY space carrying it from automatic matching + /// (it is not uniquely identifiable); + /// name fallback runs only for spaces still unmatched AND only when at least one side of the + /// pair lacks a usable GUID — two spaces with different valid GUIDs are explicit distinct identities + /// and are never merged by name; + /// a name that maps to several candidates (a "split"), or to two spaces that both carry + /// different GUIDs, is reported as ambiguous rather than matched. + /// + /// The result is deterministic: GUID matches (ordered by GUID) then name matches (ordered by + /// normalized name), and the one-sided lists are explicitly sorted. + /// + public static SpaceAlignment AlignSpaces( + IReadOnlyList? tasSpaces, + IReadOnlyList? openStudioSpaces) + { + List tas = (tasSpaces ?? Array.Empty()).Where(space => space != null).ToList(); + List openStudio = (openStudioSpaces ?? Array.Empty()).Where(space => space != null).ToList(); + + Dictionary> tasByGuid = GroupByGuid(tas); + Dictionary> openStudioByGuid = GroupByGuid(openStudio); + + List duplicateTasGuids = tasByGuid.Where(x => x.Value.Count > 1).Select(x => x.Key).OrderBy(x => x, StringComparer.Ordinal).ToList(); + List duplicateOpenStudioGuids = openStudioByGuid.Where(x => x.Value.Count > 1).Select(x => x.Key).OrderBy(x => x, StringComparer.Ordinal).ToList(); + + // A duplicated GUID cannot uniquely identify a space, so every member of that GUID group is + // excluded from matching entirely (neither GUID nor name fallback may claim it). + var poisoned = new HashSet(); + foreach (List group in tasByGuid.Values.Where(g => g.Count > 1).Concat(openStudioByGuid.Values.Where(g => g.Count > 1))) + { + foreach (BenchmarkSpaceResult space in group) + { + poisoned.Add(space); + } + } + + var matchedTas = new HashSet(); + var matchedOpenStudio = new HashSet(); + var pairs = new List(); + + // Pass 1: GUIDs present exactly once on both sides, ordered deterministically by GUID. + foreach (string guid in tasByGuid.Where(x => x.Value.Count == 1).Select(x => x.Key).OrderBy(x => x, StringComparer.Ordinal)) + { + if (openStudioByGuid.TryGetValue(guid, out List? openStudioGroup) && openStudioGroup.Count == 1) + { + BenchmarkSpaceResult tasSpace = tasByGuid[guid][0]; + BenchmarkSpaceResult openStudioSpace = openStudioGroup[0]; + pairs.Add(new SpacePair(guid, tasSpace, openStudioSpace, SpaceMatchKind.Guid)); + matchedTas.Add(tasSpace); + matchedOpenStudio.Add(openStudioSpace); + } + } + + // Pass 2: name fallback over spaces still unmatched and not poisoned. + List tasNameCandidates = tas.Where(space => !matchedTas.Contains(space) && !poisoned.Contains(space)).ToList(); + List openStudioNameCandidates = openStudio.Where(space => !matchedOpenStudio.Contains(space) && !poisoned.Contains(space)).ToList(); + + Dictionary> tasByName = GroupByName(tasNameCandidates); + Dictionary> openStudioByName = GroupByName(openStudioNameCandidates); + + List duplicateTasNames = tasByName.Where(x => x.Value.Count > 1).Select(x => x.Key).OrderBy(x => x, StringComparer.Ordinal).ToList(); + List duplicateOpenStudioNames = openStudioByName.Where(x => x.Value.Count > 1).Select(x => x.Key).OrderBy(x => x, StringComparer.Ordinal).ToList(); + + var ambiguousNameMatches = new List(); + foreach (string name in tasByName.Keys.OrderBy(x => x, StringComparer.Ordinal)) + { + if (!openStudioByName.TryGetValue(name, out List? openStudioGroup)) + { + continue; + } + + List tasGroup = tasByName[name]; + if (tasGroup.Count != 1 || openStudioGroup.Count != 1) + { + // A one-to-many or many-to-many name relationship (a split space) cannot be aligned safely. + ambiguousNameMatches.Add(name); + continue; + } + + BenchmarkSpaceResult tasSpace = tasGroup[0]; + BenchmarkSpaceResult openStudioSpace = openStudioGroup[0]; + if (HasUsableGuid(tasSpace) && HasUsableGuid(openStudioSpace)) + { + // Both sides carry different valid GUIDs: these are explicit, conflicting identities and + // must never be merged by a shared name. + ambiguousNameMatches.Add(name); + continue; + } + + string? guid = HasUsableGuid(tasSpace) ? tasSpace.Guid : openStudioSpace.Guid; + pairs.Add(new SpacePair(guid, tasSpace, openStudioSpace, SpaceMatchKind.Name)); + matchedTas.Add(tasSpace); + matchedOpenStudio.Add(openStudioSpace); + } + + List onlyTas = tas.Where(space => !matchedTas.Contains(space)).OrderBy(x => x, SpaceOrder).ToList(); + List onlyOpenStudio = openStudio.Where(space => !matchedOpenStudio.Contains(space)).OrderBy(x => x, SpaceOrder).ToList(); + + var diagnostics = new SpaceMatchDiagnostics( + tasCount: tas.Count, + openStudioCount: openStudio.Count, + matchedByGuid: pairs.Count(pair => pair.MatchKind == SpaceMatchKind.Guid), + matchedByName: pairs.Count(pair => pair.MatchKind == SpaceMatchKind.Name), + onlyInTas: onlyTas.Select(Identity).ToList(), + onlyInOpenStudio: onlyOpenStudio.Select(Identity).ToList(), + duplicateTasGuids: duplicateTasGuids, + duplicateOpenStudioGuids: duplicateOpenStudioGuids, + duplicateTasNames: duplicateTasNames, + duplicateOpenStudioNames: duplicateOpenStudioNames, + ambiguousNameMatches: ambiguousNameMatches.OrderBy(x => x, StringComparer.Ordinal).ToList()); + + return new SpaceAlignment(pairs, onlyTas, onlyOpenStudio, diagnostics); + } + + private static readonly IComparer SpaceOrder = Comparer.Create((left, right) => + { + int byGuid = string.CompareOrdinal(GuidKey(left), GuidKey(right)); + if (byGuid != 0) + { + return byGuid; + } + + int byNormalized = string.CompareOrdinal(NormalizeName(left.Name ?? string.Empty), NormalizeName(right.Name ?? string.Empty)); + return byNormalized != 0 ? byNormalized : string.CompareOrdinal(left.Name ?? string.Empty, right.Name ?? string.Empty); + }); + + private static string GuidKey(BenchmarkSpaceResult space) + { + // GUID-less spaces sort after all GUID'd spaces (U+FFFF is above any hex GUID character). + return HasUsableGuid(space) ? space.Guid! : "￿"; + } + + private static bool HasUsableGuid(BenchmarkSpaceResult space) + { + return !string.IsNullOrEmpty(space.Guid); + } + + private static Dictionary> GroupByGuid(IEnumerable spaces) + { + var groups = new Dictionary>(StringComparer.Ordinal); + foreach (BenchmarkSpaceResult space in spaces) + { + if (!HasUsableGuid(space)) + { + continue; + } + + if (!groups.TryGetValue(space.Guid!, out List? bucket)) + { + bucket = new List(); + groups[space.Guid!] = bucket; + } + + bucket.Add(space); + } + + return groups; + } + + private static Dictionary> GroupByName(IEnumerable spaces) + { + var groups = new Dictionary>(StringComparer.Ordinal); + foreach (BenchmarkSpaceResult space in spaces) + { + if (string.IsNullOrWhiteSpace(space.Name)) + { + continue; + } + + string key = NormalizeName(space.Name!); + if (!groups.TryGetValue(key, out List? bucket)) + { + bucket = new List(); + groups[key] = bucket; + } + + bucket.Add(space); + } + + return groups; + } + + private static string Identity(BenchmarkSpaceResult space) + { + if (HasUsableGuid(space)) + { + return space.Guid!; + } + + return string.IsNullOrWhiteSpace(space.Name) ? "(unnamed)" : space.Name!; + } + + /// + /// The space-name normalization used for the name fallback, identical to the schema validator's + /// (Trim().Normalize().ToUpperInvariant()) so the comparator and producers agree. + /// + internal static string NormalizeName(string value) + { + return value.Trim().Normalize().ToUpperInvariant(); + } + } + + /// A GUID- or name-matched pair of spaces from the two documents. + public sealed class SpacePair + { + public SpacePair(string? guid, BenchmarkSpaceResult tas, BenchmarkSpaceResult openStudio, SpaceMatchKind matchKind) + { + Guid = guid; + Tas = tas; + OpenStudio = openStudio; + MatchKind = matchKind; + } + + public string? Guid { get; } + + public BenchmarkSpaceResult Tas { get; } + + public BenchmarkSpaceResult OpenStudio { get; } + + public SpaceMatchKind MatchKind { get; } + } + + /// The outcome of : matched pairs, one-sided spaces and diagnostics. + public sealed class SpaceAlignment + { + public SpaceAlignment( + IReadOnlyList pairs, + IReadOnlyList onlyInTas, + IReadOnlyList onlyInOpenStudio, + SpaceMatchDiagnostics diagnostics) + { + Pairs = pairs; + OnlyInTas = onlyInTas; + OnlyInOpenStudio = onlyInOpenStudio; + Diagnostics = diagnostics; + } + + public IReadOnlyList Pairs { get; } + + public IReadOnlyList OnlyInTas { get; } + + public IReadOnlyList OnlyInOpenStudio { get; } + + public SpaceMatchDiagnostics Diagnostics { get; } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Query/Band.cs b/SAM.Analytical.Benchmark.Compare/Query/Band.cs new file mode 100644 index 0000000..491fc23 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Query/Band.cs @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Query + { + /// The number of hours in a non-leap year, used to wrap circular hour-of-year differences. + public const int HoursInYear = 8760; + + /// + /// Assigns a tolerance band to a single metric. Availability wins first: if either side is + /// unavailable the band is (never a failure). Available + /// pairs with mismatched units are also N/A. Otherwise the relative difference against the + /// floor-bounded scale max(|tas|,|os|,floor(unit)) is banded; hour-of-year metrics use the + /// circular difference and the profile's absolute hour limits. + /// + public static MetricComparison Band(string scope, string key, MetricValue? tas, MetricValue? openStudio, ToleranceProfile profile) + { + if (profile == null) + { + throw new ArgumentNullException(nameof(profile)); + } + + MetricValue tasMetric = tas ?? MetricValue.Unavailable(MetricUnit.Unknown); + MetricValue openStudioMetric = openStudio ?? MetricValue.Unavailable(MetricUnit.Unknown); + + bool tasAvailable = tasMetric.Available && tasMetric.Value.HasValue; + bool openStudioAvailable = openStudioMetric.Available && openStudioMetric.Value.HasValue; + + MetricUnit tasUnit = tasMetric.Unit; + MetricUnit openStudioUnit = openStudioMetric.Unit; + + // Prefer an available side's unit as the canonical reporting unit; fall back to whichever is set. + MetricUnit unit = tasAvailable ? tasUnit + : openStudioAvailable ? openStudioUnit + : tasUnit != MetricUnit.Unknown ? tasUnit : openStudioUnit; + + if (!tasAvailable || !openStudioAvailable) + { + return new MetricComparison( + scope, key, unit, + tasAvailable ? tasMetric.Value : null, tasAvailable, + openStudioAvailable ? openStudioMetric.Value : null, openStudioAvailable, + circular: false, + absoluteDifference: null, + relativeDifference: null, + signedDifference: null, + band: ComparisonBand.NotApplicable, + notApplicableReason: NotApplicableReason.Unavailable, + tasUnit: tasUnit, + openStudioUnit: openStudioUnit); + } + + if (tasUnit != openStudioUnit) + { + // Both sides carry a value but in different units: comparing them would be meaningless. + return new MetricComparison( + scope, key, unit, + tasMetric.Value, true, + openStudioMetric.Value, true, + circular: false, + absoluteDifference: null, + relativeDifference: null, + signedDifference: null, + band: ComparisonBand.NotApplicable, + notApplicableReason: NotApplicableReason.UnitMismatch, + tasUnit: tasUnit, + openStudioUnit: openStudioUnit); + } + + double tasValue = tasMetric.Value!.Value; + double openStudioValue = openStudioMetric.Value!.Value; + bool circular = unit == MetricUnit.HourOfYear; + + double absolute; + double? signed; + double? relative; + ComparisonBand band; + + if (circular) + { + // TOLERANCES.md: percentage bands do not apply to hour-of-year, so the band comes from the + // profile's ABSOLUTE warn/fail hour thresholds applied to the circular difference. Both + // thresholds are honoured here so a 100h gap is not reported identically to a 10h gap. That + // peak-hour differences are INFORMATIONAL is implemented where it belongs — hour-of-year + // metrics are excluded from the numerical gate (see NumericalStatus) — rather than by + // suppressing the reporting band. + absolute = CircularHourDiff(tasValue, openStudioValue); + signed = null; // A circular difference has no single meaningful sign across the year boundary. + relative = null; + band = absolute <= profile.HourWarnAbsolute ? ComparisonBand.Match + : absolute <= profile.HourFailAbsolute ? ComparisonBand.Warn + : ComparisonBand.Fail; + } + else + { + // TOLERANCES.md difference calculation: the near-zero floor is the lower bound of the SCALE + // (the denominator), not an additive allowance. scale = max(|tas|, |os|, floor); the band is + // taken from the relative difference against that scale. This keeps a tiny denominator from + // exploding into an extreme percentage, WITHOUT masking a genuine near-zero disagreement + // (e.g. 0 vs floor is a 100% difference, not a match). + signed = openStudioValue - tasValue; + absolute = Math.Abs(signed.Value); + double floor = profile.NearZeroFloor(unit); + double scale = Math.Max(Math.Max(Math.Abs(tasValue), Math.Abs(openStudioValue)), floor); + relative = scale > 0 ? absolute / scale : 0d; + band = Classify(relative.Value, profile); + } + + return new MetricComparison( + scope, key, unit, + tasValue, true, + openStudioValue, true, + circular, + absolute, + relative, + signed, + band, + NotApplicableReason.None, + tasUnit, + openStudioUnit); + } + + /// + /// Maps a relative difference to a band using the profile's boundaries: below the warn boundary is + /// a match, at least warn but below fail warns, at least fail fails (TOLERANCES.md: Pass < 5%, + /// 5% <= Warn < 15%, Fail >= 15%). + /// + private static ComparisonBand Classify(double relative, ToleranceProfile profile) + { + if (relative < profile.WarnRelative) + { + return ComparisonBand.Match; + } + + return relative < profile.FailRelative ? ComparisonBand.Warn : ComparisonBand.Fail; + } + + /// + /// The circular (year-boundary-wrapping) distance between two hour-of-year values in [0, 8759]: + /// min(|a-b|, 8760-|a-b|), so hour 8759 and hour 0 are one hour apart, not 8759. + /// + public static double CircularHourDiff(double a, double b) + { + double direct = Math.Abs(a - b); + double wrapped = HoursInYear - direct; + return Math.Min(direct, wrapped); + } + + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Query/CheckProvenance.cs b/SAM.Analytical.Benchmark.Compare/Query/CheckProvenance.cs new file mode 100644 index 0000000..d63be37 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Query/CheckProvenance.cs @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Query + { + /// + /// Checks whether the two documents are actually comparable: the first argument must be a + /// successful TAS run and the second a successful OpenStudio run, produced from the same canonical + /// source model, weather and design-day basis, via a compatible route family. Every disagreement is + /// reported as a ; any mismatch makes the pair incompatible and + /// prevents an overall pass. This guards against swapped CLI inputs, two documents from the same + /// engine, different models or weather, and failure documents whose measurements are mostly + /// unavailable. + /// + public static ProvenanceCompatibility CheckProvenanceCompatibility(BenchmarkProvenance? tas, BenchmarkProvenance? openStudio) + { + var mismatches = new List(); + + if (tas == null || openStudio == null) + { + mismatches.Add(new ProvenanceMismatch("provenance", tas == null ? "missing" : "present", openStudio == null ? "missing" : "present")); + return new ProvenanceCompatibility(mismatches); + } + + // Each argument must carry the engine and route family the CLI position promises. + RequireEngineKind(mismatches, "tasEngineKind", tas.Engine?.Kind, EngineKind.Tas); + RequireEngineKind(mismatches, "openStudioEngineKind", openStudio.Engine?.Kind, EngineKind.OpenStudio); + RequireRouteFamily(mismatches, "tasRoute", tas.Route, isTas: true); + RequireRouteFamily(mismatches, "openStudioRoute", openStudio.Route, isTas: false); + + // Route pairing: native must pair with native, shared-gbXML with shared-gbXML. + if (RouteFamily(tas.Route) != RouteFamily(openStudio.Route)) + { + mismatches.Add(new ProvenanceMismatch("routePairing", RouteToken(tas.Route), RouteToken(openStudio.Route))); + } + + // Both runs must have succeeded; a failure document's measurements are largely unavailable. + if (tas.State != RunState.Success || openStudio.State != RunState.Success) + { + mismatches.Add(new ProvenanceMismatch("runState", RunStateToken(tas.State), RunStateToken(openStudio.State))); + } + + // Same canonicalization rules, otherwise the canonical hashes are not comparable. + bool sameCanonicalization = StringsEqual(tas.CanonicalizationVersion, openStudio.CanonicalizationVersion); + if (!sameCanonicalization) + { + mismatches.Add(new ProvenanceMismatch("canonicalizationVersion", tas.CanonicalizationVersion, openStudio.CanonicalizationVersion)); + } + + // Same canonical source model, source-model identity, weather and design-day basis. SCHEMA.md: + // a canonicalization-version mismatch does not prove different models, and hashes produced by + // different canonicalizers must not be compared as equivalent evidence — the version mismatch + // above is the reported finding, so the hash equality check is skipped in that case. + if (sameCanonicalization) + { + RequireEqual(mismatches, "canonicalModelHash", tas.CanonicalModelHash, openStudio.CanonicalModelHash); + } + + RequireEqual(mismatches, "sourceModelGuid", tas.SourceModelGuid, openStudio.SourceModelGuid); + RequireEqual(mismatches, "weatherHash", tas.Weather?.Hash, openStudio.Weather?.Hash); + RequireEqual(mismatches, "weatherIdentity", tas.Weather?.Identity, openStudio.Weather?.Identity); + if (tas.DesignDaySource != openStudio.DesignDaySource) + { + mismatches.Add(new ProvenanceMismatch("designDaySource", DesignDayToken(tas.DesignDaySource), DesignDayToken(openStudio.DesignDaySource))); + } + + return new ProvenanceCompatibility(mismatches); + } + + private static void RequireEngineKind(ICollection mismatches, string field, EngineKind? actual, EngineKind expected) + { + if (actual != expected) + { + mismatches.Add(new ProvenanceMismatch(field, EngineKindToken(actual), EngineKindToken(expected))); + } + } + + private static void RequireRouteFamily(ICollection mismatches, string field, BenchmarkRoute route, bool isTas) + { + bool ok = isTas + ? route == BenchmarkRoute.NativeTas || route == BenchmarkRoute.SharedGbXmlTas + : route == BenchmarkRoute.NativeOpenStudio || route == BenchmarkRoute.SharedGbXmlOpenStudio; + if (!ok) + { + mismatches.Add(new ProvenanceMismatch(field, RouteToken(route), isTas ? "a TAS route" : "an OpenStudio route")); + } + } + + private static void RequireEqual(ICollection mismatches, string field, string? tas, string? openStudio) + { + if (!StringsEqual(tas, openStudio)) + { + mismatches.Add(new ProvenanceMismatch(field, tas, openStudio)); + } + } + + private static bool StringsEqual(string? left, string? right) + { + return string.Equals(left ?? string.Empty, right ?? string.Empty, StringComparison.Ordinal); + } + + private static string RouteFamily(BenchmarkRoute route) + { + switch (route) + { + case BenchmarkRoute.NativeTas: + case BenchmarkRoute.NativeOpenStudio: + return "Native"; + case BenchmarkRoute.SharedGbXmlTas: + case BenchmarkRoute.SharedGbXmlOpenStudio: + return "SharedGbXML"; + default: + return "Unknown"; + } + } + + private static string EngineKindToken(EngineKind? kind) + { + if (!kind.HasValue) + { + return "unknown"; + } + + switch (kind.Value) + { + case EngineKind.OpenStudio: return "OpenStudio"; + case EngineKind.Tas: return "TAS"; + default: return "unknown"; + } + } + + private static string RouteToken(BenchmarkRoute route) + { + switch (route) + { + case BenchmarkRoute.NativeOpenStudio: return "Native-OpenStudio"; + case BenchmarkRoute.NativeTas: return "Native-TAS"; + case BenchmarkRoute.SharedGbXmlOpenStudio: return "SharedGbXML-OpenStudio"; + case BenchmarkRoute.SharedGbXmlTas: return "SharedGbXML-TAS"; + default: return "Unknown"; + } + } + + private static string DesignDayToken(DesignDaySource source) + { + switch (source) + { + case DesignDaySource.Ddy: return "DDY"; + case DesignDaySource.EmbeddedModel: return "EmbeddedModel"; + case DesignDaySource.None: return "None"; + default: return "Unknown"; + } + } + + private static string RunStateToken(RunState state) + { + switch (state) + { + case RunState.Success: return "Success"; + case RunState.Failure: return "Failure"; + default: return "Unknown"; + } + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Query/Compare.cs b/SAM.Analytical.Benchmark.Compare/Query/Compare.cs new file mode 100644 index 0000000..34f9366 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Query/Compare.cs @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Query + { + /// + /// Compares a TAS benchmark document against an OpenStudio benchmark document, producing a fully + /// deterministic . The comparator is engine-independent: it reads + /// only the neutral schema types and never touches an engine runtime. + /// + /// + /// + /// The method validates BOTH documents itself (it does not rely on the caller having read them + /// through ): a document that violates the schema — including a + /// wrong metric unit, a broken availability invariant, an out-of-range hour, or an incompatible + /// schema major version — is a contract error and throws , + /// which the CLI maps to the shared validation exit code. + /// + /// + /// Beyond per-document validity, the comparator checks that the two runs are actually comparable — + /// same canonical source model, weather and design-day basis, expected engine/route on each side, + /// and both successful (see ). A provenance failure prevents an + /// overall . A minor schema drift (relative to this comparator or + /// between the two documents) is recorded as a non-fatal note; a patch-only difference is not. + /// + /// + public static ComparisonResult Compare(BenchmarkDocument tas, BenchmarkDocument openStudio, ToleranceProfile profile) + { + if (tas == null) + { + throw new ArgumentNullException(nameof(tas)); + } + + if (openStudio == null) + { + throw new ArgumentNullException(nameof(openStudio)); + } + + if (profile == null) + { + throw new ArgumentNullException(nameof(profile)); + } + + // Contract errors (unit mismatch, availability-invariant break, out-of-range hours, malformed or + // major-incompatible schema version) are rejected here rather than silently normalised to N/A. + BenchmarkValidator.ThrowIfInvalid(tas); + BenchmarkValidator.ThrowIfInvalid(openStudio); + + string? schemaDriftNote = CheckSchemaDrift(tas.SchemaVersion, openStudio.SchemaVersion); + ProvenanceCompatibility provenanceCompatibility = CheckProvenanceCompatibility(tas.Provenance, openStudio.Provenance); + + IReadOnlyList modelMetrics = CompareModel(tas.Model, openStudio.Model, profile); + SpaceAlignment alignment = AlignSpaces( + (IReadOnlyList?)tas.Spaces, + (IReadOnlyList?)openStudio.Spaces); + IReadOnlyList spaces = CompareSpaces(alignment, profile); + + // Reconciliation sums only uniquely-matched spaces (TOLERANCES.md); one-sided/ambiguous spaces + // are excluded and make the reconciliation incomplete. + var tasMatched = new List(); + var openStudioMatched = new List(); + foreach (SpacePair pair in alignment.Pairs) + { + tasMatched.Add(pair.Tas); + openStudioMatched.Add(pair.OpenStudio); + } + + var reconciliations = new List(); + reconciliations.AddRange(Reconcile("TAS", tas.Model, tasMatched, alignment.OnlyInTas.Count, profile)); + reconciliations.AddRange(Reconcile("OpenStudio", openStudio.Model, openStudioMatched, alignment.OnlyInOpenStudio.Count, profile)); + + return new ComparisonResult( + profile, + tas.SchemaVersion, + openStudio.SchemaVersion, + schemaDriftNote, + provenanceCompatibility, + tas.Provenance, + openStudio.Provenance, + modelMetrics, + spaces, + alignment.Diagnostics, + reconciliations); + } + + /// + /// Builds the schema-drift note. Both documents have already been validated (equal, compatible + /// major). A MINOR drift — either document's minor differs from this comparator's, or the two + /// documents' minors differ from each other — warns, because unknown additive fields may be + /// ignored. A patch-only difference does not warn. + /// + private static string? CheckSchemaDrift(string? tasVersion, string? openStudioVersion) + { + var notes = new List(); + if (BenchmarkSchema.GetCompatibility(tasVersion) == SchemaCompatibility.CompatibleWithMinorWarning) + { + notes.Add($"The TAS document schema {tasVersion} has a newer minor than this comparator ({BenchmarkSchema.CurrentVersion}); unknown additive fields are ignored."); + } + + if (BenchmarkSchema.GetCompatibility(openStudioVersion) == SchemaCompatibility.CompatibleWithMinorWarning) + { + notes.Add($"The OpenStudio document schema {openStudioVersion} has a newer minor than this comparator ({BenchmarkSchema.CurrentVersion}); unknown additive fields are ignored."); + } + + if (BenchmarkSchema.TryParseVersion(tasVersion, out _, out int tasMinor, out _) + && BenchmarkSchema.TryParseVersion(openStudioVersion, out _, out int openStudioMinor, out _) + && tasMinor != openStudioMinor) + { + notes.Add($"The documents declare different schema minor versions (TAS {tasVersion}, OpenStudio {openStudioVersion}); comparing shared fields."); + } + + return notes.Count == 0 ? null : string.Join(" ", notes); + } + + private static IReadOnlyList CompareModel(BenchmarkModelResult? tas, BenchmarkModelResult? openStudio, ToleranceProfile profile) + { + // ModelScope is shared with IsInformationalForNumericalGate, which recognises the two whole-model + // peak-load keys below by scope + key rather than by a scattered string comparison. + return new List + { + Band(ModelScope, "consumptionHeating", tas?.ConsumptionHeating, openStudio?.ConsumptionHeating, profile), + Band(ModelScope, "consumptionCooling", tas?.ConsumptionCooling, openStudio?.ConsumptionCooling, profile), + Band(ModelScope, "peakHeatingLoad", tas?.PeakHeatingLoad, openStudio?.PeakHeatingLoad, profile), + Band(ModelScope, "peakHeatingHour", tas?.PeakHeatingHour, openStudio?.PeakHeatingHour, profile), + Band(ModelScope, "peakCoolingLoad", tas?.PeakCoolingLoad, openStudio?.PeakCoolingLoad, profile), + Band(ModelScope, "peakCoolingHour", tas?.PeakCoolingHour, openStudio?.PeakCoolingHour, profile), + Band(ModelScope, "floorArea", tas?.FloorArea, openStudio?.FloorArea, profile), + Band(ModelScope, "volume", tas?.Volume, openStudio?.Volume, profile) + }; + } + + private static IReadOnlyList CompareSpaces(SpaceAlignment alignment, ToleranceProfile profile) + { + var comparisons = new List(); + + foreach (SpacePair pair in alignment.Pairs) + { + string scope = pair.Guid ?? pair.Tas.Name ?? pair.OpenStudio.Name ?? "(unnamed)"; + comparisons.Add(new SpaceComparison( + pair.Guid, + pair.Tas.Name, + pair.OpenStudio.Name, + pair.MatchKind, + CompareSpaceMetrics(scope, pair.Tas, pair.OpenStudio, profile))); + } + + foreach (BenchmarkSpaceResult space in alignment.OnlyInTas) + { + string scope = space.Guid ?? space.Name ?? "(unnamed)"; + comparisons.Add(new SpaceComparison( + space.Guid, + space.Name, + null, + SpaceMatchKind.TasOnly, + CompareSpaceMetrics(scope, space, null, profile))); + } + + foreach (BenchmarkSpaceResult space in alignment.OnlyInOpenStudio) + { + string scope = space.Guid ?? space.Name ?? "(unnamed)"; + comparisons.Add(new SpaceComparison( + space.Guid, + null, + space.Name, + SpaceMatchKind.OpenStudioOnly, + CompareSpaceMetrics(scope, null, space, profile))); + } + + return comparisons; + } + + private static IReadOnlyList CompareSpaceMetrics(string scope, BenchmarkSpaceResult? tas, BenchmarkSpaceResult? openStudio, ToleranceProfile profile) + { + var metrics = new List + { + Band(scope, "area", tas?.Area, openStudio?.Area, profile), + Band(scope, "volume", tas?.Volume, openStudio?.Volume, profile) + }; + + AddConditionMetrics(metrics, scope, "heating", tas?.Heating, openStudio?.Heating, profile); + AddConditionMetrics(metrics, scope, "cooling", tas?.Cooling, openStudio?.Cooling, profile); + return metrics; + } + + private static void AddConditionMetrics( + ICollection metrics, + string scope, + string condition, + BenchmarkConditionResult? tas, + BenchmarkConditionResult? openStudio, + ToleranceProfile profile) + { + metrics.Add(Band(scope, condition + ".designLoad", tas?.DesignLoad, openStudio?.DesignLoad, profile)); + metrics.Add(Band(scope, condition + ".peakLoad", tas?.PeakLoad, openStudio?.PeakLoad, profile)); + metrics.Add(Band(scope, condition + ".peakHour", tas?.PeakHour, openStudio?.PeakHour, profile)); + metrics.Add(Band(scope, condition + ".unmetHours", tas?.UnmetHours, openStudio?.UnmetHours, profile)); + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Query/Gate.cs b/SAM.Analytical.Benchmark.Compare/Query/Gate.cs new file mode 100644 index 0000000..ab9af64 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Query/Gate.cs @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; +using GateStatusValue = SAM.Analytical.Benchmark.Compare.GateStatus; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Query + { + /// The scope label carried by whole-model metric comparisons. + public const string ModelScope = "model"; + + /// + /// The whole-model metric keys that are banded and reported but excluded from + /// . Only the two whole-model PEAK LOADS qualify: METRICS.md keeps them + /// informational because OpenStudio reports a coincident total while TAS takes the maximum of the + /// building profile, so a difference can reflect aggregation semantics rather than engine physics. + /// Promotion into the gate requires the B4 corpus and energy-modeller review. + /// + private static readonly HashSet InformationalModelMetricKeys = new HashSet(StringComparer.Ordinal) + { + "peakHeatingLoad", + "peakCoolingLoad" + }; + + /// + /// Whether a metric is designated INFORMATIONAL for numerical-gate aggregation: it is compared, + /// banded and reported as usual, but its band cannot move . Exactly two + /// families qualify, both by explicit contract: + /// + /// every metric (TOLERANCES.md: peak-hour differences are + /// informational until their absolute thresholds are reviewed); and + /// the whole-model peak loads peakHeatingLoad/peakCoolingLoad (METRICS.md: the + /// two engines aggregate whole-model peaks differently). + /// + /// Nothing else is excluded — annual energy, per-space peak loads, unmet hours and geometry all gate + /// normally. The per-space peak loads use the distinct keys heating.peakLoad/ + /// cooling.peakLoad, so a space that happens to be NAMED "model" cannot be captured here. + /// + public static bool IsInformationalForNumericalGate(MetricComparison metric) + { + if (metric == null) + { + throw new ArgumentNullException(nameof(metric)); + } + + if (metric.Unit == MetricUnit.HourOfYear) + { + return true; + } + + return string.Equals(metric.Scope, ModelScope, StringComparison.Ordinal) + && InformationalModelMetricKeys.Contains(metric.Key); + } + + /// + /// The numerical status: the worst comparable NON-INFORMATIONAL metric band (Fail over Warn over + /// Pass). N/A metrics do not affect the ordering, and metrics designated informational by + /// are skipped — their bands are still calculated and + /// reported, they just cannot fail an experiment on semantics the contract has not yet settled. + /// + public static GateStatusValue NumericalStatus(IEnumerable metrics) + { + if (metrics == null) + { + throw new ArgumentNullException(nameof(metrics)); + } + + bool anyWarn = false; + foreach (MetricComparison metric in metrics) + { + if (IsInformationalForNumericalGate(metric)) + { + continue; + } + + if (metric.Band == ComparisonBand.Fail) + { + return GateStatusValue.Fail; + } + + if (metric.Band == ComparisonBand.Warn) + { + anyWarn = true; + } + } + + return anyWarn ? GateStatusValue.Warn : GateStatusValue.Pass; + } + + /// + /// The coverage status: whether the comparison actually compared the runs. Coverage is incomplete + /// (Warn) when any space is unmatched, duplicated or ambiguous ("split"), when ANY required metric + /// in a comparable scope was unavailable, or when nothing was comparable at all. TOLERANCES.md + /// requires unavailable required metrics to be represented in coverage, because N/A metrics do not + /// affect the numerical status either — without this, a run missing most of its results could + /// present as a clean overall pass. + /// + /// The space-alignment diagnostics. + /// Required metrics that were actually compared. + /// + /// Required metrics that could not be compared, counted over the whole-model metrics and the metrics + /// of uniquely matched spaces only. One-sided and ambiguous spaces are deliberately excluded: every + /// one of their metrics is N/A by construction, and that gap is already represented by the unmatched + /// space diagnostics, so counting them here would double-count the same missing data. + /// + public static GateStatusValue CoverageStatus(SpaceMatchDiagnostics diagnostics, int comparableMetricCount, int unavailableRequiredMetricCount) + { + if (diagnostics == null) + { + throw new ArgumentNullException(nameof(diagnostics)); + } + + bool incompleteSpaces = diagnostics.OnlyInTas.Count > 0 + || diagnostics.OnlyInOpenStudio.Count > 0 + || diagnostics.DuplicateTasGuids.Count > 0 + || diagnostics.DuplicateOpenStudioGuids.Count > 0 + || diagnostics.DuplicateTasNames.Count > 0 + || diagnostics.DuplicateOpenStudioNames.Count > 0 + || diagnostics.AmbiguousNameMatches.Count > 0; + + bool incomplete = incompleteSpaces + || unavailableRequiredMetricCount > 0 + || comparableMetricCount == 0; + + return incomplete ? GateStatusValue.Warn : GateStatusValue.Pass; + } + + /// The provenance status: Pass when the two runs are compatible, otherwise Fail. + public static GateStatusValue ProvenanceStatus(ProvenanceCompatibility compatibility) + { + if (compatibility == null) + { + throw new ArgumentNullException(nameof(compatibility)); + } + + return compatibility.IsCompatible ? GateStatusValue.Pass : GateStatusValue.Fail; + } + + /// + /// The reconciliation status. A within-document total-vs-sum mismatch is a data-quality signal, not a + /// cross-engine disagreement, so it is capped at Warn: Warn when any reconciliation is incomplete, + /// not applicable, or lands outside the match band; Pass otherwise. + /// + public static GateStatusValue ReconciliationStatus(IEnumerable reconciliations) + { + if (reconciliations == null) + { + throw new ArgumentNullException(nameof(reconciliations)); + } + + foreach (ReconciliationResult reconciliation in reconciliations) + { + if (!reconciliation.Complete || reconciliation.Band != ComparisonBand.Match) + { + return GateStatusValue.Warn; + } + } + + return GateStatusValue.Pass; + } + + /// The overall gate: the worst of the numerical, coverage, provenance and reconciliation statuses. + public static GateStatusValue OverallGate(GateStatusValue numerical, GateStatusValue coverage, GateStatusValue provenance, GateStatusValue reconciliation) + { + GateStatusValue worst = numerical; + worst = Worse(worst, coverage); + worst = Worse(worst, provenance); + worst = Worse(worst, reconciliation); + return worst; + } + + private static GateStatusValue Worse(GateStatusValue left, GateStatusValue right) + { + return (int)right > (int)left ? right : left; + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/Query/Reconcile.cs b/SAM.Analytical.Benchmark.Compare/Query/Reconcile.cs new file mode 100644 index 0000000..cde9919 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/Query/Reconcile.cs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright (c) 2020-2026 Michal Dengusiak & Jakub Ziolkowski and contributors + +using System; +using System.Collections.Generic; + +namespace SAM.Analytical.Benchmark.Compare +{ + public static partial class Query + { + /// + /// Reconciles a single engine's additive whole-model totals (floor area, volume) against the sum + /// of the same quantity over its UNIQUELY-MATCHED spaces, banding the difference with the supplied + /// profile. Per TOLERANCES.md, only additive quantities are reconciled and only available, + /// uniquely-matched space values contribute; one-sided/ambiguous spaces are excluded and make the + /// reconciliation incomplete (a within-document diagnostic, kept separate from the numerical gate). + /// + /// This engine's spaces that were uniquely matched to the other document. + /// How many of this engine's spaces were NOT uniquely matched. + public static IReadOnlyList Reconcile( + string engine, + BenchmarkModelResult? model, + IReadOnlyList matchedSpaces, + int excludedSpaceCount, + ToleranceProfile profile) + { + if (profile == null) + { + throw new ArgumentNullException(nameof(profile)); + } + + IReadOnlyList spaces = matchedSpaces ?? Array.Empty(); + return new List + { + Reconcile(engine, "floorArea", MetricUnit.SquareMetre, model?.FloorArea, spaces, excludedSpaceCount, space => space?.Area, profile), + Reconcile(engine, "volume", MetricUnit.CubicMetre, model?.Volume, spaces, excludedSpaceCount, space => space?.Volume, profile) + }; + } + + private static ReconciliationResult Reconcile( + string engine, + string key, + MetricUnit unit, + MetricValue? modelTotal, + IReadOnlyList matchedSpaces, + int excludedSpaceCount, + Func selector, + ToleranceProfile profile) + { + double sum = 0; + int contributing = 0; + int missing = 0; + foreach (BenchmarkSpaceResult space in matchedSpaces) + { + MetricValue? metric = selector(space); + if (metric != null && metric.Available && metric.Value.HasValue) + { + sum += metric.Value.Value; + contributing++; + } + else + { + missing++; + } + } + + bool modelAvailable = modelTotal != null && modelTotal.Available && modelTotal.Value.HasValue; + + // A reconciliation is only "complete" when nothing was left out: the model total is present, + // every one of this engine's spaces was uniquely matched, and every matched space had a value. + bool complete = modelAvailable && excludedSpaceCount == 0 && missing == 0; + + if (!modelAvailable) + { + return new ReconciliationResult(engine, key, unit, null, false, sum, contributing, missing, excludedSpaceCount, false, null, null, ComparisonBand.NotApplicable, NotApplicableReason.Unavailable); + } + + double total = modelTotal!.Value!.Value; + double absolute = Math.Abs(total - sum); + double scale = Math.Max(Math.Max(Math.Abs(total), Math.Abs(sum)), profile.NearZeroFloor(unit)); + double relative = scale > 0 ? absolute / scale : 0d; + ComparisonBand band = Classify(relative, profile); + + return new ReconciliationResult(engine, key, unit, total, true, sum, contributing, missing, excludedSpaceCount, complete, absolute, relative, band, NotApplicableReason.None); + } + } +} diff --git a/SAM.Analytical.Benchmark.Compare/SAM.Analytical.Benchmark.Compare.csproj b/SAM.Analytical.Benchmark.Compare/SAM.Analytical.Benchmark.Compare.csproj new file mode 100644 index 0000000..0883861 --- /dev/null +++ b/SAM.Analytical.Benchmark.Compare/SAM.Analytical.Benchmark.Compare.csproj @@ -0,0 +1,38 @@ + + + + Exe + net8.0 + benchmark-compare + SAM.Analytical.Benchmark.Compare + latest + enable + disable + + + + + false + false + ..\build\ + + + + + + + + + + + + diff --git a/SAM_Validation.sln b/SAM_Validation.sln index c3745f2..4d71bb7 100644 --- a/SAM_Validation.sln +++ b/SAM_Validation.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.0.11217.181 d18.0 +VisualStudioVersion = 18.0.11217.181 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAM_IAPWS", "SAM_IAPWS\SAM_IAPWS.csproj", "{CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}" EndProject @@ -18,32 +18,104 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAM.Analytical.Benchmark.Compare", "SAM.Analytical.Benchmark.Compare\SAM.Analytical.Benchmark.Compare.csproj", "{22CC52AC-46A6-4AAA-8A68-9748E1950052}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAM.Analytical.Benchmark.Compare.Tests", "SAM.Analytical.Benchmark.Compare.Tests\SAM.Analytical.Benchmark.Compare.Tests.csproj", "{D4FB6D96-8831-45F8-9F8A-38BE156EC237}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Debug|x64.ActiveCfg = Debug|Any CPU + {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Debug|x64.Build.0 = Debug|Any CPU + {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Debug|x86.Build.0 = Debug|Any CPU {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Release|Any CPU.Build.0 = Release|Any CPU + {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Release|x64.ActiveCfg = Release|Any CPU + {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Release|x64.Build.0 = Release|Any CPU + {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Release|x86.ActiveCfg = Release|Any CPU + {CC18AB28-FDF4-4D7E-B45A-A03406ACB9A2}.Release|x86.Build.0 = Release|Any CPU {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Debug|x64.ActiveCfg = Debug|Any CPU + {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Debug|x64.Build.0 = Debug|Any CPU + {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Debug|x86.ActiveCfg = Debug|Any CPU + {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Debug|x86.Build.0 = Debug|Any CPU {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Release|Any CPU.ActiveCfg = Release|Any CPU {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Release|Any CPU.Build.0 = Release|Any CPU + {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Release|x64.ActiveCfg = Release|Any CPU + {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Release|x64.Build.0 = Release|Any CPU + {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Release|x86.ActiveCfg = Release|Any CPU + {5FC1F323-DB20-417E-90AE-D2F91CE4810A}.Release|x86.Build.0 = Release|Any CPU {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Debug|x64.Build.0 = Debug|Any CPU + {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Debug|x86.Build.0 = Debug|Any CPU {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Release|Any CPU.Build.0 = Release|Any CPU + {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Release|x64.ActiveCfg = Release|Any CPU + {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Release|x64.Build.0 = Release|Any CPU + {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Release|x86.ActiveCfg = Release|Any CPU + {5A18E1C0-C2A7-4709-AD6F-4B77F41ED239}.Release|x86.Build.0 = Release|Any CPU {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Debug|x64.ActiveCfg = Debug|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Debug|x64.Build.0 = Debug|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Debug|x86.ActiveCfg = Debug|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Debug|x86.Build.0 = Debug|Any CPU {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Release|Any CPU.Build.0 = Release|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Release|x64.ActiveCfg = Release|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Release|x64.Build.0 = Release|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Release|x86.ActiveCfg = Release|Any CPU + {A0E10A5F-698A-4D12-A76D-2F8D293B89E1}.Release|x86.Build.0 = Release|Any CPU {A6C4F453-F2A9-43E4-8628-968868162703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A6C4F453-F2A9-43E4-8628-968868162703}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Debug|x64.ActiveCfg = Debug|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Debug|x64.Build.0 = Debug|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Debug|x86.ActiveCfg = Debug|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Debug|x86.Build.0 = Debug|Any CPU {A6C4F453-F2A9-43E4-8628-968868162703}.Release|Any CPU.ActiveCfg = Release|Any CPU {A6C4F453-F2A9-43E4-8628-968868162703}.Release|Any CPU.Build.0 = Release|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Release|x64.ActiveCfg = Release|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Release|x64.Build.0 = Release|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Release|x86.ActiveCfg = Release|Any CPU + {A6C4F453-F2A9-43E4-8628-968868162703}.Release|x86.Build.0 = Release|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Debug|Any CPU.Build.0 = Debug|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Debug|x64.ActiveCfg = Debug|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Debug|x64.Build.0 = Debug|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Debug|x86.ActiveCfg = Debug|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Debug|x86.Build.0 = Debug|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Release|Any CPU.ActiveCfg = Release|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Release|Any CPU.Build.0 = Release|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Release|x64.ActiveCfg = Release|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Release|x64.Build.0 = Release|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Release|x86.ActiveCfg = Release|Any CPU + {22CC52AC-46A6-4AAA-8A68-9748E1950052}.Release|x86.Build.0 = Release|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Debug|x64.ActiveCfg = Debug|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Debug|x64.Build.0 = Debug|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Debug|x86.ActiveCfg = Debug|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Debug|x86.Build.0 = Debug|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Release|Any CPU.Build.0 = Release|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Release|x64.ActiveCfg = Release|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Release|x64.Build.0 = Release|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Release|x86.ActiveCfg = Release|Any CPU + {D4FB6D96-8831-45F8-9F8A-38BE156EC237}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/benchmark/METRICS.md b/docs/benchmark/METRICS.md index 2a855f3..678583d 100644 --- a/docs/benchmark/METRICS.md +++ b/docs/benchmark/METRICS.md @@ -35,6 +35,8 @@ B1b must compare the raw EnergyPlus SQL value, the OpenStudio result-set value, The values are comparable reporting metrics but do not yet have proven identical semantics. OpenStudio reports its coincident-total result, while Tas takes the maximum of the Tas building profile. Differences can therefore reflect aggregation and reporting semantics as well as engine physics. These metrics remain informational until corpus evidence supports a stronger gate interpretation. +Concretely, `peakHeatingLoad` and `peakCoolingLoad` are **designated informational metrics**: the comparator bands, reports and marks them, but excludes them from the numerical status, so they cannot fail an experiment on aggregation semantics the contract has not yet settled. Only these two whole-model peaks are excluded — the per-space peak loads (`heating.peakLoad` / `cooling.peakLoad`) gate normally. See [Informational metrics](TOLERANCES.md#informational-metrics). + ## Per-space metrics Each space contains `area` and `volume` plus `heating` and `cooling` result groups. SAM results are selected by both space identity and `SpaceSimulationResultParameter.LoadType`. diff --git a/docs/benchmark/TOLERANCES.md b/docs/benchmark/TOLERANCES.md index f7cbb15..33bc02d 100644 --- a/docs/benchmark/TOLERANCES.md +++ b/docs/benchmark/TOLERANCES.md @@ -58,6 +58,8 @@ The following are contract errors rather than `N/A` comparisons: Coverage diagnostics count unavailable required metrics and unmatched spaces separately. A numerically passing report with poor coverage must not be described as complete. +Concretely, the coverage status is `Warn` when any required metric is unavailable, when any space is unmatched, duplicated or ambiguous, or when nothing was comparable at all. Required metrics are the whole-model metrics plus the metrics of uniquely matched spaces; the metrics of one-sided or ambiguous spaces are not counted again, because those spaces are already reported by the unmatched-space diagnostics. Because `N/A` cannot affect the numerical status either, this is what stops a run that produced few results from presenting as a clean overall pass. + ## Peak-hour comparison Peak hours are circular over a normal 8760-hour year. For valid hours `a` and `b`: @@ -67,7 +69,7 @@ directDifference = abs(a - b) circularDifference = min(directDifference, 8760 - directDifference) ``` -Thus hours `8759` and `0` differ by one hour, not 8759 hours. Percentage bands do not apply to `hourOfYear`; the tolerance profile must provide absolute warn/fail thresholds in hours. Until those thresholds are reviewed, peak-hour differences are informational and cannot produce a numerical Fail. +Thus hours `8759` and `0` differ by one hour, not 8759 hours. Percentage bands do not apply to `hourOfYear`; the tolerance profile must provide absolute warn/fail thresholds in hours, and both thresholds are applied so a large gap is not reported identically to a small one. Until those thresholds are reviewed, peak-hour differences remain informational: `hourOfYear` metrics are excluded from the numerical status, so a reported peak-hour Fail band cannot produce a numerical Fail. The exclusion — not a suppressed band — is what keeps them informational. Leap-year or sub-hourly runs are outside v1. A producer must not force such indices into the `0..8759` contract without an explicit normalization policy and schema revision. @@ -97,9 +99,22 @@ The comparator keeps these concerns distinct: 4. numerical bands; and 5. reconciliation diagnostics. -For numerical bands, overall severity is the worst comparable metric: Fail over Warn over Pass. `N/A` does not affect that ordering. Contract errors prevent a valid numerical gate. Coverage and reconciliation statuses are always reported alongside the numerical status so missing data cannot improve the apparent result. +For numerical bands, overall severity is the worst comparable **non-informational** metric: Fail over Warn over Pass. `N/A` does not affect that ordering. Contract errors prevent a valid numerical gate. Coverage and reconciliation statuses are always reported alongside the numerical status so missing data cannot improve the apparent result. + +### Informational metrics + +A metric may be **explicitly designated informational**: it is compared, banded and reported exactly like any other metric, but its band is excluded from the numerical status and therefore cannot fail an experiment. Two families are designated in v1: + +| Metric | Why it is informational | +|---|---| +| Every `hourOfYear` metric (model and per-space peak hours) | The absolute warn/fail hour thresholds are unreviewed | +| `model.peakHeatingLoad` and `model.peakCoolingLoad` | METRICS.md: OpenStudio reports a coincident total while Tas takes the maximum of the building profile, so a difference can reflect aggregation semantics rather than engine physics | + +The designation is deliberately narrow. Annual heating and cooling energy, per-space peak and design loads, unmet hours, and all geometry metrics gate normally — the same "differing semantics" argument must not be extended to them by implication, because it would leave nothing to gate on. + +Informational status is expressed by exclusion, not by hiding evidence: the band is still calculated, still counted in the report's match/warn/fail totals, and every report marks the affected row so a reported Fail cannot be mistaken for a gate failure. Promoting either family into the numerical gate requires the same validation path as the bands themselves — corpus evidence across both engines plus energy-modeller review — and must be versioned and recorded with the report. -The whole-model peak-load caveat and candidate conditioning pairing remain visible even when their numbers fall in Pass. Tolerance profiles cannot remove those methodological limitations. +The whole-model peak-load caveat and candidate conditioning pairing remain visible even when their numbers fall in Pass. Tolerance profiles cannot remove those methodological limitations, and a tolerance profile cannot change which metrics are designated informational. ## Validation path diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/README.md b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/README.md new file mode 100644 index 0000000..724345a --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/README.md @@ -0,0 +1,118 @@ +# B3 — first real cross-engine comparison (same model, same weather) + +This records the first end-to-end run of the **B3 comparator** over two real engine documents +produced from the **same source model and the same weather file** — the numeric pairing that the B2 +/ D10 checkpoint deferred (it had to use a geographically mismatched EPW). + +It is a **mechanical validation of the comparator**, not a validated engine-equivalence claim. The +tolerance bands remain provisional reporting buckets. + +## Run + +- **Date:** 2026-07-25 +- **Machine:** licensed EDSL Tas install + OpenStudio/EnergyPlus on the same workstation. +- **Model:** `HungaryHouse-WeahterDDY.sam` — real Revit-derived SAM `AnalyticalModel`, 14 spaces + (conditioned rooms + unconditioned voids), carrying embedded design days. +- **Weather:** `HUN_CEN_Budapest.Met.Center.128400_TMYx.epw` — geographically correct for this model + (the D10 checkpoint used a Boston EPW purely to exercise the pipeline). +- **Engines:** Tas `9.5.7.0`; EnergyPlus `25.1.0`. +- **Commands** (both producers, then the comparator): + + ``` + benchmark-tas --model HungaryHouse.sam --weather Budapest.epw --tbd hungary-budapest.tbd \ + --out benchmark-TAS.json --sam-commit --runner-commit + benchmark-openstudio --model HungaryHouse.sam --weather Budapest.epw \ + --out benchmark-OpenStudio.json --work \ + --sam-commit --runner-commit + benchmark-compare --tas benchmark-TAS.json --openstudio benchmark-OpenStudio.json \ + --out report --tolerance-profile default + ``` + +- **Result:** both producers exited `0` with `state = Success`; the comparator exited `0` and wrote + all three reports. Durations: TAS ≈ 58 s, OpenStudio ≈ 21 s. +- **Note:** the two neutral input documents are the verbatim producer outputs of that run. The three + comparison reports were **regenerated from those same committed inputs** after three comparator + corrections landed in this PR (unavailable required metrics now count towards coverage; peak-hour + differences now use the profile's absolute fail threshold; the two whole-model peak loads are now + designated informational and excluded from the numerical status). The simulations were not re-run, + and no input byte changed — only the comparator's classification of the same data. + +## What the comparator got right (its acceptance evidence) + +| Check | Outcome | +|---|---| +| Space alignment | **14/14 matched by GUID**, 0 by name, none one-sided, none ambiguous | +| Geometry agreement | floor area `167.95195245742798` vs `167.95195207345205` m² → rel. diff **2.3e-7**, `Match` | +| Reconciliation | model total vs sum of matched spaces **complete (14/0/0)** and `Match` for both engines, both quantities | +| Circular peak hour | TAS hour `0` vs OpenStudio `7974` correctly reported as **786 h** apart (wrapped), not 7974 | +| Unavailable ⇒ N/A | 66 metrics N/A, none contributing a numerical failure (e.g. OpenStudio emits no per-space *design* loads — a documented route limitation, so every `heating.designLoad` is `TasOnly`) | +| Coverage counted honestly | only **82 of 148** required metrics were comparable, so coverage is `Warn`: perfect space alignment does not let 66 missing measurements read as a complete result | +| Determinism | reports written LF / UTF-8 no BOM, full-precision values | + +The near-identical geometry is the strongest signal that the whole chain is sound: two independent +engines, two independent producers, one neutral schema, and the areas/volumes agree to seven +significant figures. + +## Finding 1 — the provenance gate caught a real producer defect + +The comparator reported **provenance INCOMPATIBLE** on a single field: + +| Field | TAS | OpenStudio | +|---|---|---| +| `designDaySource` | `EmbeddedModel` | `None` | + +This is **not** a genuine input difference — both runs used the identical model, and the OpenStudio +run demonstrably did construct design days. The OpenStudio producer **hardcodes** +`DesignDaySource.None` (`SAM_OpenStudio/benchmark/SAM.Analytical.OpenStudio.Benchmark/Program.cs:86`) +instead of deriving it as the TAS producer does. + +So the provenance gate did exactly the job it was added for: it refused to bless a comparison whose +declared inputs did not line up, and pointed at the responsible field. **Fix belongs in +SAM_OpenStudio** (separate repo, separate PR) — tracked as a follow-up; nothing to change in B3. + +## Finding 2 — the candidate conditioning pairing is NOT yet numerically equivalent + +With the correct weather and an aligned model, the whole-model numbers still diverge well beyond the +provisional fail band: + +| Metric | TAS | OpenStudio | Rel. diff | Band | Gates? | +|---|---:|---:|---:|---|---| +| Annual heating (kWh) | 4331.96 | 1751.18 | **59.6 %** | Fail | **yes** | +| Annual cooling (kWh) | 0 | 5.25 | 100 % | Fail | **yes** | +| Peak heating load (kW) | 3.934 | 5.721 | **31.2 %** | Fail | no — designated informational | +| Peak cooling load (kW) | 0 | 0.040 | 100 % | Fail | no — designated informational | +| Peak heating hour | 919 | 775 | 144 h | Fail | no — `hourOfYear` | +| Floor area / volume | — | — | ~2e-7 | Match | yes | + +The whole-model peak loads and peak hours are banded and shown but excluded from the numerical status +(TOLERANCES.md → Informational metrics), so they are *not* what fails this comparison. The numerical +status is `Fail` on the gating metrics alone — annual heating and cooling, plus per-space peak loads +and unmet hours — which is why narrowing the exclusion did not rescue the result. + +Per-space heating peaks diverge similarly (sampled: 29 %, 52 %). Note TAS reports a *measured zero* +for cooling on this heating-dominated model, so the cooling rows compare 0 against a near-zero +OpenStudio value — the 100 % figures there are not meaningful magnitudes. + +**Conclusion:** the D10 candidate pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD +sizing) is **not** validated by this run and must remain labelled a *candidate*. The design-day +reporting defect above is a plausible contributor (a differing sizing basis changes loads), so the +pairing should be re-assessed only after that fix, and then investigated properly against the +B4 corpus — not by tuning tolerances. + +Two further observations worth carrying into that work: + +- TAS emits `peakCoolingHour = 0` with `available: true` while its cooling load is a measured zero; + a peak *hour* for a zero load carries no information and would be better reported unavailable + (a B2 producer-semantics question, not a comparator one). +- One space reported `heating.peakLoad` as available `0` from OpenStudio while TAS reported it + unavailable, i.e. the two engines disagree about which spaces are conditioned. + +## Status + +- **B3 comparator: validated on real two-engine data.** Alignment, banding, circular hours, + reconciliation, availability handling, determinism and the provenance gate all behaved as specified. +- **Engine equivalence: open.** Deliberately out of B3's scope; it needs the producer fix, then the + B4 corpus and an energy-modeller review. + +Artefacts in this folder are the verbatim outputs of the run above: the two neutral input documents +and the three generated reports. diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-OpenStudio.json b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-OpenStudio.json new file mode 100644 index 0000000..b8f336d --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-OpenStudio.json @@ -0,0 +1,888 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "3de8a4c7793f96d2118513bfb4fb42349a89ca1f", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "25.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "Budapest", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "None", + "runTimestampUtc": "2026-07-25T08:10:07.0886114Z", + "durationSeconds": 21.1734065, + "state": "Success", + "resultSources": [ + "SAM.Analytical.OpenStudio" + ], + "warnings": [], + "notes": [] + }, + "model": { + "consumptionHeating": { + "value": 1751.1764825992748, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": 5.2499930426550705, + "unit": "kWh", + "available": true + }, + "peakHeatingLoad": { + "value": 5.720615732872154, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 775, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 0.0402250938503556, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 7974, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 167.95195207345205, + "unit": "m2", + "available": true + }, + "volume": { + "value": 470.1821620538829, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "025eb80f65d745b799511b78ac2f82fb", + "name": "01_18 Void", + "area": { + "value": 6.6920779999999995, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.118059598283878, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "name": "01_13 Bedroom", + "area": { + "value": 21.280010175465385, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.33913699268221, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 1185.0374713731348, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 15.588918884437554, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 7974, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "name": "GF_08 Bathroom", + "area": { + "value": 3.647967799712404, + "unit": "m2", + "available": true + }, + "volume": { + "value": 10.396708229180378, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 141.7703965184828, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 918, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "name": "01_15 Plantroom", + "area": { + "value": 2.9450000000000016, + "unit": "m2", + "available": true + }, + "volume": { + "value": 5.746544632499992, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "name": "GF_09 WC", + "area": { + "value": 2.355387206192353, + "unit": "m2", + "available": true + }, + "volume": { + "value": 6.712853537648204, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "354b8786b4714f3c9941cec051a93361", + "name": "GF_10 Closet", + "area": { + "value": 3.0986077487352315, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.007679927026205, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 262.9986126690514, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "name": "01_11 Void", + "area": { + "value": 5.03787628653, + "unit": "m2", + "available": true + }, + "volume": { + "value": 7.564547888993382, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "6e7ef8487b714da3b730e0f226af927e", + "name": "01_17 Gallery", + "area": { + "value": 23.55004650825489, + "unit": "m2", + "available": true + }, + "volume": { + "value": 58.340316505330264, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 255.3860902131586, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "name": "01_16 Bathroom", + "area": { + "value": 9.409999999999991, + "unit": "m2", + "available": true + }, + "volume": { + "value": 26.966204214472434, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 190.23285633880042, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "name": "GF_05 Stairs", + "area": { + "value": 4.634866891556, + "unit": "m2", + "available": true + }, + "volume": { + "value": 28.16450759620351, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 39.29050142981706, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 774, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "name": "GF_12 EntranceHall", + "area": { + "value": 6.955039759539971, + "unit": "m2", + "available": true + }, + "volume": { + "value": 20.363019689898447, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 133.44180277588939, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 774, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "name": "GF_07 LivingRoom", + "area": { + "value": 38.98503142608865, + "unit": "m2", + "available": true + }, + "volume": { + "value": 121.59536233412693, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 1789.9830426085152, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 775, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "name": "GF_11 Bedroom", + "area": { + "value": 18.080018143643976, + "unit": "m2", + "available": true + }, + "volume": { + "value": 51.52805170938531, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 936.5975380258114, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 727, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 28.70645886203079, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 7991, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "name": "01_14 Bedroom", + "area": { + "value": 21.280022127733165, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.3391691981517, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 989.0652526225581, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + } + ] +} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-TAS.json b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-TAS.json new file mode 100644 index 0000000..eda7055 --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-TAS.json @@ -0,0 +1,890 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", + "engine": { + "kind": "TAS", + "name": "Tas", + "version": "9.5.7.0", + "sdkVersion": null + }, + "route": "Native-TAS", + "weather": { + "identity": "Budapest", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-25T08:08:24.9884006Z", + "durationSeconds": 58.4307425, + "state": "Success", + "resultSources": [ + "SAM.Analytical.Tas" + ], + "warnings": [], + "notes": [ + "Shared gbXML was exported from the source model (no --gbxml supplied)." + ] + }, + "model": { + "consumptionHeating": { + "value": 4331.961588091359, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": 0, + "unit": "kWh", + "available": true + }, + "peakHeatingLoad": { + "value": 3.93393212890625, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 0, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 167.95195245742798, + "unit": "m2", + "available": true + }, + "volume": { + "value": 470.1821608543396, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "025eb80f65d745b799511b78ac2f82fb", + "name": "01_18 Void", + "area": { + "value": 6.6920779999999995, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.118059598283878, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "name": "01_13 Bedroom", + "area": { + "value": 21.280010175465385, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.33913699268221, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 574.0481567382812, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 565.50927734375, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 65, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "name": "GF_08 Bathroom", + "area": { + "value": 3.647967799712404, + "unit": "m2", + "available": true + }, + "volume": { + "value": 10.396708229180378, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 199.83349609375, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 199.82528686523438, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 4761, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "name": "01_15 Plantroom", + "area": { + "value": 2.9450000000000016, + "unit": "m2", + "available": true + }, + "volume": { + "value": 5.746544632499992, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 28.741456985473633, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "name": "GF_09 WC", + "area": { + "value": 2.355387206192353, + "unit": "m2", + "available": true + }, + "volume": { + "value": 6.712853537648204, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 69.60518646240234, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 52.024513244628906, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "354b8786b4714f3c9941cec051a93361", + "name": "GF_10 Closet", + "area": { + "value": 3.0986077487352315, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.007679927026205, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 137.82980346679688, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 117.870849609375, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 67, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "name": "01_11 Void", + "area": { + "value": 5.03787628653, + "unit": "m2", + "available": true + }, + "volume": { + "value": 7.564547888993382, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "6e7ef8487b714da3b730e0f226af927e", + "name": "01_17 Gallery", + "area": { + "value": 23.55004650825489, + "unit": "m2", + "available": true + }, + "volume": { + "value": 58.340316505330264, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 379.9020080566406, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 132.86868286132812, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1442, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "name": "01_16 Bathroom", + "area": { + "value": 9.409999999999991, + "unit": "m2", + "available": true + }, + "volume": { + "value": 26.966204214472434, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 366.3569641113281, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 366.33624267578125, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1444, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "name": "GF_05 Stairs", + "area": { + "value": 4.634866891556, + "unit": "m2", + "available": true + }, + "volume": { + "value": 28.16450759620351, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 69.23214721679688, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 11.025825500488281, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1452, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "name": "GF_12 EntranceHall", + "area": { + "value": 6.955039759539971, + "unit": "m2", + "available": true + }, + "volume": { + "value": 20.363019689898447, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 204.9483642578125, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 117.45973205566406, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1447, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "name": "GF_07 LivingRoom", + "area": { + "value": 38.98503142608865, + "unit": "m2", + "available": true + }, + "volume": { + "value": 121.59536233412693, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 1065.076171875, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 1025.7105712890625, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1443, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 114, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "name": "GF_11 Bedroom", + "area": { + "value": 18.080018143643976, + "unit": "m2", + "available": true + }, + "volume": { + "value": 51.52805170938531, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 564.768798828125, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 559.67822265625, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 69, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "name": "01_14 Bedroom", + "area": { + "value": 21.280022127733165, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.3391691981517, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 549.6607055664062, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 529.828857421875, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1457, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 29, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + } + ] +} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison-summary.json b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison-summary.json new file mode 100644 index 0000000..5c4980e --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison-summary.json @@ -0,0 +1,3734 @@ +{ + "summarySchemaVersion": "1.0.0", + "toleranceProfile": "default", + "toleranceProfileDetail": { + "name": "default", + "warnRelative": 0.05, + "failRelative": 0.15, + "hourWarnAbsolute": 1, + "hourFailAbsolute": 24, + "defaultNearZeroFloor": 0, + "nearZeroFloors": { + "kWh": 1, + "Wh": 1000, + "kW": 0.01, + "W": 10, + "m2": 0.01, + "m3": 0.01, + "h": 0.5 + } + }, + "gate": "Fail", + "statuses": { + "overall": "Fail", + "numerical": "Fail", + "coverage": "Warn", + "provenance": "Fail", + "reconciliation": "Pass" + }, + "provenanceCompatibility": { + "compatible": false, + "mismatches": [ + { + "field": "designDaySource", + "tas": "EmbeddedModel", + "openStudio": "None" + } + ] + }, + "counts": { + "match": 48, + "warn": 1, + "fail": 33, + "notApplicable": 66 + }, + "coverage": { + "status": "Warn", + "requiredMetricCount": 148, + "comparableMetricCount": 82, + "unavailableRequiredMetricCount": 66, + "unmatchedSpaceCount": 0, + "ambiguousSpaceCount": 0, + "duplicateSpaceIdentityCount": 0 + }, + "schema": { + "tas": "1.0.0", + "openStudio": "1.0.0", + "driftNote": null + }, + "provenance": { + "tas": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", + "engine": { + "kind": "TAS", + "name": "Tas", + "version": "9.5.7.0", + "sdkVersion": null + }, + "route": "Native-TAS", + "weather": { + "identity": "Budapest", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-25T08:08:24.9884006Z", + "durationSeconds": 58.4307425, + "state": "Success", + "resultSources": [ + "SAM.Analytical.Tas" + ], + "warnings": [], + "notes": [ + "Shared gbXML was exported from the source model (no --gbxml supplied)." + ] + }, + "openStudio": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "3de8a4c7793f96d2118513bfb4fb42349a89ca1f", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "25.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "Budapest", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "None", + "runTimestampUtc": "2026-07-25T08:10:07.0886114Z", + "durationSeconds": 21.1734065, + "state": "Success", + "resultSources": [ + "SAM.Analytical.OpenStudio" + ], + "warnings": [], + "notes": [] + } + }, + "modelMetrics": [ + { + "scope": "model", + "key": "consumptionHeating", + "unit": "kWh", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4331.961588091359, + "available": true, + "unit": "kWh" + }, + "openStudio": { + "value": 1751.1764825992748, + "available": true, + "unit": "kWh" + }, + "absoluteDifference": 2580.785105492084, + "relativeDifference": 0.5957543835537944, + "signedDifference": -2580.785105492084 + }, + { + "scope": "model", + "key": "consumptionCooling", + "unit": "kWh", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "kWh" + }, + "openStudio": { + "value": 5.2499930426550705, + "available": true, + "unit": "kWh" + }, + "absoluteDifference": 5.2499930426550705, + "relativeDifference": 1, + "signedDifference": 5.2499930426550705 + }, + { + "scope": "model", + "key": "peakHeatingLoad", + "unit": "kW", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.93393212890625, + "available": true, + "unit": "kW" + }, + "openStudio": { + "value": 5.720615732872154, + "available": true, + "unit": "kW" + }, + "absoluteDifference": 1.7866836039659035, + "relativeDifference": 0.3123236531513824, + "signedDifference": 1.7866836039659035 + }, + { + "scope": "model", + "key": "peakHeatingHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 775, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 144, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "peakCoolingLoad", + "unit": "kW", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "kW" + }, + "openStudio": { + "value": 0.0402250938503556, + "available": true, + "unit": "kW" + }, + "absoluteDifference": 0.0402250938503556, + "relativeDifference": 1, + "signedDifference": 0.0402250938503556 + }, + { + "scope": "model", + "key": "peakCoolingHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7974, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 786, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "floorArea", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 167.95195245742798, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 167.95195207345205, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 3.839759301627055E-07, + "relativeDifference": 2.2862248669602973E-09, + "signedDifference": -3.839759301627055E-07 + }, + { + "scope": "model", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 470.1821608543396, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 470.1821620538829, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 1.1995433055744797E-06, + "relativeDifference": 2.5512309959496336E-09, + "signedDifference": 1.1995433055744797E-06 + } + ], + "spaces": [ + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "guid": "025eb80f65d745b799511b78ac2f82fb", + "tasName": "01_18 Void", + "openStudioName": "01_18 Void", + "matchKind": "Guid", + "metrics": [ + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.6920779999999995, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 6.6920779999999995, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.118059598283878, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 9.118059598283878, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "tasName": "01_13 Bedroom", + "openStudioName": "01_13 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 21.280010175465385, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 21.280010175465385, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 57.33913699268221, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 57.33913699268221, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 574.0481567382812, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 565.50927734375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 1185.0374713731348, + "available": true, + "unit": "W" + }, + "absoluteDifference": 619.5281940293848, + "relativeDifference": 0.5227920711330089, + "signedDifference": 619.5281940293848 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 522, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 65, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 65, + "relativeDifference": 1, + "signedDifference": -65 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 15.588918884437554, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7974, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "tasName": "GF_08 Bathroom", + "openStudioName": "GF_08 Bathroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.647967799712404, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 3.647967799712404, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 10.396708229180378, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 10.396708229180378, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 199.83349609375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 199.82528686523438, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 141.7703965184828, + "available": true, + "unit": "W" + }, + "absoluteDifference": 58.05489034675156, + "relativeDifference": 0.2905282472378222, + "signedDifference": -58.05489034675156 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 918, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 523, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4761, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 4761, + "relativeDifference": 1, + "signedDifference": -4761 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "tasName": "01_15 Plantroom", + "openStudioName": "01_15 Plantroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 2.9450000000000016, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 2.9450000000000016, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 5.746544632499992, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 5.746544632499992, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 28.741456985473633, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "tasName": "GF_09 WC", + "openStudioName": "GF_09 WC", + "matchKind": "Guid", + "metrics": [ + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 2.355387206192353, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 2.355387206192353, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.712853537648204, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 6.712853537648204, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 69.60518646240234, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 52.024513244628906, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": 52.024513244628906, + "relativeDifference": 1, + "signedDifference": -52.024513244628906 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 1441, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "guid": "354b8786b4714f3c9941cec051a93361", + "tasName": "GF_10 Closet", + "openStudioName": "GF_10 Closet", + "matchKind": "Guid", + "metrics": [ + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.0986077487352315, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 3.0986077487352315, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.007679927026205, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 9.007679927026205, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 137.82980346679688, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 117.870849609375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 262.9986126690514, + "available": true, + "unit": "W" + }, + "absoluteDifference": 145.1277630596764, + "relativeDifference": 0.5518195004408647, + "signedDifference": 145.1277630596764 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 522, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 67, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 67, + "relativeDifference": 1, + "signedDifference": -67 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "tasName": "01_11 Void", + "openStudioName": "01_11 Void", + "matchKind": "Guid", + "metrics": [ + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 5.03787628653, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 5.03787628653, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 7.564547888993382, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 7.564547888993382, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "guid": "6e7ef8487b714da3b730e0f226af927e", + "tasName": "01_17 Gallery", + "openStudioName": "01_17 Gallery", + "matchKind": "Guid", + "metrics": [ + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 23.55004650825489, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 23.55004650825489, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 58.340316505330264, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 58.340316505330264, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 379.9020080566406, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 132.86868286132812, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 255.3860902131586, + "available": true, + "unit": "W" + }, + "absoluteDifference": 122.51740735183049, + "relativeDifference": 0.4797340655850561, + "signedDifference": 122.51740735183049 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1442, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 523, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "tasName": "01_16 Bathroom", + "openStudioName": "01_16 Bathroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.409999999999991, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 9.409999999999991, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 26.966204214472434, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 26.966204214472434, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 366.3569641113281, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 366.33624267578125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 190.23285633880042, + "available": true, + "unit": "W" + }, + "absoluteDifference": 176.10338633698083, + "relativeDifference": 0.4807151622528315, + "signedDifference": -176.10338633698083 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1444, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 525, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "tasName": "GF_05 Stairs", + "openStudioName": "GF_05 Stairs", + "matchKind": "Guid", + "metrics": [ + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4.634866891556, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 4.634866891556, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 28.16450759620351, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 28.16450759620351, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 69.23214721679688, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 11.025825500488281, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 39.29050142981706, + "available": true, + "unit": "W" + }, + "absoluteDifference": 28.26467592932878, + "relativeDifference": 0.7193768188430163, + "signedDifference": 28.26467592932878 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1452, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 774, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 678, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "tasName": "GF_12 EntranceHall", + "openStudioName": "GF_12 EntranceHall", + "matchKind": "Guid", + "metrics": [ + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.955039759539971, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 6.955039759539971, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 20.363019689898447, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 20.363019689898447, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 204.9483642578125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Warn", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 117.45973205566406, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 133.44180277588939, + "available": true, + "unit": "W" + }, + "absoluteDifference": 15.982070720225323, + "relativeDifference": 0.1197680965616646, + "signedDifference": 15.982070720225323 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1447, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 774, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 673, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "tasName": "GF_07 LivingRoom", + "openStudioName": "GF_07 LivingRoom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 38.98503142608865, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 38.98503142608865, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 121.59536233412693, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 121.59536233412693, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 1065.076171875, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 1025.7105712890625, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 1789.9830426085152, + "available": true, + "unit": "W" + }, + "absoluteDifference": 764.2724713194527, + "relativeDifference": 0.4269719059492821, + "signedDifference": 764.2724713194527 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1443, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 775, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 668, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 114, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 114, + "relativeDifference": 1, + "signedDifference": -114 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "tasName": "GF_11 Bedroom", + "openStudioName": "GF_11 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 18.080018143643976, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 18.080018143643976, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 51.52805170938531, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 51.52805170938531, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 564.768798828125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 559.67822265625, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 936.5975380258114, + "available": true, + "unit": "W" + }, + "absoluteDifference": 376.91931536956145, + "relativeDifference": 0.4024346638408247, + "signedDifference": 376.91931536956145 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 727, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 714, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 69, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 69, + "relativeDifference": 1, + "signedDifference": -69 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 28.70645886203079, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7991, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "tasName": "01_14 Bedroom", + "openStudioName": "01_14 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 21.280022127733165, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 21.280022127733165, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 57.3391691981517, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 57.3391691981517, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 549.6607055664062, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 529.828857421875, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 989.0652526225581, + "available": true, + "unit": "W" + }, + "absoluteDifference": 459.2363952006831, + "relativeDifference": 0.4643135465359781, + "signedDifference": 459.2363952006831 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1457, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 538, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 29, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 29, + "relativeDifference": 1, + "signedDifference": -29 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + } + ], + "spaceDiagnostics": { + "tasCount": 14, + "openStudioCount": 14, + "matchedByGuid": 14, + "matchedByName": 0, + "onlyInTas": [], + "onlyInOpenStudio": [], + "duplicateTasGuids": [], + "duplicateOpenStudioGuids": [], + "duplicateTasNames": [], + "duplicateOpenStudioNames": [], + "ambiguousNameMatches": [] + }, + "reconciliation": [ + { + "engine": "TAS", + "key": "floorArea", + "unit": "m2", + "modelTotal": 167.95195245742798, + "modelTotalAvailable": true, + "sumOfSpaces": 167.95195207345202, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 3.839759585844149E-07, + "relativeDifference": 2.2862250361855374E-09, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "TAS", + "key": "volume", + "unit": "m3", + "modelTotal": 470.1821608543396, + "modelTotalAvailable": true, + "sumOfSpaces": 470.1821620538828, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 1.199543191887642E-06, + "relativeDifference": 2.5512307541564592E-09, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "OpenStudio", + "key": "floorArea", + "unit": "m2", + "modelTotal": 167.95195207345205, + "modelTotalAvailable": true, + "sumOfSpaces": 167.95195207345202, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 2.842170943040401E-14, + "relativeDifference": 1.6922524019235018E-16, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "OpenStudio", + "key": "volume", + "unit": "m3", + "modelTotal": 470.1821620538829, + "modelTotalAvailable": true, + "sumOfSpaces": 470.1821620538828, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 1.1368683772161603E-13, + "relativeDifference": 2.417931748516387E-16, + "band": "Match", + "notApplicableReason": null + } + ] +} diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.csv b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.csv new file mode 100644 index 0000000..8406556 --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.csv @@ -0,0 +1,149 @@ +scope,metric,unit,availability,tasValue,openStudioValue,absoluteDifference,relativeDifference,relativePercent,signedDifference,band,note,toleranceProfile,warnRelative,failRelative,hourWarnAbsolute,hourFailAbsolute,nearZeroFloor,informational +model,consumptionHeating,kWh,Both,4331.961588091359,1751.1764825992748,2580.785105492084,0.5957543835537944,59.57543835537944,-2580.785105492084,Fail,,default,0.05,0.15,1,24,1,false +model,consumptionCooling,kWh,Both,0,5.2499930426550705,5.2499930426550705,1,100,5.2499930426550705,Fail,,default,0.05,0.15,1,24,1,false +model,peakHeatingLoad,kW,Both,3.93393212890625,5.720615732872154,1.7866836039659035,0.3123236531513824,31.23236531513824,1.7866836039659035,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true +model,peakHeatingHour,hourOfYear,Both,919,775,144,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +model,peakCoolingLoad,kW,Both,0,0.0402250938503556,0.0402250938503556,1,100,0.0402250938503556,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true +model,peakCoolingHour,hourOfYear,Both,0,7974,786,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +model,floorArea,m2,Both,167.95195245742798,167.95195207345205,3.839759301627055E-07,2.2862248669602973E-09,2.2862248669602975E-07,-3.839759301627055E-07,Match,,default,0.05,0.15,1,24,0.01,false +model,volume,m3,Both,470.1821608543396,470.1821620538829,1.1995433055744797E-06,2.5512309959496336E-09,2.5512309959496334E-07,1.1995433055744797E-06,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,area,m2,Both,6.6920779999999995,6.6920779999999995,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,volume,m3,Both,9.118059598283878,9.118059598283878,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +025eb80f65d745b799511b78ac2f82fb,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +025eb80f65d745b799511b78ac2f82fb,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +025eb80f65d745b799511b78ac2f82fb,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +0872cd39962f43c3b62a6e031a76c24a,area,m2,Both,21.280010175465385,21.280010175465385,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0872cd39962f43c3b62a6e031a76c24a,volume,m3,Both,57.33913699268221,57.33913699268221,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0872cd39962f43c3b62a6e031a76c24a,heating.designLoad,W,TasOnly,574.0481567382812,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,heating.peakLoad,W,Both,565.50927734375,1185.0374713731348,619.5281940293848,0.5227920711330089,52.279207113300885,619.5281940293848,Fail,,default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +0872cd39962f43c3b62a6e031a76c24a,heating.unmetHours,h,Both,65,0,65,1,100,-65,Fail,,default,0.05,0.15,1,24,0.5,false +0872cd39962f43c3b62a6e031a76c24a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,cooling.peakLoad,W,OpenStudioOnly,,15.588918884437554,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,cooling.peakHour,hourOfYear,OpenStudioOnly,,7974,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +0872cd39962f43c3b62a6e031a76c24a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +13a0403ab3a543608613b0ce9bf52d92,area,m2,Both,3.647967799712404,3.647967799712404,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +13a0403ab3a543608613b0ce9bf52d92,volume,m3,Both,10.396708229180378,10.396708229180378,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +13a0403ab3a543608613b0ce9bf52d92,heating.designLoad,W,TasOnly,199.83349609375,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,heating.peakLoad,W,Both,199.82528686523438,141.7703965184828,58.05489034675156,0.2905282472378222,29.052824723782223,-58.05489034675156,Fail,,default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,heating.peakHour,hourOfYear,Both,1441,918,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +13a0403ab3a543608613b0ce9bf52d92,heating.unmetHours,h,Both,4761,0,4761,1,100,-4761,Fail,,default,0.05,0.15,1,24,0.5,false +13a0403ab3a543608613b0ce9bf52d92,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +13a0403ab3a543608613b0ce9bf52d92,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +21bdbb806b4f4e2fbad0632b19fdf14b,area,m2,Both,2.9450000000000016,2.9450000000000016,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +21bdbb806b4f4e2fbad0632b19fdf14b,volume,m3,Both,5.746544632499992,5.746544632499992,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.designLoad,W,TasOnly,28.741456985473633,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +21bdbb806b4f4e2fbad0632b19fdf14b,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +312fab2f1ba64756b47ccc3dcb21ac8a,area,m2,Both,2.355387206192353,2.355387206192353,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +312fab2f1ba64756b47ccc3dcb21ac8a,volume,m3,Both,6.712853537648204,6.712853537648204,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.designLoad,W,TasOnly,69.60518646240234,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakLoad,W,Both,52.024513244628906,0,52.024513244628906,1,100,-52.024513244628906,Fail,,default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakHour,hourOfYear,Both,1441,0,1441,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +312fab2f1ba64756b47ccc3dcb21ac8a,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +354b8786b4714f3c9941cec051a93361,area,m2,Both,3.0986077487352315,3.0986077487352315,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +354b8786b4714f3c9941cec051a93361,volume,m3,Both,9.007679927026205,9.007679927026205,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +354b8786b4714f3c9941cec051a93361,heating.designLoad,W,TasOnly,137.82980346679688,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,heating.peakLoad,W,Both,117.870849609375,262.9986126690514,145.1277630596764,0.5518195004408647,55.18195004408647,145.1277630596764,Fail,,default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +354b8786b4714f3c9941cec051a93361,heating.unmetHours,h,Both,67,0,67,1,100,-67,Fail,,default,0.05,0.15,1,24,0.5,false +354b8786b4714f3c9941cec051a93361,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +354b8786b4714f3c9941cec051a93361,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +4377cbd6712349e68fdae8814ff9c5d9,area,m2,Both,5.03787628653,5.03787628653,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +4377cbd6712349e68fdae8814ff9c5d9,volume,m3,Both,7.564547888993382,7.564547888993382,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +4377cbd6712349e68fdae8814ff9c5d9,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +4377cbd6712349e68fdae8814ff9c5d9,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +4377cbd6712349e68fdae8814ff9c5d9,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +6e7ef8487b714da3b730e0f226af927e,area,m2,Both,23.55004650825489,23.55004650825489,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +6e7ef8487b714da3b730e0f226af927e,volume,m3,Both,58.340316505330264,58.340316505330264,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +6e7ef8487b714da3b730e0f226af927e,heating.designLoad,W,TasOnly,379.9020080566406,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,heating.peakLoad,W,Both,132.86868286132812,255.3860902131586,122.51740735183049,0.4797340655850561,47.97340655850561,122.51740735183049,Fail,,default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,heating.peakHour,hourOfYear,Both,1442,919,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +6e7ef8487b714da3b730e0f226af927e,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +6e7ef8487b714da3b730e0f226af927e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +6e7ef8487b714da3b730e0f226af927e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +7ee49aac87b24671bb00ed1b291806a1,area,m2,Both,9.409999999999991,9.409999999999991,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +7ee49aac87b24671bb00ed1b291806a1,volume,m3,Both,26.966204214472434,26.966204214472434,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +7ee49aac87b24671bb00ed1b291806a1,heating.designLoad,W,TasOnly,366.3569641113281,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,heating.peakLoad,W,Both,366.33624267578125,190.23285633880042,176.10338633698083,0.4807151622528315,48.07151622528315,-176.10338633698083,Fail,,default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,heating.peakHour,hourOfYear,Both,1444,919,525,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +7ee49aac87b24671bb00ed1b291806a1,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +7ee49aac87b24671bb00ed1b291806a1,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +7ee49aac87b24671bb00ed1b291806a1,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +8d2d2a0234584fe4b171e5a9373f1b4d,area,m2,Both,4.634866891556,4.634866891556,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +8d2d2a0234584fe4b171e5a9373f1b4d,volume,m3,Both,28.16450759620351,28.16450759620351,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.designLoad,W,TasOnly,69.23214721679688,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakLoad,W,Both,11.025825500488281,39.29050142981706,28.26467592932878,0.7193768188430163,71.93768188430163,28.26467592932878,Fail,,default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakHour,hourOfYear,Both,1452,774,678,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +8d2d2a0234584fe4b171e5a9373f1b4d,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +affc43b0b5b3445f85d5cea8a7b88d4f,area,m2,Both,6.955039759539971,6.955039759539971,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +affc43b0b5b3445f85d5cea8a7b88d4f,volume,m3,Both,20.363019689898447,20.363019689898447,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.designLoad,W,TasOnly,204.9483642578125,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakLoad,W,Both,117.45973205566406,133.44180277588939,15.982070720225323,0.1197680965616646,11.97680965616646,15.982070720225323,Warn,,default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakHour,hourOfYear,Both,1447,774,673,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +affc43b0b5b3445f85d5cea8a7b88d4f,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,area,m2,Both,38.98503142608865,38.98503142608865,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,volume,m3,Both,121.59536233412693,121.59536233412693,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.designLoad,W,TasOnly,1065.076171875,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakLoad,W,Both,1025.7105712890625,1789.9830426085152,764.2724713194527,0.4269719059492821,42.69719059492821,764.2724713194527,Fail,,default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakHour,hourOfYear,Both,1443,775,668,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.unmetHours,h,Both,114,0,114,1,100,-114,Fail,,default,0.05,0.15,1,24,0.5,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +d8294f2a30b445ca9f6ba83c8dcf4903,area,m2,Both,18.080018143643976,18.080018143643976,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d8294f2a30b445ca9f6ba83c8dcf4903,volume,m3,Both,51.52805170938531,51.52805170938531,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.designLoad,W,TasOnly,564.768798828125,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakLoad,W,Both,559.67822265625,936.5975380258114,376.91931536956145,0.4024346638408247,40.243466384082474,376.91931536956145,Fail,,default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakHour,hourOfYear,Both,1441,727,714,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +d8294f2a30b445ca9f6ba83c8dcf4903,heating.unmetHours,h,Both,69,0,69,1,100,-69,Fail,,default,0.05,0.15,1,24,0.5,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakLoad,W,OpenStudioOnly,,28.70645886203079,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakHour,hourOfYear,OpenStudioOnly,,7991,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,area,m2,Both,21.280022127733165,21.280022127733165,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,volume,m3,Both,57.3391691981517,57.3391691981517,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.designLoad,W,TasOnly,549.6607055664062,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakLoad,W,Both,529.828857421875,989.0652526225581,459.2363952006831,0.4643135465359781,46.431354653597815,459.2363952006831,Fail,,default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakHour,hourOfYear,Both,1457,919,538,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.unmetHours,h,Both,29,0,29,1,100,-29,Fail,,default,0.05,0.15,1,24,0.5,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.md b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.md new file mode 100644 index 0000000..2966595 --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.md @@ -0,0 +1,296 @@ +# Benchmark comparison: TAS vs OpenStudio + +Independent comparison of two engine-neutral benchmark documents. Tolerance bands are +**provisional reporting buckets**, not validated thresholds. The overall gate is the worst of +the numerical, coverage, provenance and reconciliation statuses. Peak-hour bands and the two +whole-model peak-load bands are informational and are excluded from the numerical status — they are +still calculated and shown, and rows carrying such a band are marked in the Note column; +reconciliation is a separate within-document diagnostic status that also contributes to the +overall gate. + +## Summary + +| Field | Value | +| --- | --- | +| Tolerance profile | default | +| Gate status (overall) | Fail | +| Numerical status | Fail | +| Coverage status | Warn | +| Provenance status | Fail | +| Reconciliation status | Pass | +| Metrics matched | 48 | +| Metrics warned | 1 | +| Metrics failed | 33 | +| Metrics not applicable | 66 | +| Required metrics compared | 82 of 148 | +| Required metrics unavailable | 66 | +| TAS schema version | 1.0.0 | +| OpenStudio schema version | 1.0.0 | + +## Coverage + +How much of the two runs was actually compared. Required metrics are the whole-model metrics plus +the metrics of uniquely matched spaces; the metrics of one-sided, duplicated or ambiguous spaces are +not counted here because those spaces are reported under space alignment instead. An unavailable +metric is never a numerical failure, so it is recorded here — otherwise missing results could read as +a clean pass. + +| Field | Value | +| --- | --- | +| Coverage status | Warn | +| Required metrics | 148 | +| Compared | 82 | +| Unavailable (not compared) | 66 | +| Unmatched spaces | 0 | +| Ambiguous (split) space names | 0 | +| Duplicated space identities | 0 | + +**Coverage is INCOMPLETE**, so this comparison must not be described as a complete result: + +- 66 of 148 required metrics were unavailable on at least one side (reported as N/A, never as a numerical failure). + +## Provenance compatibility + +**INCOMPATIBLE** — the two documents are not a valid comparison pair, so a numerical pass is not meaningful. + +| Field | TAS | OpenStudio | +| --- | --- | --- | +| designDaySource | EmbeddedModel | None | + +## Tolerance profile + +Provisional reporting bands (not validated thresholds). Recorded so a changed profile is never hidden behind an unchanged name. + +| Setting | Value | +| --- | --- | +| Name | default | +| Warn (relative) | 0.05 | +| Fail (relative) | 0.15 | +| Peak-hour warn (h) | 1 | +| Peak-hour fail (h) | 24 | +| Default near-zero floor | 0 | +| Near-zero floor (kWh) | 1 | +| Near-zero floor (Wh) | 1000 | +| Near-zero floor (kW) | 0.01 | +| Near-zero floor (W) | 10 | +| Near-zero floor (m2) | 0.01 | +| Near-zero floor (m3) | 0.01 | +| Near-zero floor (h) | 0.5 | + +## Provenance + +| Field | TAS | OpenStudio | +| --- | --- | --- | +| Engine kind | TAS | OpenStudio | +| Engine name | Tas | EnergyPlus | +| Engine version | 9.5.7.0 | 25.1.0 | +| SDK version | | 3.10.0 | +| Route | Native-TAS | Native-OpenStudio | +| Weather | Budapest | Budapest | +| Design-day source | EmbeddedModel | None | +| Run state | Success | Success | +| Source model | 000000_SAM_AnalyticalModel | 000000_SAM_AnalyticalModel | +| Source model GUID | a527e890fceb465fbb50f4b6874f3a4c | a527e890fceb465fbb50f4b6874f3a4c | +| SAM commit | abcd617887f6792047023820f3efd2463fc5c6b3 | abcd617887f6792047023820f3efd2463fc5c6b3 | + +## Model metrics + +> **Caveats (apply even when values pass):** whole-model peak-load semantics differ between +> engines — OpenStudio reports a coincident total, TAS a building-profile maximum — so peak-load +> comparisons are informational, not equivalence claims. `peakHeatingLoad` and `peakCoolingLoad` +> are therefore banded and shown below but **excluded from the numerical status**, exactly like the +> peak-hour metrics; promoting them into the gate needs the B4 corpus and energy-modeller review. +> Every other metric — annual energy, per-space peak loads, unmet hours, geometry — gates normally. +> The primary conditioning pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing) +> is a **candidate** pending validation. A tolerance profile cannot remove these limitations. + +| Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | +| --- | --- | --- | --- | --- | --- | --- | --- | +| consumptionHeating | kWh | 4331.961588091359 | 1751.1764825992748 | 2580.785105492084 | 59.57543835537944 | Fail | | +| consumptionCooling | kWh | 0 | 5.2499930426550705 | 5.2499930426550705 | 100 | Fail | | +| peakHeatingLoad | kW | 3.93393212890625 | 5.720615732872154 | 1.7866836039659035 | 31.23236531513824 | Fail | informational (excluded from the numerical status) | +| peakHeatingHour | hourOfYear | 919 | 775 | 144 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| peakCoolingLoad | kW | 0 | 0.0402250938503556 | 0.0402250938503556 | 100 | Fail | informational (excluded from the numerical status) | +| peakCoolingHour | hourOfYear | 0 | 7974 | 786 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| floorArea | m2 | 167.95195245742798 | 167.95195207345205 | 3.839759301627055E-07 | 2.2862248669602975E-07 | Match | | +| volume | m3 | 470.1821608543396 | 470.1821620538829 | 1.1995433055744797E-06 | 2.5512309959496334E-07 | Match | | + +## Model-total vs sum-of-spaces reconciliation + +Additive quantities only; non-additive peak loads are not reconciled. Only uniquely-matched spaces are summed; unmatched or ambiguous spaces make a reconciliation incomplete. + +| Engine | Quantity | Unit | Model total | Sum of spaces | Abs diff | Band | Used/Missing/Excluded | Complete | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TAS | floorArea | m2 | 167.95195245742798 | 167.95195207345202 | 3.839759585844149E-07 | Match | 14/0/0 | yes | +| TAS | volume | m3 | 470.1821608543396 | 470.1821620538828 | 1.199543191887642E-06 | Match | 14/0/0 | yes | +| OpenStudio | floorArea | m2 | 167.95195207345205 | 167.95195207345202 | 2.842170943040401E-14 | Match | 14/0/0 | yes | +| OpenStudio | volume | m3 | 470.1821620538829 | 470.1821620538828 | 1.1368683772161603E-13 | Match | 14/0/0 | yes | + +## Space alignment + +| Field | Value | +| --- | --- | +| TAS spaces | 14 | +| OpenStudio spaces | 14 | +| Matched by GUID | 14 | +| Matched by name | 0 | + +**Only in TAS:** none + +**Only in OpenStudio:** none + +**Duplicate TAS GUIDs:** none + +**Duplicate OpenStudio GUIDs:** none + +**Duplicate TAS names:** none + +**Duplicate OpenStudio names:** none + +**Ambiguous (split) name matches:** none + +## Space metrics + +| Space | Match | Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 025eb80f65d745b799511b78ac2f82fb | Guid | area | m2 | 6.6920779999999995 | 6.6920779999999995 | 0 | 0 | Match | | +| 025eb80f65d745b799511b78ac2f82fb | Guid | volume | m3 | 9.118059598283878 | 9.118059598283878 | 0 | 0 | Match | | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | area | m2 | 21.280010175465385 | 21.280010175465385 | 0 | 0 | Match | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | volume | m3 | 57.33913699268221 | 57.33913699268221 | 0 | 0 | Match | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.designLoad | W | 574.0481567382812 | | | | N/A | unavailable (TasOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakLoad | W | 565.50927734375 | 1185.0374713731348 | 619.5281940293848 | 52.279207113300885 | Fail | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.unmetHours | h | 65 | 0 | 65 | 100 | Fail | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakLoad | W | | 15.588918884437554 | | | N/A | unavailable (OpenStudioOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakHour | hourOfYear | | 7974 | | | N/A | unavailable (OpenStudioOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | area | m2 | 3.647967799712404 | 3.647967799712404 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | volume | m3 | 10.396708229180378 | 10.396708229180378 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.designLoad | W | 199.83349609375 | | | | N/A | unavailable (TasOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakLoad | W | 199.82528686523438 | 141.7703965184828 | 58.05489034675156 | 29.052824723782223 | Fail | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakHour | hourOfYear | 1441 | 918 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.unmetHours | h | 4761 | 0 | 4761 | 100 | Fail | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | area | m2 | 2.9450000000000016 | 2.9450000000000016 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | volume | m3 | 5.746544632499992 | 5.746544632499992 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.designLoad | W | 28.741456985473633 | | | | N/A | unavailable (TasOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | area | m2 | 2.355387206192353 | 2.355387206192353 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | volume | m3 | 6.712853537648204 | 6.712853537648204 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.designLoad | W | 69.60518646240234 | | | | N/A | unavailable (TasOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakLoad | W | 52.024513244628906 | 0 | 52.024513244628906 | 100 | Fail | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakHour | hourOfYear | 1441 | 0 | 1441 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | area | m2 | 3.0986077487352315 | 3.0986077487352315 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | volume | m3 | 9.007679927026205 | 9.007679927026205 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.designLoad | W | 137.82980346679688 | | | | N/A | unavailable (TasOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakLoad | W | 117.870849609375 | 262.9986126690514 | 145.1277630596764 | 55.18195004408647 | Fail | | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.unmetHours | h | 67 | 0 | 67 | 100 | Fail | | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | area | m2 | 5.03787628653 | 5.03787628653 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | volume | m3 | 7.564547888993382 | 7.564547888993382 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | area | m2 | 23.55004650825489 | 23.55004650825489 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | volume | m3 | 58.340316505330264 | 58.340316505330264 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.designLoad | W | 379.9020080566406 | | | | N/A | unavailable (TasOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakLoad | W | 132.86868286132812 | 255.3860902131586 | 122.51740735183049 | 47.97340655850561 | Fail | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakHour | hourOfYear | 1442 | 919 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | area | m2 | 9.409999999999991 | 9.409999999999991 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | volume | m3 | 26.966204214472434 | 26.966204214472434 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.designLoad | W | 366.3569641113281 | | | | N/A | unavailable (TasOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakLoad | W | 366.33624267578125 | 190.23285633880042 | 176.10338633698083 | 48.07151622528315 | Fail | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakHour | hourOfYear | 1444 | 919 | 525 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | area | m2 | 4.634866891556 | 4.634866891556 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | volume | m3 | 28.16450759620351 | 28.16450759620351 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.designLoad | W | 69.23214721679688 | | | | N/A | unavailable (TasOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakLoad | W | 11.025825500488281 | 39.29050142981706 | 28.26467592932878 | 71.93768188430163 | Fail | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakHour | hourOfYear | 1452 | 774 | 678 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | area | m2 | 6.955039759539971 | 6.955039759539971 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | volume | m3 | 20.363019689898447 | 20.363019689898447 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.designLoad | W | 204.9483642578125 | | | | N/A | unavailable (TasOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakLoad | W | 117.45973205566406 | 133.44180277588939 | 15.982070720225323 | 11.97680965616646 | Warn | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakHour | hourOfYear | 1447 | 774 | 673 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | area | m2 | 38.98503142608865 | 38.98503142608865 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | volume | m3 | 121.59536233412693 | 121.59536233412693 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.designLoad | W | 1065.076171875 | | | | N/A | unavailable (TasOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakLoad | W | 1025.7105712890625 | 1789.9830426085152 | 764.2724713194527 | 42.69719059492821 | Fail | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakHour | hourOfYear | 1443 | 775 | 668 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.unmetHours | h | 114 | 0 | 114 | 100 | Fail | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | area | m2 | 18.080018143643976 | 18.080018143643976 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | volume | m3 | 51.52805170938531 | 51.52805170938531 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.designLoad | W | 564.768798828125 | | | | N/A | unavailable (TasOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakLoad | W | 559.67822265625 | 936.5975380258114 | 376.91931536956145 | 40.243466384082474 | Fail | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakHour | hourOfYear | 1441 | 727 | 714 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.unmetHours | h | 69 | 0 | 69 | 100 | Fail | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakLoad | W | | 28.70645886203079 | | | N/A | unavailable (OpenStudioOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakHour | hourOfYear | | 7991 | | | N/A | unavailable (OpenStudioOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | area | m2 | 21.280022127733165 | 21.280022127733165 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | volume | m3 | 57.3391691981517 | 57.3391691981517 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.designLoad | W | 549.6607055664062 | | | | N/A | unavailable (TasOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakLoad | W | 529.828857421875 | 989.0652526225581 | 459.2363952006831 | 46.431354653597815 | Fail | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakHour | hourOfYear | 1457 | 919 | 538 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.unmetHours | h | 29 | 0 | 29 | 100 | Fail | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | + From a4065aad3bfaac41837ae2e784a09ec44b849544 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Sat, 25 Jul 2026 22:30:31 +0200 Subject: [PATCH 06/11] docs: repeat the cross-engine comparison on the fixed OpenStudio producer (#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Repeats the same-model/same-weather run now that SAM_OpenStudio #17 (1ae8637) derives designDaySource from the route instead of hardcoding None. Same licensed workstation, same model (HungaryHouse-WeahterDDY.sam, 14 spaces, embedded design days) and same weather (HUN_CEN_Budapest TMYx). Producers built from SAM_Tas@ff00497 and SAM_OpenStudio@1ae8637; comparator SAM_Validation@c3eec2cafaf42267a506579a9efd078cb7fcc473 (the B3 squash merge). Tas 9.5.7.0 (60.8 s) and EnergyPlus 25.1.0 / SDK 3.10.0 (21.5 s), both exit 0 state=Success; comparator exit 0. Result 1 — the provenance gate now PASSES. OpenStudio reports designDaySource=EmbeddedModel (was None), matching TAS, and the two documents agree on canonicalModelHash, sourceModelGuid and the weather hash. ProvenanceStatus Fail -> Pass. The defect the gate caught is closed and the fix is verified end to end. Result 2 — not one simulated number changed. The complete generated comparison.csv is byte-identical to the 2026-07-25-cross-engine-smoke run: both files hash to the same Git blob 840655621cd7c9e95d24233687c229f50c8f3cca, so every value, difference, band, note and tolerance column agrees exactly. (comparison.md and comparison-summary.json differ, as they must — they carry the provenance block, which is what changed.) That confirms the defect was exactly what it was diagnosed as, a REPORTING defect: the route was already building design days from the embedded model. It also doubles as a reproducibility check — two independent full runs of both engines produced identical metrics to the last binary64 digit, across TAS COM, a gbXML round trip, EnergyPlus and both producers' extraction paths. Result 3 — the candidate conditioning pairing is still NOT equivalent. The overall gate stays Fail, now on the numerical status alone: annual heating 4331.96 vs 1751.18 kWh (59.6%). Among comparable per-space heating peak-load rows, relative differences span approximately 12-100% across the 11 comparable rows; the 29% and 52% figures quoted earlier are selected examples, not the complete range. Previously that Fail was confounded by the provenance mismatch, so the disagreement could be set aside as "probably the declared design-day basis"; that specific objection is now answered. The two whole-model peak loads are banded and shown but excluded from the numerical status, so they are not what fails it. The D10 pairing must stay labelled a candidate and be investigated metric by metric against the B4 corpus — not by adjusting tolerances. What this run does NOT establish: it does not eliminate design-day handling as a cause of the load gap. It shows both runs declare the same coarse designDaySource CATEGORY and that a provenance-only fix changed no result; neither is evidence that the engines instantiated equivalent design days AFTER translation. canonicalModelHash describes the neutral loaded SAM model and is computed before any engine-specific modification (METHODOLOGY.md), and designDaySource records only Ddy/EmbeddedModel/None. Differing design-day translation therefore remains an open and leading hypothesis — TAS sizes through TBD while OpenStudio emits SizingPeriod:DesignDay objects — and closing it needs engine-level evidence comparing the design days actually instantiated on each side. Recorded as such in the README rather than claimed shut. Carried forward for B4, with the 66 unavailable required metrics now broken down in the README: OpenStudio's missing per-space design loads account for 28 of the 66 (heating+cooling designLoad across all 14 matched spaces) and are the largest single source of incomplete coverage; a further 24 are cooling peakLoad/peakHour on 12 spaces where TAS reports no cooling result but OpenStudio does. TAS still reports peakCoolingHour=0 available for a measured-zero cooling load, and the engines disagree about which spaces are conditioned — a bounded lead worth chasing first. Artefacts are the verbatim outputs: two neutral inputs plus the three generated reports, LF-pinned by the existing docs/benchmark/reports/** gitattributes rule. No simulation was re-run for these corrections and no neutral benchmark JSON was modified. Generated by Michal Dengusiak and CodeClaude Co-authored-by: Claude Opus 5 --- .../README.md | 156 + .../benchmark-OpenStudio.json | 888 ++++ .../benchmark-TAS.json | 890 ++++ .../comparison-summary.json | 3728 +++++++++++++++++ .../comparison.csv | 149 + .../comparison.md | 292 ++ 6 files changed, 6103 insertions(+) create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/README.md create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-OpenStudio.json create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-TAS.json create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison-summary.json create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.csv create mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.md diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/README.md b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/README.md new file mode 100644 index 0000000..bdbb4e9 --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/README.md @@ -0,0 +1,156 @@ +# B3 — cross-engine comparison repeated on the fixed OpenStudio producer + +This repeats the [first real cross-engine run](../2026-07-25-cross-engine-smoke/README.md) — same +source model, same weather file, same workstation — now that the OpenStudio producer's design-day +provenance defect is fixed ([SAM_OpenStudio #17](https://github.com/SAM-BIM/SAM_OpenStudio/pull/17), +merged to `sow/2026-Q3` as `1ae8637`). + +Its purpose is narrow: establish whether the provenance gate now accepts the pair, and whether +correcting the declared design-day basis moves any number. It remains a **mechanical validation of +the comparator and the producers**, not a validated engine-equivalence claim. + +## Run + +- **Date:** 2026-07-25 (TAS `14:35:23Z`, OpenStudio `14:36:36Z`). +- **Machine:** the same licensed EDSL Tas workstation as the previous run. +- **Model:** `HungaryHouse-WeahterDDY.sam` — 14 spaces, carrying embedded design days. +- **Weather:** `HUN_CEN_Budapest.Met.Center.128400_TMYx.epw`. +- **Engines:** Tas `9.5.7.0` (producer duration 60.8 s); EnergyPlus `25.1.0` / OpenStudio SDK + `3.10.0` (producer duration 21.5 s). +- **Producers built from:** `SAM_Tas@ff00497` and `SAM_OpenStudio@1ae8637`, both on `sow/2026-Q3`. +- **Comparator:** `SAM_Validation@c3eec2cafaf42267a506579a9efd078cb7fcc473` (the B3 squash merge). +- Both producers exited `0` with `state = Success`; the comparator exited `0`. + +Artefacts in this folder are the verbatim outputs: the two neutral input documents and the three +generated reports. + +## Result 1 — the provenance gate now passes + +| Field | Previous run | This run | +|---|---|---| +| TAS `designDaySource` | `EmbeddedModel` | `EmbeddedModel` | +| OpenStudio `designDaySource` | `None` (hardcoded — the defect) | **`EmbeddedModel`** (derived from the route) | +| **Provenance status** | **Fail** | **Pass** | + +The two documents also agree on `canonicalModelHash` +(`sha256:07a3ef15…`), `sourceModelGuid` and the weather hash, so the comparator now confirms in full +that the pair is a valid comparison: same canonical model, same weather, same design-day basis, one +engine and route on each side. + +## Result 2 — not one simulated number changed + +The complete generated `comparison.csv` is **byte-identical** to the previous run — not merely equal on +the value columns. The two files share one Git blob: + +```text +git hash-object ../2026-07-25-cross-engine-smoke/comparison.csv comparison.csv + 840655621cd7c9e95d24233687c229f50c8f3cca + 840655621cd7c9e95d24233687c229f50c8f3cca +``` + +Every metric value, absolute and relative difference, band, note and tolerance column therefore agrees +exactly. `comparison.md` and `comparison-summary.json` do differ, as they must: they carry the +provenance block, which is precisely what changed. + +Two things follow, and they matter more than the passing gate: + +1. **The defect was exactly what it was diagnosed as** — a *reporting* defect. The OpenStudio route + was already building design days from the embedded model; only the declared `designDaySource` was + wrong. Had the fix altered the sizing basis, the loads would have moved. +2. **The pipeline is reproducible.** Two independent full runs of both engines, hours apart, produced + identical metrics down to the last binary64 digit — across TAS COM, a gbXML round trip, + EnergyPlus, and both producers' extraction paths. + +## Result 3 — the candidate conditioning pairing is still not equivalent + +The overall gate is still `Fail`, now on the numerical status alone: + +| Status | Value | +|---|---| +| Numerical | **Fail** | +| Coverage | Warn (82 of 148 required metrics comparable; 66 unavailable) | +| Provenance | Pass | +| Reconciliation | Pass | +| **Overall** | **Fail** | + +This is the point of the exercise. Previously the `Fail` was confounded: a provenance mismatch made the +comparison invalid regardless of the numbers, so the numerical disagreement could be set aside as +"probably the declared design-day basis". That specific objection is answered — **the neutral inputs are +provably identical, the declared design-day basis now agrees, and the numbers still disagree.** + +What this does *not* establish is covered under +[what remains open](#what-this-run-does-not-establish-about-design-days) below. + +| Metric | TAS | OpenStudio | Rel. diff | Band | Gates? | +|---|---:|---:|---:|---|---| +| Annual heating (kWh) | 4331.96 | 1751.18 | **59.6 %** | Fail | **yes** | +| Annual cooling (kWh) | 0 | 5.25 | 100 % | Fail | **yes** | +| Peak heating load (kW) | 3.934 | 5.721 | 31.2 % | Fail | no — designated informational | +| Peak cooling load (kW) | 0 | 0.040 | 100 % | Fail | no — designated informational | +| Floor area | 167.95195245742798 | 167.95195207345205 | 2.3e-9 (= 2.3e-7 %) | Match | yes | + +Among comparable per-space heating peak-load rows, relative differences span approximately **12–100 %** +(11 comparable rows). The previously quoted 29 % and 52 % values are selected examples, not the complete +range. The cooling rows compare a TAS *measured zero* against a +near-zero OpenStudio value, so their 100 % figures are not meaningful magnitudes. The two whole-model +peak loads are banded and shown but excluded from the numerical status +(TOLERANCES.md → Informational metrics), so they are not what fails this comparison — annual energy, +per-space peak loads and unmet hours are. + +**Conclusion:** the D10 candidate pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD +sizing) is **not** validated. It must stay labelled a *candidate* and be investigated against the B4 +corpus, metric by metric — not by adjusting tolerances. + +### What this run does *not* establish about design days + +It would be wrong to read this run as eliminating design-day handling as a cause of the load gap. Two +things were shown: the two runs declare the same design-day *category*, and the provenance fix changed +no result. Neither is evidence that the engines built **equivalent design days** after translation. + +- `canonicalModelHash` describes the neutral loaded SAM model and is computed *before any + engine-specific modification* ([METHODOLOGY.md](../../METHODOLOGY.md) → The two model hashes). It + says nothing about what either engine did with the embedded design days afterwards. +- `designDaySource` records only the coarse category `Ddy` / `EmbeddedModel` / `None`. Both sides + reporting `EmbeddedModel` means both drew on the model's own design days, not that they instantiated + the same ones. +- Identical metrics before and after a provenance-only fix show that *the fix* did not alter results — + by construction, since it only changed a reported field. + +**Differing design-day translation therefore remains an open hypothesis**, and a leading one: TAS sizes +through TBD while OpenStudio emits `SizingPeriod:DesignDay` objects into the EnergyPlus model, and a +differing sizing basis moves heating loads. Closing it needs engine-level evidence — compare the design +days actually instantiated on each side (the TBD sizing inputs against the generated OSM/IDF +`SizingPeriod:DesignDay` objects), which is outside this run's provenance-only scope and belongs with +the B4 investigation. + +## Carried forward + +The 66 unavailable required metrics break down as follows (all 14 spaces matched, so none of this is +an alignment problem): + +| Count | Metrics | Availability | Meaning | +|---:|---|---|---| +| 28 | `heating.designLoad` + `cooling.designLoad`, all 14 spaces | TasOnly | OpenStudio emits no per-space design load on the native route | +| 24 | `cooling.peakLoad` + `cooling.peakHour`, 12 spaces | OpenStudioOnly | TAS reports no cooling result where OpenStudio does | +| 8 | `heating`/`cooling` `peakLoad` + `peakHour`, 2 spaces | Neither | neither engine reports these two spaces | +| 4 | `heating`/`cooling` `unmetHours`, 2 spaces | TasOnly | | +| 2 | `heating.peakLoad` + `heating.peakHour`, 1 space | OpenStudioOnly | | + +- OpenStudio's missing per-space design loads account for **28 of the 66** unavailable required metrics + and are the largest single source of incomplete coverage (a documented route limitation). +- TAS still reports `peakCoolingHour = 0` with `available: true` for a measured-zero cooling load; a + peak *hour* for a zero load carries no information (a B2 producer-semantics question). +- The engines still disagree about which spaces are conditioned, and the table above quantifies it: 24 + metrics are unavailable because TAS reports no cooling result where OpenStudio does, and one space + reports `heating.peakLoad` as available `0` from OpenStudio while TAS reports it unavailable. This is + a concrete, bounded lead for the B4 investigation. +- **Design-day translation is an open hypothesis** for the load gap (see above). It needs engine-level + evidence comparing the design days each engine actually instantiated, not provenance fields. + +## Status + +- **Provenance gate: closed.** The defect it caught is fixed and the fix is verified end to end. +- **Comparator: validated twice on real two-engine data**, now including a reproducibility check. +- **Engine equivalence: open.** The declared-provenance objection is answered, but design-day + *translation* remains untested, so the cause of the load gap is still unidentified. Next: the B4 + corpus (`SAM_Samples`) and energy-modeller review. diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-OpenStudio.json b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-OpenStudio.json new file mode 100644 index 0000000..332192d --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-OpenStudio.json @@ -0,0 +1,888 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "1ae8637ad19accd4d2740a5aba0106463882752f", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "25.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "Budapest", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-25T14:36:36.3746344Z", + "durationSeconds": 21.5130193, + "state": "Success", + "resultSources": [ + "SAM.Analytical.OpenStudio" + ], + "warnings": [], + "notes": [] + }, + "model": { + "consumptionHeating": { + "value": 1751.1764825992748, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": 5.2499930426550705, + "unit": "kWh", + "available": true + }, + "peakHeatingLoad": { + "value": 5.720615732872154, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 775, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 0.0402250938503556, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 7974, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 167.95195207345205, + "unit": "m2", + "available": true + }, + "volume": { + "value": 470.1821620538829, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "025eb80f65d745b799511b78ac2f82fb", + "name": "01_18 Void", + "area": { + "value": 6.6920779999999995, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.118059598283878, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "name": "01_13 Bedroom", + "area": { + "value": 21.280010175465385, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.33913699268221, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 1185.0374713731348, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 15.588918884437554, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 7974, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "name": "GF_08 Bathroom", + "area": { + "value": 3.647967799712404, + "unit": "m2", + "available": true + }, + "volume": { + "value": 10.396708229180378, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 141.7703965184828, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 918, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "name": "01_15 Plantroom", + "area": { + "value": 2.9450000000000016, + "unit": "m2", + "available": true + }, + "volume": { + "value": 5.746544632499992, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "name": "GF_09 WC", + "area": { + "value": 2.355387206192353, + "unit": "m2", + "available": true + }, + "volume": { + "value": 6.712853537648204, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "354b8786b4714f3c9941cec051a93361", + "name": "GF_10 Closet", + "area": { + "value": 3.0986077487352315, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.007679927026205, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 262.9986126690514, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "name": "01_11 Void", + "area": { + "value": 5.03787628653, + "unit": "m2", + "available": true + }, + "volume": { + "value": 7.564547888993382, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "6e7ef8487b714da3b730e0f226af927e", + "name": "01_17 Gallery", + "area": { + "value": 23.55004650825489, + "unit": "m2", + "available": true + }, + "volume": { + "value": 58.340316505330264, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 255.3860902131586, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "name": "01_16 Bathroom", + "area": { + "value": 9.409999999999991, + "unit": "m2", + "available": true + }, + "volume": { + "value": 26.966204214472434, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 190.23285633880042, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "name": "GF_05 Stairs", + "area": { + "value": 4.634866891556, + "unit": "m2", + "available": true + }, + "volume": { + "value": 28.16450759620351, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 39.29050142981706, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 774, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "name": "GF_12 EntranceHall", + "area": { + "value": 6.955039759539971, + "unit": "m2", + "available": true + }, + "volume": { + "value": 20.363019689898447, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 133.44180277588939, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 774, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "name": "GF_07 LivingRoom", + "area": { + "value": 38.98503142608865, + "unit": "m2", + "available": true + }, + "volume": { + "value": 121.59536233412693, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 1789.9830426085152, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 775, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "name": "GF_11 Bedroom", + "area": { + "value": 18.080018143643976, + "unit": "m2", + "available": true + }, + "volume": { + "value": 51.52805170938531, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 936.5975380258114, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 727, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 28.70645886203079, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 7991, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "name": "01_14 Bedroom", + "area": { + "value": 21.280022127733165, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.3391691981517, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 989.0652526225581, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + } + ] +} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-TAS.json b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-TAS.json new file mode 100644 index 0000000..dc68c19 --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-TAS.json @@ -0,0 +1,890 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", + "engine": { + "kind": "TAS", + "name": "Tas", + "version": "9.5.7.0", + "sdkVersion": null + }, + "route": "Native-TAS", + "weather": { + "identity": "Budapest", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-25T14:35:23.4577336Z", + "durationSeconds": 60.7963213, + "state": "Success", + "resultSources": [ + "SAM.Analytical.Tas" + ], + "warnings": [], + "notes": [ + "Shared gbXML was exported from the source model (no --gbxml supplied)." + ] + }, + "model": { + "consumptionHeating": { + "value": 4331.961588091359, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": 0, + "unit": "kWh", + "available": true + }, + "peakHeatingLoad": { + "value": 3.93393212890625, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 0, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 167.95195245742798, + "unit": "m2", + "available": true + }, + "volume": { + "value": 470.1821608543396, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "025eb80f65d745b799511b78ac2f82fb", + "name": "01_18 Void", + "area": { + "value": 6.6920779999999995, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.118059598283878, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "name": "01_13 Bedroom", + "area": { + "value": 21.280010175465385, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.33913699268221, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 574.0481567382812, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 565.50927734375, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 65, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "name": "GF_08 Bathroom", + "area": { + "value": 3.647967799712404, + "unit": "m2", + "available": true + }, + "volume": { + "value": 10.396708229180378, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 199.83349609375, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 199.82528686523438, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 4761, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "name": "01_15 Plantroom", + "area": { + "value": 2.9450000000000016, + "unit": "m2", + "available": true + }, + "volume": { + "value": 5.746544632499992, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 28.741456985473633, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "name": "GF_09 WC", + "area": { + "value": 2.355387206192353, + "unit": "m2", + "available": true + }, + "volume": { + "value": 6.712853537648204, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 69.60518646240234, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 52.024513244628906, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "354b8786b4714f3c9941cec051a93361", + "name": "GF_10 Closet", + "area": { + "value": 3.0986077487352315, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.007679927026205, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 137.82980346679688, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 117.870849609375, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 67, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "name": "01_11 Void", + "area": { + "value": 5.03787628653, + "unit": "m2", + "available": true + }, + "volume": { + "value": 7.564547888993382, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "6e7ef8487b714da3b730e0f226af927e", + "name": "01_17 Gallery", + "area": { + "value": 23.55004650825489, + "unit": "m2", + "available": true + }, + "volume": { + "value": 58.340316505330264, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 379.9020080566406, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 132.86868286132812, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1442, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "name": "01_16 Bathroom", + "area": { + "value": 9.409999999999991, + "unit": "m2", + "available": true + }, + "volume": { + "value": 26.966204214472434, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 366.3569641113281, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 366.33624267578125, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1444, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "name": "GF_05 Stairs", + "area": { + "value": 4.634866891556, + "unit": "m2", + "available": true + }, + "volume": { + "value": 28.16450759620351, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 69.23214721679688, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 11.025825500488281, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1452, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "name": "GF_12 EntranceHall", + "area": { + "value": 6.955039759539971, + "unit": "m2", + "available": true + }, + "volume": { + "value": 20.363019689898447, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 204.9483642578125, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 117.45973205566406, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1447, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "name": "GF_07 LivingRoom", + "area": { + "value": 38.98503142608865, + "unit": "m2", + "available": true + }, + "volume": { + "value": 121.59536233412693, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 1065.076171875, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 1025.7105712890625, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1443, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 114, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "name": "GF_11 Bedroom", + "area": { + "value": 18.080018143643976, + "unit": "m2", + "available": true + }, + "volume": { + "value": 51.52805170938531, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 564.768798828125, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 559.67822265625, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 69, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "name": "01_14 Bedroom", + "area": { + "value": 21.280022127733165, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.3391691981517, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 549.6607055664062, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 529.828857421875, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1457, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 29, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + } + ] +} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison-summary.json b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison-summary.json new file mode 100644 index 0000000..cf3836c --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison-summary.json @@ -0,0 +1,3728 @@ +{ + "summarySchemaVersion": "1.0.0", + "toleranceProfile": "default", + "toleranceProfileDetail": { + "name": "default", + "warnRelative": 0.05, + "failRelative": 0.15, + "hourWarnAbsolute": 1, + "hourFailAbsolute": 24, + "defaultNearZeroFloor": 0, + "nearZeroFloors": { + "kWh": 1, + "Wh": 1000, + "kW": 0.01, + "W": 10, + "m2": 0.01, + "m3": 0.01, + "h": 0.5 + } + }, + "gate": "Fail", + "statuses": { + "overall": "Fail", + "numerical": "Fail", + "coverage": "Warn", + "provenance": "Pass", + "reconciliation": "Pass" + }, + "provenanceCompatibility": { + "compatible": true, + "mismatches": [] + }, + "counts": { + "match": 48, + "warn": 1, + "fail": 33, + "notApplicable": 66 + }, + "coverage": { + "status": "Warn", + "requiredMetricCount": 148, + "comparableMetricCount": 82, + "unavailableRequiredMetricCount": 66, + "unmatchedSpaceCount": 0, + "ambiguousSpaceCount": 0, + "duplicateSpaceIdentityCount": 0 + }, + "schema": { + "tas": "1.0.0", + "openStudio": "1.0.0", + "driftNote": null + }, + "provenance": { + "tas": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", + "engine": { + "kind": "TAS", + "name": "Tas", + "version": "9.5.7.0", + "sdkVersion": null + }, + "route": "Native-TAS", + "weather": { + "identity": "Budapest", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-25T14:35:23.4577336Z", + "durationSeconds": 60.7963213, + "state": "Success", + "resultSources": [ + "SAM.Analytical.Tas" + ], + "warnings": [], + "notes": [ + "Shared gbXML was exported from the source model (no --gbxml supplied)." + ] + }, + "openStudio": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "1ae8637ad19accd4d2740a5aba0106463882752f", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "25.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "Budapest", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-25T14:36:36.3746344Z", + "durationSeconds": 21.5130193, + "state": "Success", + "resultSources": [ + "SAM.Analytical.OpenStudio" + ], + "warnings": [], + "notes": [] + } + }, + "modelMetrics": [ + { + "scope": "model", + "key": "consumptionHeating", + "unit": "kWh", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4331.961588091359, + "available": true, + "unit": "kWh" + }, + "openStudio": { + "value": 1751.1764825992748, + "available": true, + "unit": "kWh" + }, + "absoluteDifference": 2580.785105492084, + "relativeDifference": 0.5957543835537944, + "signedDifference": -2580.785105492084 + }, + { + "scope": "model", + "key": "consumptionCooling", + "unit": "kWh", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "kWh" + }, + "openStudio": { + "value": 5.2499930426550705, + "available": true, + "unit": "kWh" + }, + "absoluteDifference": 5.2499930426550705, + "relativeDifference": 1, + "signedDifference": 5.2499930426550705 + }, + { + "scope": "model", + "key": "peakHeatingLoad", + "unit": "kW", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.93393212890625, + "available": true, + "unit": "kW" + }, + "openStudio": { + "value": 5.720615732872154, + "available": true, + "unit": "kW" + }, + "absoluteDifference": 1.7866836039659035, + "relativeDifference": 0.3123236531513824, + "signedDifference": 1.7866836039659035 + }, + { + "scope": "model", + "key": "peakHeatingHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 775, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 144, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "peakCoolingLoad", + "unit": "kW", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "kW" + }, + "openStudio": { + "value": 0.0402250938503556, + "available": true, + "unit": "kW" + }, + "absoluteDifference": 0.0402250938503556, + "relativeDifference": 1, + "signedDifference": 0.0402250938503556 + }, + { + "scope": "model", + "key": "peakCoolingHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7974, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 786, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "floorArea", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 167.95195245742798, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 167.95195207345205, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 3.839759301627055E-07, + "relativeDifference": 2.2862248669602973E-09, + "signedDifference": -3.839759301627055E-07 + }, + { + "scope": "model", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 470.1821608543396, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 470.1821620538829, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 1.1995433055744797E-06, + "relativeDifference": 2.5512309959496336E-09, + "signedDifference": 1.1995433055744797E-06 + } + ], + "spaces": [ + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "guid": "025eb80f65d745b799511b78ac2f82fb", + "tasName": "01_18 Void", + "openStudioName": "01_18 Void", + "matchKind": "Guid", + "metrics": [ + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.6920779999999995, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 6.6920779999999995, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.118059598283878, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 9.118059598283878, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "tasName": "01_13 Bedroom", + "openStudioName": "01_13 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 21.280010175465385, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 21.280010175465385, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 57.33913699268221, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 57.33913699268221, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 574.0481567382812, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 565.50927734375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 1185.0374713731348, + "available": true, + "unit": "W" + }, + "absoluteDifference": 619.5281940293848, + "relativeDifference": 0.5227920711330089, + "signedDifference": 619.5281940293848 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 522, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 65, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 65, + "relativeDifference": 1, + "signedDifference": -65 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 15.588918884437554, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7974, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "tasName": "GF_08 Bathroom", + "openStudioName": "GF_08 Bathroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.647967799712404, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 3.647967799712404, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 10.396708229180378, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 10.396708229180378, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 199.83349609375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 199.82528686523438, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 141.7703965184828, + "available": true, + "unit": "W" + }, + "absoluteDifference": 58.05489034675156, + "relativeDifference": 0.2905282472378222, + "signedDifference": -58.05489034675156 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 918, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 523, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4761, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 4761, + "relativeDifference": 1, + "signedDifference": -4761 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "tasName": "01_15 Plantroom", + "openStudioName": "01_15 Plantroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 2.9450000000000016, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 2.9450000000000016, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 5.746544632499992, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 5.746544632499992, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 28.741456985473633, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "tasName": "GF_09 WC", + "openStudioName": "GF_09 WC", + "matchKind": "Guid", + "metrics": [ + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 2.355387206192353, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 2.355387206192353, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.712853537648204, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 6.712853537648204, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 69.60518646240234, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 52.024513244628906, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": 52.024513244628906, + "relativeDifference": 1, + "signedDifference": -52.024513244628906 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 1441, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "guid": "354b8786b4714f3c9941cec051a93361", + "tasName": "GF_10 Closet", + "openStudioName": "GF_10 Closet", + "matchKind": "Guid", + "metrics": [ + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.0986077487352315, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 3.0986077487352315, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.007679927026205, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 9.007679927026205, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 137.82980346679688, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 117.870849609375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 262.9986126690514, + "available": true, + "unit": "W" + }, + "absoluteDifference": 145.1277630596764, + "relativeDifference": 0.5518195004408647, + "signedDifference": 145.1277630596764 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 522, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 67, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 67, + "relativeDifference": 1, + "signedDifference": -67 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "tasName": "01_11 Void", + "openStudioName": "01_11 Void", + "matchKind": "Guid", + "metrics": [ + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 5.03787628653, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 5.03787628653, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 7.564547888993382, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 7.564547888993382, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "guid": "6e7ef8487b714da3b730e0f226af927e", + "tasName": "01_17 Gallery", + "openStudioName": "01_17 Gallery", + "matchKind": "Guid", + "metrics": [ + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 23.55004650825489, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 23.55004650825489, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 58.340316505330264, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 58.340316505330264, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 379.9020080566406, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 132.86868286132812, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 255.3860902131586, + "available": true, + "unit": "W" + }, + "absoluteDifference": 122.51740735183049, + "relativeDifference": 0.4797340655850561, + "signedDifference": 122.51740735183049 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1442, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 523, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "tasName": "01_16 Bathroom", + "openStudioName": "01_16 Bathroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.409999999999991, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 9.409999999999991, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 26.966204214472434, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 26.966204214472434, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 366.3569641113281, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 366.33624267578125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 190.23285633880042, + "available": true, + "unit": "W" + }, + "absoluteDifference": 176.10338633698083, + "relativeDifference": 0.4807151622528315, + "signedDifference": -176.10338633698083 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1444, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 525, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "tasName": "GF_05 Stairs", + "openStudioName": "GF_05 Stairs", + "matchKind": "Guid", + "metrics": [ + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4.634866891556, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 4.634866891556, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 28.16450759620351, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 28.16450759620351, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 69.23214721679688, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 11.025825500488281, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 39.29050142981706, + "available": true, + "unit": "W" + }, + "absoluteDifference": 28.26467592932878, + "relativeDifference": 0.7193768188430163, + "signedDifference": 28.26467592932878 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1452, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 774, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 678, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "tasName": "GF_12 EntranceHall", + "openStudioName": "GF_12 EntranceHall", + "matchKind": "Guid", + "metrics": [ + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.955039759539971, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 6.955039759539971, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 20.363019689898447, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 20.363019689898447, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 204.9483642578125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Warn", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 117.45973205566406, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 133.44180277588939, + "available": true, + "unit": "W" + }, + "absoluteDifference": 15.982070720225323, + "relativeDifference": 0.1197680965616646, + "signedDifference": 15.982070720225323 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1447, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 774, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 673, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "tasName": "GF_07 LivingRoom", + "openStudioName": "GF_07 LivingRoom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 38.98503142608865, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 38.98503142608865, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 121.59536233412693, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 121.59536233412693, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 1065.076171875, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 1025.7105712890625, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 1789.9830426085152, + "available": true, + "unit": "W" + }, + "absoluteDifference": 764.2724713194527, + "relativeDifference": 0.4269719059492821, + "signedDifference": 764.2724713194527 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1443, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 775, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 668, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 114, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 114, + "relativeDifference": 1, + "signedDifference": -114 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "tasName": "GF_11 Bedroom", + "openStudioName": "GF_11 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 18.080018143643976, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 18.080018143643976, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 51.52805170938531, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 51.52805170938531, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 564.768798828125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 559.67822265625, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 936.5975380258114, + "available": true, + "unit": "W" + }, + "absoluteDifference": 376.91931536956145, + "relativeDifference": 0.4024346638408247, + "signedDifference": 376.91931536956145 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 727, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 714, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 69, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 69, + "relativeDifference": 1, + "signedDifference": -69 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 28.70645886203079, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7991, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "tasName": "01_14 Bedroom", + "openStudioName": "01_14 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 21.280022127733165, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 21.280022127733165, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 57.3391691981517, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 57.3391691981517, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 549.6607055664062, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 529.828857421875, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 989.0652526225581, + "available": true, + "unit": "W" + }, + "absoluteDifference": 459.2363952006831, + "relativeDifference": 0.4643135465359781, + "signedDifference": 459.2363952006831 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1457, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 538, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 29, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 29, + "relativeDifference": 1, + "signedDifference": -29 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + } + ], + "spaceDiagnostics": { + "tasCount": 14, + "openStudioCount": 14, + "matchedByGuid": 14, + "matchedByName": 0, + "onlyInTas": [], + "onlyInOpenStudio": [], + "duplicateTasGuids": [], + "duplicateOpenStudioGuids": [], + "duplicateTasNames": [], + "duplicateOpenStudioNames": [], + "ambiguousNameMatches": [] + }, + "reconciliation": [ + { + "engine": "TAS", + "key": "floorArea", + "unit": "m2", + "modelTotal": 167.95195245742798, + "modelTotalAvailable": true, + "sumOfSpaces": 167.95195207345202, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 3.839759585844149E-07, + "relativeDifference": 2.2862250361855374E-09, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "TAS", + "key": "volume", + "unit": "m3", + "modelTotal": 470.1821608543396, + "modelTotalAvailable": true, + "sumOfSpaces": 470.1821620538828, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 1.199543191887642E-06, + "relativeDifference": 2.5512307541564592E-09, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "OpenStudio", + "key": "floorArea", + "unit": "m2", + "modelTotal": 167.95195207345205, + "modelTotalAvailable": true, + "sumOfSpaces": 167.95195207345202, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 2.842170943040401E-14, + "relativeDifference": 1.6922524019235018E-16, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "OpenStudio", + "key": "volume", + "unit": "m3", + "modelTotal": 470.1821620538829, + "modelTotalAvailable": true, + "sumOfSpaces": 470.1821620538828, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 1.1368683772161603E-13, + "relativeDifference": 2.417931748516387E-16, + "band": "Match", + "notApplicableReason": null + } + ] +} diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.csv b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.csv new file mode 100644 index 0000000..8406556 --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.csv @@ -0,0 +1,149 @@ +scope,metric,unit,availability,tasValue,openStudioValue,absoluteDifference,relativeDifference,relativePercent,signedDifference,band,note,toleranceProfile,warnRelative,failRelative,hourWarnAbsolute,hourFailAbsolute,nearZeroFloor,informational +model,consumptionHeating,kWh,Both,4331.961588091359,1751.1764825992748,2580.785105492084,0.5957543835537944,59.57543835537944,-2580.785105492084,Fail,,default,0.05,0.15,1,24,1,false +model,consumptionCooling,kWh,Both,0,5.2499930426550705,5.2499930426550705,1,100,5.2499930426550705,Fail,,default,0.05,0.15,1,24,1,false +model,peakHeatingLoad,kW,Both,3.93393212890625,5.720615732872154,1.7866836039659035,0.3123236531513824,31.23236531513824,1.7866836039659035,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true +model,peakHeatingHour,hourOfYear,Both,919,775,144,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +model,peakCoolingLoad,kW,Both,0,0.0402250938503556,0.0402250938503556,1,100,0.0402250938503556,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true +model,peakCoolingHour,hourOfYear,Both,0,7974,786,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +model,floorArea,m2,Both,167.95195245742798,167.95195207345205,3.839759301627055E-07,2.2862248669602973E-09,2.2862248669602975E-07,-3.839759301627055E-07,Match,,default,0.05,0.15,1,24,0.01,false +model,volume,m3,Both,470.1821608543396,470.1821620538829,1.1995433055744797E-06,2.5512309959496336E-09,2.5512309959496334E-07,1.1995433055744797E-06,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,area,m2,Both,6.6920779999999995,6.6920779999999995,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,volume,m3,Both,9.118059598283878,9.118059598283878,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +025eb80f65d745b799511b78ac2f82fb,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +025eb80f65d745b799511b78ac2f82fb,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +025eb80f65d745b799511b78ac2f82fb,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +0872cd39962f43c3b62a6e031a76c24a,area,m2,Both,21.280010175465385,21.280010175465385,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0872cd39962f43c3b62a6e031a76c24a,volume,m3,Both,57.33913699268221,57.33913699268221,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0872cd39962f43c3b62a6e031a76c24a,heating.designLoad,W,TasOnly,574.0481567382812,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,heating.peakLoad,W,Both,565.50927734375,1185.0374713731348,619.5281940293848,0.5227920711330089,52.279207113300885,619.5281940293848,Fail,,default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +0872cd39962f43c3b62a6e031a76c24a,heating.unmetHours,h,Both,65,0,65,1,100,-65,Fail,,default,0.05,0.15,1,24,0.5,false +0872cd39962f43c3b62a6e031a76c24a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,cooling.peakLoad,W,OpenStudioOnly,,15.588918884437554,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,cooling.peakHour,hourOfYear,OpenStudioOnly,,7974,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +0872cd39962f43c3b62a6e031a76c24a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +13a0403ab3a543608613b0ce9bf52d92,area,m2,Both,3.647967799712404,3.647967799712404,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +13a0403ab3a543608613b0ce9bf52d92,volume,m3,Both,10.396708229180378,10.396708229180378,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +13a0403ab3a543608613b0ce9bf52d92,heating.designLoad,W,TasOnly,199.83349609375,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,heating.peakLoad,W,Both,199.82528686523438,141.7703965184828,58.05489034675156,0.2905282472378222,29.052824723782223,-58.05489034675156,Fail,,default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,heating.peakHour,hourOfYear,Both,1441,918,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +13a0403ab3a543608613b0ce9bf52d92,heating.unmetHours,h,Both,4761,0,4761,1,100,-4761,Fail,,default,0.05,0.15,1,24,0.5,false +13a0403ab3a543608613b0ce9bf52d92,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +13a0403ab3a543608613b0ce9bf52d92,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +21bdbb806b4f4e2fbad0632b19fdf14b,area,m2,Both,2.9450000000000016,2.9450000000000016,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +21bdbb806b4f4e2fbad0632b19fdf14b,volume,m3,Both,5.746544632499992,5.746544632499992,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.designLoad,W,TasOnly,28.741456985473633,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +21bdbb806b4f4e2fbad0632b19fdf14b,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +312fab2f1ba64756b47ccc3dcb21ac8a,area,m2,Both,2.355387206192353,2.355387206192353,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +312fab2f1ba64756b47ccc3dcb21ac8a,volume,m3,Both,6.712853537648204,6.712853537648204,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.designLoad,W,TasOnly,69.60518646240234,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakLoad,W,Both,52.024513244628906,0,52.024513244628906,1,100,-52.024513244628906,Fail,,default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakHour,hourOfYear,Both,1441,0,1441,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +312fab2f1ba64756b47ccc3dcb21ac8a,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +354b8786b4714f3c9941cec051a93361,area,m2,Both,3.0986077487352315,3.0986077487352315,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +354b8786b4714f3c9941cec051a93361,volume,m3,Both,9.007679927026205,9.007679927026205,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +354b8786b4714f3c9941cec051a93361,heating.designLoad,W,TasOnly,137.82980346679688,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,heating.peakLoad,W,Both,117.870849609375,262.9986126690514,145.1277630596764,0.5518195004408647,55.18195004408647,145.1277630596764,Fail,,default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +354b8786b4714f3c9941cec051a93361,heating.unmetHours,h,Both,67,0,67,1,100,-67,Fail,,default,0.05,0.15,1,24,0.5,false +354b8786b4714f3c9941cec051a93361,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +354b8786b4714f3c9941cec051a93361,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +4377cbd6712349e68fdae8814ff9c5d9,area,m2,Both,5.03787628653,5.03787628653,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +4377cbd6712349e68fdae8814ff9c5d9,volume,m3,Both,7.564547888993382,7.564547888993382,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +4377cbd6712349e68fdae8814ff9c5d9,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +4377cbd6712349e68fdae8814ff9c5d9,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +4377cbd6712349e68fdae8814ff9c5d9,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +6e7ef8487b714da3b730e0f226af927e,area,m2,Both,23.55004650825489,23.55004650825489,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +6e7ef8487b714da3b730e0f226af927e,volume,m3,Both,58.340316505330264,58.340316505330264,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +6e7ef8487b714da3b730e0f226af927e,heating.designLoad,W,TasOnly,379.9020080566406,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,heating.peakLoad,W,Both,132.86868286132812,255.3860902131586,122.51740735183049,0.4797340655850561,47.97340655850561,122.51740735183049,Fail,,default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,heating.peakHour,hourOfYear,Both,1442,919,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +6e7ef8487b714da3b730e0f226af927e,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +6e7ef8487b714da3b730e0f226af927e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +6e7ef8487b714da3b730e0f226af927e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +7ee49aac87b24671bb00ed1b291806a1,area,m2,Both,9.409999999999991,9.409999999999991,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +7ee49aac87b24671bb00ed1b291806a1,volume,m3,Both,26.966204214472434,26.966204214472434,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +7ee49aac87b24671bb00ed1b291806a1,heating.designLoad,W,TasOnly,366.3569641113281,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,heating.peakLoad,W,Both,366.33624267578125,190.23285633880042,176.10338633698083,0.4807151622528315,48.07151622528315,-176.10338633698083,Fail,,default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,heating.peakHour,hourOfYear,Both,1444,919,525,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +7ee49aac87b24671bb00ed1b291806a1,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +7ee49aac87b24671bb00ed1b291806a1,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +7ee49aac87b24671bb00ed1b291806a1,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +8d2d2a0234584fe4b171e5a9373f1b4d,area,m2,Both,4.634866891556,4.634866891556,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +8d2d2a0234584fe4b171e5a9373f1b4d,volume,m3,Both,28.16450759620351,28.16450759620351,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.designLoad,W,TasOnly,69.23214721679688,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakLoad,W,Both,11.025825500488281,39.29050142981706,28.26467592932878,0.7193768188430163,71.93768188430163,28.26467592932878,Fail,,default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakHour,hourOfYear,Both,1452,774,678,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +8d2d2a0234584fe4b171e5a9373f1b4d,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +affc43b0b5b3445f85d5cea8a7b88d4f,area,m2,Both,6.955039759539971,6.955039759539971,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +affc43b0b5b3445f85d5cea8a7b88d4f,volume,m3,Both,20.363019689898447,20.363019689898447,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.designLoad,W,TasOnly,204.9483642578125,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakLoad,W,Both,117.45973205566406,133.44180277588939,15.982070720225323,0.1197680965616646,11.97680965616646,15.982070720225323,Warn,,default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakHour,hourOfYear,Both,1447,774,673,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +affc43b0b5b3445f85d5cea8a7b88d4f,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,area,m2,Both,38.98503142608865,38.98503142608865,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,volume,m3,Both,121.59536233412693,121.59536233412693,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.designLoad,W,TasOnly,1065.076171875,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakLoad,W,Both,1025.7105712890625,1789.9830426085152,764.2724713194527,0.4269719059492821,42.69719059492821,764.2724713194527,Fail,,default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakHour,hourOfYear,Both,1443,775,668,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.unmetHours,h,Both,114,0,114,1,100,-114,Fail,,default,0.05,0.15,1,24,0.5,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +d8294f2a30b445ca9f6ba83c8dcf4903,area,m2,Both,18.080018143643976,18.080018143643976,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d8294f2a30b445ca9f6ba83c8dcf4903,volume,m3,Both,51.52805170938531,51.52805170938531,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.designLoad,W,TasOnly,564.768798828125,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakLoad,W,Both,559.67822265625,936.5975380258114,376.91931536956145,0.4024346638408247,40.243466384082474,376.91931536956145,Fail,,default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakHour,hourOfYear,Both,1441,727,714,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +d8294f2a30b445ca9f6ba83c8dcf4903,heating.unmetHours,h,Both,69,0,69,1,100,-69,Fail,,default,0.05,0.15,1,24,0.5,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakLoad,W,OpenStudioOnly,,28.70645886203079,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakHour,hourOfYear,OpenStudioOnly,,7991,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,area,m2,Both,21.280022127733165,21.280022127733165,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,volume,m3,Both,57.3391691981517,57.3391691981517,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.designLoad,W,TasOnly,549.6607055664062,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakLoad,W,Both,529.828857421875,989.0652526225581,459.2363952006831,0.4643135465359781,46.431354653597815,459.2363952006831,Fail,,default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakHour,hourOfYear,Both,1457,919,538,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.unmetHours,h,Both,29,0,29,1,100,-29,Fail,,default,0.05,0.15,1,24,0.5,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.md b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.md new file mode 100644 index 0000000..e7771d3 --- /dev/null +++ b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.md @@ -0,0 +1,292 @@ +# Benchmark comparison: TAS vs OpenStudio + +Independent comparison of two engine-neutral benchmark documents. Tolerance bands are +**provisional reporting buckets**, not validated thresholds. The overall gate is the worst of +the numerical, coverage, provenance and reconciliation statuses. Peak-hour bands and the two +whole-model peak-load bands are informational and are excluded from the numerical status — they are +still calculated and shown, and rows carrying such a band are marked in the Note column; +reconciliation is a separate within-document diagnostic status that also contributes to the +overall gate. + +## Summary + +| Field | Value | +| --- | --- | +| Tolerance profile | default | +| Gate status (overall) | Fail | +| Numerical status | Fail | +| Coverage status | Warn | +| Provenance status | Pass | +| Reconciliation status | Pass | +| Metrics matched | 48 | +| Metrics warned | 1 | +| Metrics failed | 33 | +| Metrics not applicable | 66 | +| Required metrics compared | 82 of 148 | +| Required metrics unavailable | 66 | +| TAS schema version | 1.0.0 | +| OpenStudio schema version | 1.0.0 | + +## Coverage + +How much of the two runs was actually compared. Required metrics are the whole-model metrics plus +the metrics of uniquely matched spaces; the metrics of one-sided, duplicated or ambiguous spaces are +not counted here because those spaces are reported under space alignment instead. An unavailable +metric is never a numerical failure, so it is recorded here — otherwise missing results could read as +a clean pass. + +| Field | Value | +| --- | --- | +| Coverage status | Warn | +| Required metrics | 148 | +| Compared | 82 | +| Unavailable (not compared) | 66 | +| Unmatched spaces | 0 | +| Ambiguous (split) space names | 0 | +| Duplicated space identities | 0 | + +**Coverage is INCOMPLETE**, so this comparison must not be described as a complete result: + +- 66 of 148 required metrics were unavailable on at least one side (reported as N/A, never as a numerical failure). + +## Provenance compatibility + +Compatible: both runs share the same canonical model, weather and design-day basis, with the expected engine and route on each side. + +## Tolerance profile + +Provisional reporting bands (not validated thresholds). Recorded so a changed profile is never hidden behind an unchanged name. + +| Setting | Value | +| --- | --- | +| Name | default | +| Warn (relative) | 0.05 | +| Fail (relative) | 0.15 | +| Peak-hour warn (h) | 1 | +| Peak-hour fail (h) | 24 | +| Default near-zero floor | 0 | +| Near-zero floor (kWh) | 1 | +| Near-zero floor (Wh) | 1000 | +| Near-zero floor (kW) | 0.01 | +| Near-zero floor (W) | 10 | +| Near-zero floor (m2) | 0.01 | +| Near-zero floor (m3) | 0.01 | +| Near-zero floor (h) | 0.5 | + +## Provenance + +| Field | TAS | OpenStudio | +| --- | --- | --- | +| Engine kind | TAS | OpenStudio | +| Engine name | Tas | EnergyPlus | +| Engine version | 9.5.7.0 | 25.1.0 | +| SDK version | | 3.10.0 | +| Route | Native-TAS | Native-OpenStudio | +| Weather | Budapest | Budapest | +| Design-day source | EmbeddedModel | EmbeddedModel | +| Run state | Success | Success | +| Source model | 000000_SAM_AnalyticalModel | 000000_SAM_AnalyticalModel | +| Source model GUID | a527e890fceb465fbb50f4b6874f3a4c | a527e890fceb465fbb50f4b6874f3a4c | +| SAM commit | abcd617887f6792047023820f3efd2463fc5c6b3 | abcd617887f6792047023820f3efd2463fc5c6b3 | + +## Model metrics + +> **Caveats (apply even when values pass):** whole-model peak-load semantics differ between +> engines — OpenStudio reports a coincident total, TAS a building-profile maximum — so peak-load +> comparisons are informational, not equivalence claims. `peakHeatingLoad` and `peakCoolingLoad` +> are therefore banded and shown below but **excluded from the numerical status**, exactly like the +> peak-hour metrics; promoting them into the gate needs the B4 corpus and energy-modeller review. +> Every other metric — annual energy, per-space peak loads, unmet hours, geometry — gates normally. +> The primary conditioning pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing) +> is a **candidate** pending validation. A tolerance profile cannot remove these limitations. + +| Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | +| --- | --- | --- | --- | --- | --- | --- | --- | +| consumptionHeating | kWh | 4331.961588091359 | 1751.1764825992748 | 2580.785105492084 | 59.57543835537944 | Fail | | +| consumptionCooling | kWh | 0 | 5.2499930426550705 | 5.2499930426550705 | 100 | Fail | | +| peakHeatingLoad | kW | 3.93393212890625 | 5.720615732872154 | 1.7866836039659035 | 31.23236531513824 | Fail | informational (excluded from the numerical status) | +| peakHeatingHour | hourOfYear | 919 | 775 | 144 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| peakCoolingLoad | kW | 0 | 0.0402250938503556 | 0.0402250938503556 | 100 | Fail | informational (excluded from the numerical status) | +| peakCoolingHour | hourOfYear | 0 | 7974 | 786 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| floorArea | m2 | 167.95195245742798 | 167.95195207345205 | 3.839759301627055E-07 | 2.2862248669602975E-07 | Match | | +| volume | m3 | 470.1821608543396 | 470.1821620538829 | 1.1995433055744797E-06 | 2.5512309959496334E-07 | Match | | + +## Model-total vs sum-of-spaces reconciliation + +Additive quantities only; non-additive peak loads are not reconciled. Only uniquely-matched spaces are summed; unmatched or ambiguous spaces make a reconciliation incomplete. + +| Engine | Quantity | Unit | Model total | Sum of spaces | Abs diff | Band | Used/Missing/Excluded | Complete | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TAS | floorArea | m2 | 167.95195245742798 | 167.95195207345202 | 3.839759585844149E-07 | Match | 14/0/0 | yes | +| TAS | volume | m3 | 470.1821608543396 | 470.1821620538828 | 1.199543191887642E-06 | Match | 14/0/0 | yes | +| OpenStudio | floorArea | m2 | 167.95195207345205 | 167.95195207345202 | 2.842170943040401E-14 | Match | 14/0/0 | yes | +| OpenStudio | volume | m3 | 470.1821620538829 | 470.1821620538828 | 1.1368683772161603E-13 | Match | 14/0/0 | yes | + +## Space alignment + +| Field | Value | +| --- | --- | +| TAS spaces | 14 | +| OpenStudio spaces | 14 | +| Matched by GUID | 14 | +| Matched by name | 0 | + +**Only in TAS:** none + +**Only in OpenStudio:** none + +**Duplicate TAS GUIDs:** none + +**Duplicate OpenStudio GUIDs:** none + +**Duplicate TAS names:** none + +**Duplicate OpenStudio names:** none + +**Ambiguous (split) name matches:** none + +## Space metrics + +| Space | Match | Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 025eb80f65d745b799511b78ac2f82fb | Guid | area | m2 | 6.6920779999999995 | 6.6920779999999995 | 0 | 0 | Match | | +| 025eb80f65d745b799511b78ac2f82fb | Guid | volume | m3 | 9.118059598283878 | 9.118059598283878 | 0 | 0 | Match | | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | area | m2 | 21.280010175465385 | 21.280010175465385 | 0 | 0 | Match | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | volume | m3 | 57.33913699268221 | 57.33913699268221 | 0 | 0 | Match | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.designLoad | W | 574.0481567382812 | | | | N/A | unavailable (TasOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakLoad | W | 565.50927734375 | 1185.0374713731348 | 619.5281940293848 | 52.279207113300885 | Fail | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.unmetHours | h | 65 | 0 | 65 | 100 | Fail | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakLoad | W | | 15.588918884437554 | | | N/A | unavailable (OpenStudioOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakHour | hourOfYear | | 7974 | | | N/A | unavailable (OpenStudioOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | area | m2 | 3.647967799712404 | 3.647967799712404 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | volume | m3 | 10.396708229180378 | 10.396708229180378 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.designLoad | W | 199.83349609375 | | | | N/A | unavailable (TasOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakLoad | W | 199.82528686523438 | 141.7703965184828 | 58.05489034675156 | 29.052824723782223 | Fail | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakHour | hourOfYear | 1441 | 918 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.unmetHours | h | 4761 | 0 | 4761 | 100 | Fail | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | area | m2 | 2.9450000000000016 | 2.9450000000000016 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | volume | m3 | 5.746544632499992 | 5.746544632499992 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.designLoad | W | 28.741456985473633 | | | | N/A | unavailable (TasOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | area | m2 | 2.355387206192353 | 2.355387206192353 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | volume | m3 | 6.712853537648204 | 6.712853537648204 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.designLoad | W | 69.60518646240234 | | | | N/A | unavailable (TasOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakLoad | W | 52.024513244628906 | 0 | 52.024513244628906 | 100 | Fail | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakHour | hourOfYear | 1441 | 0 | 1441 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | area | m2 | 3.0986077487352315 | 3.0986077487352315 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | volume | m3 | 9.007679927026205 | 9.007679927026205 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.designLoad | W | 137.82980346679688 | | | | N/A | unavailable (TasOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakLoad | W | 117.870849609375 | 262.9986126690514 | 145.1277630596764 | 55.18195004408647 | Fail | | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.unmetHours | h | 67 | 0 | 67 | 100 | Fail | | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | area | m2 | 5.03787628653 | 5.03787628653 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | volume | m3 | 7.564547888993382 | 7.564547888993382 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | area | m2 | 23.55004650825489 | 23.55004650825489 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | volume | m3 | 58.340316505330264 | 58.340316505330264 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.designLoad | W | 379.9020080566406 | | | | N/A | unavailable (TasOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakLoad | W | 132.86868286132812 | 255.3860902131586 | 122.51740735183049 | 47.97340655850561 | Fail | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakHour | hourOfYear | 1442 | 919 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | area | m2 | 9.409999999999991 | 9.409999999999991 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | volume | m3 | 26.966204214472434 | 26.966204214472434 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.designLoad | W | 366.3569641113281 | | | | N/A | unavailable (TasOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakLoad | W | 366.33624267578125 | 190.23285633880042 | 176.10338633698083 | 48.07151622528315 | Fail | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakHour | hourOfYear | 1444 | 919 | 525 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | area | m2 | 4.634866891556 | 4.634866891556 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | volume | m3 | 28.16450759620351 | 28.16450759620351 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.designLoad | W | 69.23214721679688 | | | | N/A | unavailable (TasOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakLoad | W | 11.025825500488281 | 39.29050142981706 | 28.26467592932878 | 71.93768188430163 | Fail | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakHour | hourOfYear | 1452 | 774 | 678 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | area | m2 | 6.955039759539971 | 6.955039759539971 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | volume | m3 | 20.363019689898447 | 20.363019689898447 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.designLoad | W | 204.9483642578125 | | | | N/A | unavailable (TasOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakLoad | W | 117.45973205566406 | 133.44180277588939 | 15.982070720225323 | 11.97680965616646 | Warn | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakHour | hourOfYear | 1447 | 774 | 673 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | area | m2 | 38.98503142608865 | 38.98503142608865 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | volume | m3 | 121.59536233412693 | 121.59536233412693 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.designLoad | W | 1065.076171875 | | | | N/A | unavailable (TasOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakLoad | W | 1025.7105712890625 | 1789.9830426085152 | 764.2724713194527 | 42.69719059492821 | Fail | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakHour | hourOfYear | 1443 | 775 | 668 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.unmetHours | h | 114 | 0 | 114 | 100 | Fail | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | area | m2 | 18.080018143643976 | 18.080018143643976 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | volume | m3 | 51.52805170938531 | 51.52805170938531 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.designLoad | W | 564.768798828125 | | | | N/A | unavailable (TasOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakLoad | W | 559.67822265625 | 936.5975380258114 | 376.91931536956145 | 40.243466384082474 | Fail | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakHour | hourOfYear | 1441 | 727 | 714 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.unmetHours | h | 69 | 0 | 69 | 100 | Fail | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakLoad | W | | 28.70645886203079 | | | N/A | unavailable (OpenStudioOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakHour | hourOfYear | | 7991 | | | N/A | unavailable (OpenStudioOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | area | m2 | 21.280022127733165 | 21.280022127733165 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | volume | m3 | 57.3391691981517 | 57.3391691981517 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.designLoad | W | 549.6607055664062 | | | | N/A | unavailable (TasOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakLoad | W | 529.828857421875 | 989.0652526225581 | 459.2363952006831 | 46.431354653597815 | Fail | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakHour | hourOfYear | 1457 | 919 | 538 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.unmetHours | h | 29 | 0 | 29 | 100 | Fail | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | + From 3f33de5daa67373ffe225c5568430770b4f0bffb Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Sun, 26 Jul 2026 17:30:00 +0200 Subject: [PATCH 07/11] docs: record the cross-engine run with per-space design loads (#10) * docs: record the cross-engine run with per-space design loads Third run in the benchmark report series, on the same model, weather bytes and workstation as the two 2026-07-25 runs, now with SAM_OpenStudio #18 (design-day pressure), #19 (diagnostic propagation) and #20 (per-space design loads from ZoneSizes) merged. Coverage rises 82 -> 92 of 148 comparable. comparison.csv differs from the previous run in exactly 10 rows, every one a heating.designLoad moving from TasOnly to Both; the other 138 rows are byte-identical, so no simulated value moved. The 10 recovered rows disagree in both directions - five above TAS, five below, aggregate OS/TAS 1.396 - which rules out a single multiplicative sizing margin as the explanation and answers the open question about whether TBD's maxHeatingLoad is pre- or post-factor. 68 conversion diagnostics now reach the artefact where the previous run recorded none. They name the London design days carried by this Budapest model, the constant-dew-point and ASHRAEClearSky approximations that replace the hourly design-day profiles (clearness 0.0 on the heating day), and two apertures skipped as degenerate. Two inert provenance deviations are recorded rather than hidden: the weather identity string differs because it is the file name and the earlier runs used a copy named Budapest.epw (the hash is unchanged), and samCommit is an unverifiable operator claim, so the SAM binaries actually loaded are pinned by SHA-256 instead. The merged 2026-07-25 record is left untouched as an immutable artefact. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * docs: keep the TAS sizing-factor question open The bidirectional design-load spread rules out a uniform sizing margin as the SOLE explanation, but it cannot establish whether TBD's maxHeatingLoad is pre- or post-factor. That question is about the TAS side alone and needs a TAS design load compared against its own unfactored TBD result. The earlier wording rested on a premise this same report contradicts: a constant factor would move every row the same way only if the two engines were evaluating comparable physics. They are not - the design-day translation is lossy and the OpenStudio side drops two apertures - so a constant multiplier on the TAS loads remains consistent with ratios above 1 in some spaces and below 1 in others. Raised by Codex on PR #10. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 --------- Co-authored-by: Claude Opus 5 --- .../reports/2026-07-26-design-loads/README.md | 223 + .../benchmark-OpenStudio.json | 958 +++++ .../benchmark-TAS.json | 890 ++++ .../comparison-summary.json | 3798 +++++++++++++++++ .../2026-07-26-design-loads/comparison.csv | 149 + .../2026-07-26-design-loads/comparison.md | 292 ++ 6 files changed, 6310 insertions(+) create mode 100644 docs/benchmark/reports/2026-07-26-design-loads/README.md create mode 100644 docs/benchmark/reports/2026-07-26-design-loads/benchmark-OpenStudio.json create mode 100644 docs/benchmark/reports/2026-07-26-design-loads/benchmark-TAS.json create mode 100644 docs/benchmark/reports/2026-07-26-design-loads/comparison-summary.json create mode 100644 docs/benchmark/reports/2026-07-26-design-loads/comparison.csv create mode 100644 docs/benchmark/reports/2026-07-26-design-loads/comparison.md diff --git a/docs/benchmark/reports/2026-07-26-design-loads/README.md b/docs/benchmark/reports/2026-07-26-design-loads/README.md new file mode 100644 index 0000000..5baafae --- /dev/null +++ b/docs/benchmark/reports/2026-07-26-design-loads/README.md @@ -0,0 +1,223 @@ +# B3 — cross-engine comparison with per-space design loads and conversion diagnostics + +Third run in the series, after the +[first real cross-engine run](../2026-07-25-cross-engine-smoke/README.md) and the +[provenance-fixed repeat](../2026-07-25-cross-engine-provenance-fixed/README.md). Same source model, +same weather bytes, same workstation — now with three merged OpenStudio producer changes: + +| PR | Merge | Change | +|---|---|---| +| [SAM_OpenStudio #18](https://github.com/SAM-BIM/SAM_OpenStudio/pull/18) | `67a6688` | design-day barometric pressure (avoids the EnergyPlus 31000 Pa IDD floor) | +| [SAM_OpenStudio #19](https://github.com/SAM-BIM/SAM_OpenStudio/pull/19) | `f86c4c5` | conversion diagnostics propagate on successful runs too | +| [SAM_OpenStudio #20](https://github.com/SAM-BIM/SAM_OpenStudio/pull/20) | `4306625` | per-space design loads from SQL `ZoneSizes` | + +Its purpose is narrow: record what those three changes do to coverage and to the reported numbers. It +remains a **mechanical validation of the comparator and the producers**, not a validated +engine-equivalence claim. + +## Run + +- **Date:** 2026-07-26 (TAS `14:57:18Z`, OpenStudio `14:58:27Z`). +- **Machine:** the same licensed EDSL Tas workstation as the two previous runs. +- **Model:** `HungaryHouse-WeahterDDY.sam` — 14 spaces, carrying embedded design days. +- **Weather:** `HUN_CEN_Budapest.Met.Center.128400_TMYx.epw`. +- **Engines:** Tas `9.5.7.0` (producer duration 56.9 s); EnergyPlus `25.1.0` / OpenStudio SDK + `3.10.0` (producer duration 22.5 s). +- **Producers built from:** `SAM_Tas@ff00497` and `SAM_OpenStudio@4306625`, both on `sow/2026-Q3`. +- **Comparator:** `SAM_Validation@a4065aad3bfaac41837ae2e784a09ec44b849544`. +- Both producers exited `0` with `state = Success`; the comparator exited `0`. + +Artefacts in this folder are the verbatim outputs: the two neutral input documents and the three +generated reports. + +### Two provenance deviations from the previous run, both inert + +Recorded here so neither is mistaken for a result. + +1. **`weather.identity` differs, `weather.hash` does not.** This run records + `HUN_CEN_Budapest.Met.Center.128400_TMYx`; the previous two recorded `Budapest`. The producers set + the identity from the file name only + (`WeatherIdentity = Path.GetFileNameWithoutExtension(weatherPath)`), so the earlier runs were + handed a copy of the same EPW named `Budapest.epw`. The **hash is unchanged on both sides** + (`sha256:472ffb4e…`), so the weather bytes are provably identical. The provenance gate compares + TAS identity against OpenStudio identity within a run, and both sides here got the same path, so + it still passes. `comparison.csv` does not carry the identity, so no metric row is affected. +2. **`samCommit` is an operator claim, not a measured fact.** `--sam-commit` is passed on the command + line and the producers cannot verify it, because local SAM assemblies are unstamped (`1.0.0.0`). + This run records `abcd617887f6792047023820f3efd2463fc5c6b3`, as the previous run did, so the two + are comparable — but the SAM binaries actually loaded were built **2026-07-21 22:03**, whereas + `abcd617` was committed **2026-07-24 10:36**, so they cannot contain it. `abcd617` changed one + file (`Query/TryGetSAMGeometries.cs`, a Grasshopper geometry-conversion path) that is not on the + producers' route, which is why this is a labelling defect and not a result defect. The binaries + are pinned instead by SHA-256, truncated to 16 hex characters: + + | Assembly | SHA-256 (first 16) | + |---|---| + | `SAM.Core.dll` | `e58a912b08d9d1c9` | + | `SAM.Geometry.dll` | `943ab8f28019876e` | + | `SAM.Analytical.dll` | `a9de2c2639efd467` | + | `SAM.Architectural.dll` | `99b8aa84c45a0b02` | + | `SAM.Weather.dll` | `6aa69438f08dc9d3` | + + These are the same binaries the previous run used — nothing in this session rebuilt `SAM\build` — + so SAM is genuinely held constant across the two runs, which is what the comparison below needs. + **Carried forward as a producer defect:** provenance should record a hash of the loaded assemblies + rather than an unverifiable commit string. + +## Result 1 — coverage rises 82 → 92, and nothing else moves + +| Field | Previous run | This run | +|---|---|---| +| Required metrics compared | 82 of 148 | **92 of 148** | +| Required metrics unavailable | 66 | **56** | +| Matched / warned / failed | 48 / 1 / 33 | 48 / 1 / **43** | +| Coverage status | Warn | Warn | +| Provenance status | Pass | Pass | +| Overall gate | Fail | Fail | + +`comparison.csv` differs from the previous run in **exactly 10 rows**, and every one of them is a +`heating.designLoad` row moving from `TasOnly` to `Both`. Every other row — all 138 of them — is +byte-identical, including all annual energy, peak load, peak hour, unmet hour and geometry values. + +So the whole of the change is the newly extracted design loads. The 10 rows land as `Fail`, which is +where the `fail` count goes from 33 to 43; the `match` and `warn` counts are unchanged. **Reading +this as a regression would be wrong** — these 10 metrics were previously not compared at all, and an +uncompared metric was never a pass. + +## Result 2 — the design loads disagree in *both* directions + +This is the substantive finding, and it bears directly on the open sizing-factor question. + +| Space | TAS (W) | OpenStudio (W) | Rel. diff | Direction | +|---|---:|---:|---:|---| +| `0872cd39` | 574.0 | 1132.1 | 49.3 % | OS higher | +| `c7df54b3` | 1065.1 | 1826.3 | 41.7 % | OS higher | +| `d8294f2a` | 564.8 | 917.5 | 38.4 % | OS higher | +| `d94f4dbe` | 549.7 | 909.1 | 39.5 % | OS higher | +| `354b8786` | 137.8 | 241.9 | 43.0 % | OS higher | +| `7ee49aac` | 366.4 | 170.6 | 53.4 % | OS lower | +| `6e7ef848` | 379.9 | 237.9 | 37.4 % | OS lower | +| `affc43b0` | 204.9 | 128.4 | 37.4 % | OS lower | +| `8d2d2a02` | 69.2 | 43.0 | 37.9 % | OS lower | +| `13a0403a` | 199.8 | 132.9 | 33.5 % | OS lower | +| **Sum** | **4111.7** | **5739.8** | — | OS/TAS = **1.396** | + +Five rows up, five rows down, with the aggregate 39.6 % higher on the OpenStudio side. What this +establishes is bounded: **no single multiplicative factor maps the TAS column onto the OpenStudio +column**, so a uniform sizing margin is ruled out as the *sole* explanation of the design-load +disagreement. The producer already reports `CalcDesLoad` rather than `UserDesLoad`, so EnergyPlus's +own 1.25 sizing factor is excluded on that side. + +**It does not settle whether TBD's `maxHeatingLoad` is pre- or post-factor**, and this run should not +be cited as having answered that. Two reasons: + +- That question is about the TAS side alone. Answering it means comparing a TAS design load against + its own unfactored TBD result — not against OpenStudio. +- This report documents that the two engines are **not evaluating comparable physics**: the + design-day translation is lossy (Result 3) and the OpenStudio side drops two apertures. A constant + multiplier applied to the TAS loads is therefore entirely consistent with ratios above 1 in some + spaces and below 1 in others, whenever the underlying per-space loads already differ. The + bidirectional spread cannot separate a sizing margin from that redistribution. + +The sizing-factor question stays **open for Stage B**. + +### An observed correlation, offered as a lead only + +The split is not random with respect to TAS's own unmet hours: + +| Direction | Spaces | TAS `heating.unmetHours` | +|---|---:|---| +| OS higher | 5 | 29, 65, 67, 69, 114 — all non-zero | +| OS lower | 5 | 0, 0, 0, 0 — and one outlier at **4761** | + +OpenStudio sizes *above* TAS in exactly those spaces where TAS reports moderate unmet heating hours, +and *below* TAS where TAS meets the load in every hour. The single exception, `13a0403a`, is +pathological rather than borderline: 4761 unmet hours is over half the year, and its TAS design load +(199.83349609375 W) sits just 0.0082 W above its annual peak load (199.82528686523438 W), i.e. with +no meaningful headroom at all. + +This is a correlation across ten rows on one fixture, and this run was not designed to test it. It is +recorded as a **lead for Stage B**, not a mechanism. Two readings are consistent with it and are not +distinguished by this data: TAS may be sizing to a design day that under-represents the load these +spaces actually see, or the two engines may be attributing load differently between adjacent spaces. + +## Result 3 — 68 conversion diagnostics now reach the artefact, where there were none + +The previous run recorded `warnings: []` and `notes: []` on the OpenStudio side. That was the +[#19](https://github.com/SAM-BIM/SAM_OpenStudio/pull/19) defect: diagnostics were dropped on +*successful* runs, so they only ever surfaced on failure. This run carries 68: + +| Code | Count | What it reports | +|---|---:|---| +| `SAM-OS-GEO-002` | 43 | duplicate/collinear vertices removed from panels (up to 24 → 7) | +| `SAM-OS-IC-001` | 14 | pollutant generation not converted (out of scope) | +| `SAM-OS-RUN-002` | 9 | design-day and weather translation (6 warnings + 3 notes) | +| `SAM-OS-GEO-001` | 2 | **apertures skipped as degenerate** | + +Three of these are material to the investigation, and all three are now self-documenting in the +committed artefact rather than resident in a session transcript: + +1. **The design days are London.** The warnings name them verbatim: + `London_TRY ANN HTG 100% CONDS DB` and `London_TRY ANN CLG 0% CONDS DB=>GRad`, on a model whose + annual weather is Budapest. This confirms in the artefact what was previously only known from + inspecting the `.sam`: **HungaryHouse is a known-mismatch forensic fixture, not a sizing + baseline.** +2. **The design-day translation is lossy, in named ways.** The warnings state that the hourly + humidity profile "was approximated as a constant dew point" (−5.2 °C for heating, 12.6 °C for + cooling), and that the hourly solar profile "was approximated by the ASHRAEClearSky model" with + **clearness 0.0 for the heating day** and 1.0 for cooling. A heating design day with clearness 0.0 + carries zero solar for all 24 hours. The `#18` pressure fix is also visible, reporting that the + elevation-based standard `101025 Pa` is written "instead of leaving the EnergyPlus 31000 Pa + default". This is precisely the hourly → parametric collapse that the design-day audit exists to + quantify, and it is now on the record from the producer's own output. +3. **Two apertures were skipped entirely.** `SAM-OS-GEO-001` reports one source aperture + (`24df7efc-1805-40ef-a7e7-fc8cd33c7225`) collapsing to 2 sides under the 0.01 m proximity rule on + two surfaces, and being dropped. The EnergyPlus model therefore has two fewer openings than the + SAM model. Both are on `SIM_INT_SLD` (internal) surfaces, so the likely effect is on inter-space + heat flow rather than solar gain — which makes it a candidate for the per-space load + redistribution in Result 2. **Not yet tested**, and the gbXML/TAS side has not been checked for + the same drop. + +The 43 `GEO-002` vertex removals are worth a separate look: reducing a 24-vertex panel to 7 is a +large simplification, and it is applied silently to the OpenStudio side only. + +## Carried forward + +The 56 unavailable required metrics (all 14 spaces matched, so none of this is an alignment problem): + +| Count | Metrics | Availability | Meaning | +|---:|---|---|---| +| 14 | `cooling.designLoad`, all 14 spaces | TasOnly | `ZoneSizes` has **no `Cooling` rows at all** for this model | +| 4 | `heating.designLoad`, 4 spaces | TasOnly | zones EnergyPlus did not size | +| 24 | `cooling.peakLoad` + `cooling.peakHour`, 12 spaces | OpenStudioOnly | TAS reports no cooling result where OpenStudio does | +| 8 | `heating`/`cooling` `peakLoad` + `peakHour`, 2 spaces | Neither | neither engine reports these two spaces | +| 4 | `heating`/`cooling` `unmetHours`, 2 spaces | TasOnly | | +| 2 | `heating.peakLoad` + `heating.peakHour`, 1 space | OpenStudioOnly | | + +- **Design loads fell from 28 unavailable to 18.** The remaining 18 are all one-sided from TAS: the + complete absence of `Cooling` rows in `ZoneSizes` is now the single largest coverage gap, and it is + a producer/route question, not a comparator one. +- `ZoneSizes.PeakTemp` remains **unmapped to any SAM parameter** and is carried verbatim on + `OpenStudioZoneSizingResult`. Every row carried `-3.20000004768372`, bit-identical to the design + day's *outdoor* maximum dry bulb, although EnergyPlus documents it as a zone value. Unsettled. +- `UserDesLoad` is preserved on the result set and may become its own metric + (`sizingCapacity` / `userDesignLoad`). +- The `Site coordinates taken from …` `Information` diagnostic in `Convert/ToOpenStudio/Weather.cs` + still carries the `RunCliFailed` code, so it does **not** appear in the 68 above despite being + material — site elevation feeds the design-day standard pressure that `#18` now writes. Re-coding + it changes conversion output, so it is a separate PR. +- The `eplusout.err` elevation warning (`Elevation difference=[416.00] percent, [104.00] meters`) is + **not fixed**, deliberately: which site is authoritative for each engine has to be established + first. +- TAS still reports `peakCoolingHour = 0` with `available: true` for a measured-zero cooling load. + +## Status + +- **Design-load coverage: improved and verified.** 10 of 28 recovered, with proof that no other + metric moved. +- **Diagnostic visibility: closed.** The producer's own warnings now name the London design days, the + humidity/solar approximations and the skipped apertures, inside the committed artefact. +- **Engine equivalence: open.** No single multiplicative margin reconciles the ten design-load rows, + but the TAS pre/post-factor question is untouched by this run and stays open — it needs a TAS design + load compared against its own unfactored TBD result. Next: the `design-day-audit-v1` neutral + contract, then the TAS (TBD + TSD) and OpenStudio (`.eio`) design-day producers. diff --git a/docs/benchmark/reports/2026-07-26-design-loads/benchmark-OpenStudio.json b/docs/benchmark/reports/2026-07-26-design-loads/benchmark-OpenStudio.json new file mode 100644 index 0000000..01c4f78 --- /dev/null +++ b/docs/benchmark/reports/2026-07-26-design-loads/benchmark-OpenStudio.json @@ -0,0 +1,958 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "43066253b20e56d14367bb9a525b40bb6bbfe52f", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "25.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "HUN_CEN_Budapest.Met.Center.128400_TMYx", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-26T14:58:27.9258228Z", + "durationSeconds": 22.497965, + "state": "Success", + "resultSources": [ + "SAM.Analytical.OpenStudio" + ], + "warnings": [ + "[SAM-OS-GEO-001] Warning: Aperture would be degenerate in EnergyPlus: its vertices collapse to 2 side(s) under the 0.01 m proximity rule (sliver geometry); skipped \u2014 repair the source aperture (SAM Aperture 24df7efc-1805-40ef-a7e7-fc8cd33c7225) \u2192 SAM_SubSurface_SIM_INT_SLD_11_24df7efc", + "[SAM-OS-GEO-001] Warning: Aperture would be degenerate in EnergyPlus: its vertices collapse to 2 side(s) under the 0.01 m proximity rule (sliver geometry); skipped \u2014 repair the source aperture (SAM Aperture 24df7efc-1805-40ef-a7e7-fc8cd33c7225) \u2192 SAM_SubSurface_SIM_INT_SLD_8_24df7efc", + "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel 9525acbb-71d0-4e09-beb4-20825202fa1f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_11_9525acbb", + "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel 9525acbb-71d0-4e09-beb4-20825202fa1f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_4_9525acbb", + "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel d112c262-362c-4e80-9265-55d8385289a7) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_4_d112c262", + "[SAM-OS-GEO-002] Warning: Removed 10 duplicate/collinear vertices (15 \u2192 5) (SAM Panel 9605c081-960c-4204-9ea7-b9d0ce5ecf82) \u2192 SAM_Surface_SIM_INT_SLD_FLR_13_9605c081", + "[SAM-OS-GEO-002] Warning: Removed 10 duplicate/collinear vertices (15 \u2192 5) (SAM Panel 9605c081-960c-4204-9ea7-b9d0ce5ecf82) \u2192 SAM_Surface_SIM_INT_SLD_FLR_6_9605c081", + "[SAM-OS-GEO-002] Warning: Removed 11 duplicate/collinear vertices (15 \u2192 4) (SAM Panel eb313b0a-6b48-4279-a2d4-7f88cfac3c97) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_6_eb313b0a", + "[SAM-OS-GEO-002] Warning: Removed 12 duplicate/collinear vertices (17 \u2192 5) (SAM Panel 4dd3f4f2-9c41-4aff-8a74-87e55ecbf381) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_8_4dd3f4f2", + "[SAM-OS-GEO-002] Warning: Removed 12 duplicate/collinear vertices (18 \u2192 6) (SAM Panel ab1bdb45-5dce-4fdc-889c-622d35a54ee7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_ab1bdb45", + "[SAM-OS-GEO-002] Warning: Removed 13 duplicate/collinear vertices (20 \u2192 7) (SAM Panel 2fdf4184-838c-454f-acd1-b3efb35e08ea) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_7_2fdf4184", + "[SAM-OS-GEO-002] Warning: Removed 14 duplicate/collinear vertices (18 \u2192 4) (SAM Panel ab1bdb45-5dce-4fdc-889c-622d35a54ee7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_7_ab1bdb45", + "[SAM-OS-GEO-002] Warning: Removed 14 duplicate/collinear vertices (20 \u2192 6) (SAM Panel a10a7a36-f696-455c-87fc-6cf3c4af093a) \u2192 SAM_Surface_SIM_EXT_SLD_0_a10a7a36", + "[SAM-OS-GEO-002] Warning: Removed 17 duplicate/collinear vertices (24 \u2192 7) (SAM Panel 7da3b4ec-d56e-4308-9470-03840f81c246) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_0_7da3b4ec", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 304137db-7ca7-406f-8fd2-2a40a90a4d91) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_304137db", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 304137db-7ca7-406f-8fd2-2a40a90a4d91) \u2192 SAM_Surface_SIM_INT_SLD_FLR_9_304137db", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 46748422-6946-403f-8b92-e541bdfa6806) \u2192 SAM_Surface_SIM_EXT_SLD_8_46748422", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 5ff33694-7651-4fc9-99d2-1df97c72fcf7) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_5_5ff33694", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel b9425bc5-bc74-44b1-ab57-038579a614d3) \u2192 SAM_Surface_SIM_EXT_SLD_6_b9425bc5", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel c668837d-34ef-465c-8a2b-1d96faa11fb8) \u2192 SAM_Surface_SIM_EXT_SLD_10_c668837d", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel ddc53dfb-89ea-4907-a720-40a05ba9eb2f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_12_ddc53dfb", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel ddc53dfb-89ea-4907-a720-40a05ba9eb2f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_5_ddc53dfb", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel e982e233-c3c7-443f-8e72-e1414cfd7a11) \u2192 SAM_Surface_SIM_INT_SLD_Partition_11_e982e233", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel e982e233-c3c7-443f-8e72-e1414cfd7a11) \u2192 SAM_Surface_SIM_INT_SLD_Partition_8_e982e233", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (11 \u2192 8) (SAM Panel 2373dee4-5fae-4552-bc6a-bf6a3b926b3b) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_1_2373dee4", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel 6ef72c4a-6cfe-4839-862b-3b5884026cb3) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_3_6ef72c4a", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel 7dbfb73e-24b7-4ce7-b380-26651faf2b02) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_13_7dbfb73e", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel bdfe1887-f40d-44cf-a108-f903de8d69ea) \u2192 SAM_Surface_SIM_INT_SLD_FLR_10_bdfe1887", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel bdfe1887-f40d-44cf-a108-f903de8d69ea) \u2192 SAM_Surface_SIM_INT_SLD_FLR_3_bdfe1887", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel e79ed3b6-8a14-4c1b-bbef-0ac5ad4cc8b7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_e79ed3b6", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel e79ed3b6-8a14-4c1b-bbef-0ac5ad4cc8b7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_10_e79ed3b6", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel 90e290fb-acd7-4869-a5a6-a88ab8d4a4b5) \u2192 SAM_Surface_SIM_EXT_SLD_7_90e290fb", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel 976a502c-fd38-4b41-9c8f-6df45eb318c9) \u2192 SAM_Surface_SIM_EXT_SLD_5_976a502c", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel b5cfef9d-d61a-4a33-9fa3-c181ba5447d6) \u2192 SAM_Surface_SIM_EXT_SLD_3_b5cfef9d", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel d5360b9a-cce9-4583-a7e7-985b0161ae4c) \u2192 SAM_Surface_SIM_EXT_SLD_7_d5360b9a", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel dc004ac8-e855-46b9-ad73-dd26c8fca0a1) \u2192 SAM_Surface_SIM_EXT_SLD_1_dc004ac8", + "[SAM-OS-GEO-002] Warning: Removed 5 duplicate/collinear vertices (11 \u2192 6) (SAM Panel eac60a33-bf37-4255-a9a9-86b88486013c) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_eac60a33", + "[SAM-OS-GEO-002] Warning: Removed 5 duplicate/collinear vertices (11 \u2192 6) (SAM Panel eac60a33-bf37-4255-a9a9-86b88486013c) \u2192 SAM_Surface_SIM_INT_SLD_FLR_8_eac60a33", + "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel b2ceb2f0-16b9-4711-afe8-84c5d5073f4d) \u2192 SAM_Surface_SIM_INT_SLD_FLR_1_b2ceb2f0", + "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel b2ceb2f0-16b9-4711-afe8-84c5d5073f4d) \u2192 SAM_Surface_SIM_INT_SLD_FLR_8_b2ceb2f0", + "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel e651d6ae-0dee-4f8a-bf92-3c63732b821d) \u2192 SAM_Surface_SIM_EXT_SLD_6_e651d6ae", + "[SAM-OS-GEO-002] Warning: Removed 7 duplicate/collinear vertices (12 \u2192 5) (SAM Panel fffe6b7c-6554-4388-8b48-2a1af9321321) \u2192 SAM_Surface_SIM_EXT_SLD_8_fffe6b7c", + "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 55a8c3b8-94de-439b-94d2-2331f5b78bce) \u2192 SAM_Surface_SIM_EXT_SLD_0_55a8c3b8", + "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 6d8d3d15-314a-4dde-bc66-35ba0e30dc36) \u2192 SAM_Surface_SIM_EXT_SLD_0_6d8d3d15", + "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 9516906c-832b-49c0-8960-ad019a33ff78) \u2192 SAM_Surface_SIM_EXT_SLD_6_9516906c", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 0944207e-e492-45ac-a962-06891228f02f) \u2192 SAM_InternalCondition_S21_DomStore_dc8aa269", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 0fce4eca-718f-441e-9262-586b3a86f827) \u2192 SAM_InternalCondition_S15_DomBath_fed079cd", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 4c0a6313-bc80-4388-afe6-c4f43d97f450) \u2192 SAM_InternalCondition_S00_Unconditioned_924d7fd2", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 6be45072-bce2-4765-b85f-2f32862ca20b) \u2192 SAM_InternalCondition_S22_DomToilet_1b0c5a3e", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 7a96ac57-e5e6-4697-b433-52fce9d6e6b5) \u2192 SAM_InternalCondition_S16_DomBed_baece118", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 817fc162-7c50-48a5-be38-2f4b195281be) \u2192 SAM_InternalCondition_S20_DomLiving_28619b20", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition a4fa3b9e-3a47-4b65-9f87-7d5f5a1a7283) \u2192 SAM_InternalCondition_S16_DomBed_8521df7f", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition d27581a5-c45e-4ea8-a7a1-6322e509bf64) \u2192 SAM_InternalCondition_S15_DomBath_8f261a30", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition db2bc3b9-53f7-4c1f-83dd-ba3525e51567) \u2192 SAM_InternalCondition_S17_DomCirculation_3d3b8e9e", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition dd085a1e-ac39-425b-9f37-29506b53f612) \u2192 SAM_InternalCondition_S17_DomCirculation_08f6e1dc", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition de8a8726-58a9-41ad-934e-7ab759aff4f7) \u2192 SAM_InternalCondition_S16_DomBed_4bdd0777", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition ea700b3c-a049-4e12-8acf-da82fd8ac80f) \u2192 SAM_InternalCondition_S35_MechPlant_69a598cf", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition f050b569-0bb0-4582-86ff-fce5e0c1dbdf) \u2192 SAM_InternalCondition_S00_Unconditioned_ab9965d6", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition f71a8c74-a5aa-4970-8f9b-5be7be923f41) \u2192 SAM_InternalCondition_S17_DomCirculation_21eac1e6", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027 carries no usable barometric-pressure profile; the elevation-based standard (101025 Pa) is written instead of leaving the EnergyPlus 31000 Pa default", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027: the hourly humidity profile was approximated as a constant dew point (12.6 \u00B0C) from 33% RH at the maximum-dry-bulb hour", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027: the hourly solar profile was approximated by the ASHRAEClearSky model with clearness 1.0; daily dry-bulb range, mean wind/pressure and the SummerDesignDay day type are derived from the SAM hourly profile", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027 carries no usable barometric-pressure profile; the elevation-based standard (101025 Pa) is written instead of leaving the EnergyPlus 31000 Pa default", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027: the hourly humidity profile was approximated as a constant dew point (-5.2 \u00B0C) from 86% RH at the maximum-dry-bulb hour", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027: the hourly solar profile was approximated by the ASHRAEClearSky model with clearness 0.0; daily dry-bulb range, mean wind/pressure and the WinterDesignDay day type are derived from the SAM hourly profile" + ], + "notes": [ + "[SAM-OS-RUN-002] Information: Annual weather source: explicit EPW (HUN_CEN_Budapest.Met.Center.128400_TMYx.epw)", + "[SAM-OS-RUN-002] Information: Design-day source: AnalyticalModel heating/cooling design days (2 imported)", + "[SAM-OS-RUN-002] Information: Ground temperatures taken from the SAM model WeatherData (depth 0 m set, 12 monthly values)" + ] + }, + "model": { + "consumptionHeating": { + "value": 1751.1764825992748, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": 5.2499930426550705, + "unit": "kWh", + "available": true + }, + "peakHeatingLoad": { + "value": 5.720615732872154, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 775, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 0.0402250938503556, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 7974, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 167.95195207345205, + "unit": "m2", + "available": true + }, + "volume": { + "value": 470.1821620538829, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "025eb80f65d745b799511b78ac2f82fb", + "name": "01_18 Void", + "area": { + "value": 6.6920779999999995, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.118059598283878, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "name": "01_13 Bedroom", + "area": { + "value": 21.280010175465385, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.33913699268221, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 1132.105378358097, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 1185.0374713731348, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 15.588918884437554, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 7974, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "name": "GF_08 Bathroom", + "area": { + "value": 3.647967799712404, + "unit": "m2", + "available": true + }, + "volume": { + "value": 10.396708229180378, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 132.9146643279224, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 141.7703965184828, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 918, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "name": "01_15 Plantroom", + "area": { + "value": 2.9450000000000016, + "unit": "m2", + "available": true + }, + "volume": { + "value": 5.746544632499992, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "name": "GF_09 WC", + "area": { + "value": 2.355387206192353, + "unit": "m2", + "available": true + }, + "volume": { + "value": 6.712853537648204, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "354b8786b4714f3c9941cec051a93361", + "name": "GF_10 Closet", + "area": { + "value": 3.0986077487352315, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.007679927026205, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 241.94985081273356, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 262.9986126690514, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "name": "01_11 Void", + "area": { + "value": 5.03787628653, + "unit": "m2", + "available": true + }, + "volume": { + "value": 7.564547888993382, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": null, + "unit": "h", + "available": false + } + } + }, + { + "guid": "6e7ef8487b714da3b730e0f226af927e", + "name": "01_17 Gallery", + "area": { + "value": 23.55004650825489, + "unit": "m2", + "available": true + }, + "volume": { + "value": 58.340316505330264, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 237.9464743064166, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 255.3860902131586, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "name": "01_16 Bathroom", + "area": { + "value": 9.409999999999991, + "unit": "m2", + "available": true + }, + "volume": { + "value": 26.966204214472434, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 170.60334873223655, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 190.23285633880042, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "name": "GF_05 Stairs", + "area": { + "value": 4.634866891556, + "unit": "m2", + "available": true + }, + "volume": { + "value": 28.16450759620351, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 42.95932098398786, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 39.29050142981706, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 774, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "name": "GF_12 EntranceHall", + "area": { + "value": 6.955039759539971, + "unit": "m2", + "available": true + }, + "volume": { + "value": 20.363019689898447, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 128.39770298313388, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 133.44180277588939, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 774, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "name": "GF_07 LivingRoom", + "area": { + "value": 38.98503142608865, + "unit": "m2", + "available": true + }, + "volume": { + "value": 121.59536233412693, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 1826.2977051410624, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 1789.9830426085152, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 775, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "name": "GF_11 Bedroom", + "area": { + "value": 18.080018143643976, + "unit": "m2", + "available": true + }, + "volume": { + "value": 51.52805170938531, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 917.4634857295723, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 936.5975380258114, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 727, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 28.70645886203079, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 7991, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "name": "01_14 Bedroom", + "area": { + "value": 21.280022127733165, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.3391691981517, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 909.1363260313252, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 989.0652526225581, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + } + ] +} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-26-design-loads/benchmark-TAS.json b/docs/benchmark/reports/2026-07-26-design-loads/benchmark-TAS.json new file mode 100644 index 0000000..2319ca6 --- /dev/null +++ b/docs/benchmark/reports/2026-07-26-design-loads/benchmark-TAS.json @@ -0,0 +1,890 @@ +{ + "schemaVersion": "1.0.0", + "provenance": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", + "engine": { + "kind": "TAS", + "name": "Tas", + "version": "9.5.7.0", + "sdkVersion": null + }, + "route": "Native-TAS", + "weather": { + "identity": "HUN_CEN_Budapest.Met.Center.128400_TMYx", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-26T14:57:18.7687622Z", + "durationSeconds": 56.9406901, + "state": "Success", + "resultSources": [ + "SAM.Analytical.Tas" + ], + "warnings": [], + "notes": [ + "Shared gbXML was exported from the source model (no --gbxml supplied)." + ] + }, + "model": { + "consumptionHeating": { + "value": 4331.961588091359, + "unit": "kWh", + "available": true + }, + "consumptionCooling": { + "value": 0, + "unit": "kWh", + "available": true + }, + "peakHeatingLoad": { + "value": 3.93393212890625, + "unit": "kW", + "available": true + }, + "peakHeatingHour": { + "value": 919, + "unit": "hourOfYear", + "available": true + }, + "peakCoolingLoad": { + "value": 0, + "unit": "kW", + "available": true + }, + "peakCoolingHour": { + "value": 0, + "unit": "hourOfYear", + "available": true + }, + "floorArea": { + "value": 167.95195245742798, + "unit": "m2", + "available": true + }, + "volume": { + "value": 470.1821608543396, + "unit": "m3", + "available": true + } + }, + "spaces": [ + { + "guid": "025eb80f65d745b799511b78ac2f82fb", + "name": "01_18 Void", + "area": { + "value": 6.6920779999999995, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.118059598283878, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "name": "01_13 Bedroom", + "area": { + "value": 21.280010175465385, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.33913699268221, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 574.0481567382812, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 565.50927734375, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 65, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "name": "GF_08 Bathroom", + "area": { + "value": 3.647967799712404, + "unit": "m2", + "available": true + }, + "volume": { + "value": 10.396708229180378, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 199.83349609375, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 199.82528686523438, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 4761, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "name": "01_15 Plantroom", + "area": { + "value": 2.9450000000000016, + "unit": "m2", + "available": true + }, + "volume": { + "value": 5.746544632499992, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 28.741456985473633, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "name": "GF_09 WC", + "area": { + "value": 2.355387206192353, + "unit": "m2", + "available": true + }, + "volume": { + "value": 6.712853537648204, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 69.60518646240234, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 52.024513244628906, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "354b8786b4714f3c9941cec051a93361", + "name": "GF_10 Closet", + "area": { + "value": 3.0986077487352315, + "unit": "m2", + "available": true + }, + "volume": { + "value": 9.007679927026205, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 137.82980346679688, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 117.870849609375, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 67, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "name": "01_11 Void", + "area": { + "value": 5.03787628653, + "unit": "m2", + "available": true + }, + "volume": { + "value": 7.564547888993382, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "6e7ef8487b714da3b730e0f226af927e", + "name": "01_17 Gallery", + "area": { + "value": 23.55004650825489, + "unit": "m2", + "available": true + }, + "volume": { + "value": 58.340316505330264, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 379.9020080566406, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 132.86868286132812, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1442, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "name": "01_16 Bathroom", + "area": { + "value": 9.409999999999991, + "unit": "m2", + "available": true + }, + "volume": { + "value": 26.966204214472434, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 366.3569641113281, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 366.33624267578125, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1444, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "name": "GF_05 Stairs", + "area": { + "value": 4.634866891556, + "unit": "m2", + "available": true + }, + "volume": { + "value": 28.16450759620351, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 69.23214721679688, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 11.025825500488281, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1452, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "name": "GF_12 EntranceHall", + "area": { + "value": 6.955039759539971, + "unit": "m2", + "available": true + }, + "volume": { + "value": 20.363019689898447, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 204.9483642578125, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 117.45973205566406, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1447, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "name": "GF_07 LivingRoom", + "area": { + "value": 38.98503142608865, + "unit": "m2", + "available": true + }, + "volume": { + "value": 121.59536233412693, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 1065.076171875, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 1025.7105712890625, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1443, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 114, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "name": "GF_11 Bedroom", + "area": { + "value": 18.080018143643976, + "unit": "m2", + "available": true + }, + "volume": { + "value": 51.52805170938531, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 564.768798828125, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 559.67822265625, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1441, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 69, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + }, + { + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "name": "01_14 Bedroom", + "area": { + "value": 21.280022127733165, + "unit": "m2", + "available": true + }, + "volume": { + "value": 57.3391691981517, + "unit": "m3", + "available": true + }, + "heating": { + "designLoad": { + "value": 549.6607055664062, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": 529.828857421875, + "unit": "W", + "available": true + }, + "peakHour": { + "value": 1457, + "unit": "hourOfYear", + "available": true + }, + "unmetHours": { + "value": 29, + "unit": "h", + "available": true + } + }, + "cooling": { + "designLoad": { + "value": 0, + "unit": "W", + "available": true + }, + "peakLoad": { + "value": null, + "unit": "W", + "available": false + }, + "peakHour": { + "value": null, + "unit": "hourOfYear", + "available": false + }, + "unmetHours": { + "value": 0, + "unit": "h", + "available": true + } + } + } + ] +} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-26-design-loads/comparison-summary.json b/docs/benchmark/reports/2026-07-26-design-loads/comparison-summary.json new file mode 100644 index 0000000..7d9cbe4 --- /dev/null +++ b/docs/benchmark/reports/2026-07-26-design-loads/comparison-summary.json @@ -0,0 +1,3798 @@ +{ + "summarySchemaVersion": "1.0.0", + "toleranceProfile": "default", + "toleranceProfileDetail": { + "name": "default", + "warnRelative": 0.05, + "failRelative": 0.15, + "hourWarnAbsolute": 1, + "hourFailAbsolute": 24, + "defaultNearZeroFloor": 0, + "nearZeroFloors": { + "kWh": 1, + "Wh": 1000, + "kW": 0.01, + "W": 10, + "m2": 0.01, + "m3": 0.01, + "h": 0.5 + } + }, + "gate": "Fail", + "statuses": { + "overall": "Fail", + "numerical": "Fail", + "coverage": "Warn", + "provenance": "Pass", + "reconciliation": "Pass" + }, + "provenanceCompatibility": { + "compatible": true, + "mismatches": [] + }, + "counts": { + "match": 48, + "warn": 1, + "fail": 43, + "notApplicable": 56 + }, + "coverage": { + "status": "Warn", + "requiredMetricCount": 148, + "comparableMetricCount": 92, + "unavailableRequiredMetricCount": 56, + "unmatchedSpaceCount": 0, + "ambiguousSpaceCount": 0, + "duplicateSpaceIdentityCount": 0 + }, + "schema": { + "tas": "1.0.0", + "openStudio": "1.0.0", + "driftNote": null + }, + "provenance": { + "tas": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", + "engine": { + "kind": "TAS", + "name": "Tas", + "version": "9.5.7.0", + "sdkVersion": null + }, + "route": "Native-TAS", + "weather": { + "identity": "HUN_CEN_Budapest.Met.Center.128400_TMYx", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-26T14:57:18.7687622Z", + "durationSeconds": 56.9406901, + "state": "Success", + "resultSources": [ + "SAM.Analytical.Tas" + ], + "warnings": [], + "notes": [ + "Shared gbXML was exported from the source model (no --gbxml supplied)." + ] + }, + "openStudio": { + "sourceModelName": "000000_SAM_AnalyticalModel", + "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", + "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", + "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", + "canonicalizationVersion": "1.0.0", + "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", + "runnerCommit": "43066253b20e56d14367bb9a525b40bb6bbfe52f", + "engine": { + "kind": "OpenStudio", + "name": "EnergyPlus", + "version": "25.1.0", + "sdkVersion": "3.10.0" + }, + "route": "Native-OpenStudio", + "weather": { + "identity": "HUN_CEN_Budapest.Met.Center.128400_TMYx", + "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" + }, + "designDaySource": "EmbeddedModel", + "runTimestampUtc": "2026-07-26T14:58:27.9258228Z", + "durationSeconds": 22.497965, + "state": "Success", + "resultSources": [ + "SAM.Analytical.OpenStudio" + ], + "warnings": [ + "[SAM-OS-GEO-001] Warning: Aperture would be degenerate in EnergyPlus: its vertices collapse to 2 side(s) under the 0.01 m proximity rule (sliver geometry); skipped \u2014 repair the source aperture (SAM Aperture 24df7efc-1805-40ef-a7e7-fc8cd33c7225) \u2192 SAM_SubSurface_SIM_INT_SLD_11_24df7efc", + "[SAM-OS-GEO-001] Warning: Aperture would be degenerate in EnergyPlus: its vertices collapse to 2 side(s) under the 0.01 m proximity rule (sliver geometry); skipped \u2014 repair the source aperture (SAM Aperture 24df7efc-1805-40ef-a7e7-fc8cd33c7225) \u2192 SAM_SubSurface_SIM_INT_SLD_8_24df7efc", + "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel 9525acbb-71d0-4e09-beb4-20825202fa1f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_11_9525acbb", + "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel 9525acbb-71d0-4e09-beb4-20825202fa1f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_4_9525acbb", + "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel d112c262-362c-4e80-9265-55d8385289a7) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_4_d112c262", + "[SAM-OS-GEO-002] Warning: Removed 10 duplicate/collinear vertices (15 \u2192 5) (SAM Panel 9605c081-960c-4204-9ea7-b9d0ce5ecf82) \u2192 SAM_Surface_SIM_INT_SLD_FLR_13_9605c081", + "[SAM-OS-GEO-002] Warning: Removed 10 duplicate/collinear vertices (15 \u2192 5) (SAM Panel 9605c081-960c-4204-9ea7-b9d0ce5ecf82) \u2192 SAM_Surface_SIM_INT_SLD_FLR_6_9605c081", + "[SAM-OS-GEO-002] Warning: Removed 11 duplicate/collinear vertices (15 \u2192 4) (SAM Panel eb313b0a-6b48-4279-a2d4-7f88cfac3c97) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_6_eb313b0a", + "[SAM-OS-GEO-002] Warning: Removed 12 duplicate/collinear vertices (17 \u2192 5) (SAM Panel 4dd3f4f2-9c41-4aff-8a74-87e55ecbf381) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_8_4dd3f4f2", + "[SAM-OS-GEO-002] Warning: Removed 12 duplicate/collinear vertices (18 \u2192 6) (SAM Panel ab1bdb45-5dce-4fdc-889c-622d35a54ee7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_ab1bdb45", + "[SAM-OS-GEO-002] Warning: Removed 13 duplicate/collinear vertices (20 \u2192 7) (SAM Panel 2fdf4184-838c-454f-acd1-b3efb35e08ea) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_7_2fdf4184", + "[SAM-OS-GEO-002] Warning: Removed 14 duplicate/collinear vertices (18 \u2192 4) (SAM Panel ab1bdb45-5dce-4fdc-889c-622d35a54ee7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_7_ab1bdb45", + "[SAM-OS-GEO-002] Warning: Removed 14 duplicate/collinear vertices (20 \u2192 6) (SAM Panel a10a7a36-f696-455c-87fc-6cf3c4af093a) \u2192 SAM_Surface_SIM_EXT_SLD_0_a10a7a36", + "[SAM-OS-GEO-002] Warning: Removed 17 duplicate/collinear vertices (24 \u2192 7) (SAM Panel 7da3b4ec-d56e-4308-9470-03840f81c246) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_0_7da3b4ec", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 304137db-7ca7-406f-8fd2-2a40a90a4d91) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_304137db", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 304137db-7ca7-406f-8fd2-2a40a90a4d91) \u2192 SAM_Surface_SIM_INT_SLD_FLR_9_304137db", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 46748422-6946-403f-8b92-e541bdfa6806) \u2192 SAM_Surface_SIM_EXT_SLD_8_46748422", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 5ff33694-7651-4fc9-99d2-1df97c72fcf7) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_5_5ff33694", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel b9425bc5-bc74-44b1-ab57-038579a614d3) \u2192 SAM_Surface_SIM_EXT_SLD_6_b9425bc5", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel c668837d-34ef-465c-8a2b-1d96faa11fb8) \u2192 SAM_Surface_SIM_EXT_SLD_10_c668837d", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel ddc53dfb-89ea-4907-a720-40a05ba9eb2f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_12_ddc53dfb", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel ddc53dfb-89ea-4907-a720-40a05ba9eb2f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_5_ddc53dfb", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel e982e233-c3c7-443f-8e72-e1414cfd7a11) \u2192 SAM_Surface_SIM_INT_SLD_Partition_11_e982e233", + "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel e982e233-c3c7-443f-8e72-e1414cfd7a11) \u2192 SAM_Surface_SIM_INT_SLD_Partition_8_e982e233", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (11 \u2192 8) (SAM Panel 2373dee4-5fae-4552-bc6a-bf6a3b926b3b) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_1_2373dee4", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel 6ef72c4a-6cfe-4839-862b-3b5884026cb3) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_3_6ef72c4a", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel 7dbfb73e-24b7-4ce7-b380-26651faf2b02) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_13_7dbfb73e", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel bdfe1887-f40d-44cf-a108-f903de8d69ea) \u2192 SAM_Surface_SIM_INT_SLD_FLR_10_bdfe1887", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel bdfe1887-f40d-44cf-a108-f903de8d69ea) \u2192 SAM_Surface_SIM_INT_SLD_FLR_3_bdfe1887", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel e79ed3b6-8a14-4c1b-bbef-0ac5ad4cc8b7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_e79ed3b6", + "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel e79ed3b6-8a14-4c1b-bbef-0ac5ad4cc8b7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_10_e79ed3b6", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel 90e290fb-acd7-4869-a5a6-a88ab8d4a4b5) \u2192 SAM_Surface_SIM_EXT_SLD_7_90e290fb", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel 976a502c-fd38-4b41-9c8f-6df45eb318c9) \u2192 SAM_Surface_SIM_EXT_SLD_5_976a502c", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel b5cfef9d-d61a-4a33-9fa3-c181ba5447d6) \u2192 SAM_Surface_SIM_EXT_SLD_3_b5cfef9d", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel d5360b9a-cce9-4583-a7e7-985b0161ae4c) \u2192 SAM_Surface_SIM_EXT_SLD_7_d5360b9a", + "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel dc004ac8-e855-46b9-ad73-dd26c8fca0a1) \u2192 SAM_Surface_SIM_EXT_SLD_1_dc004ac8", + "[SAM-OS-GEO-002] Warning: Removed 5 duplicate/collinear vertices (11 \u2192 6) (SAM Panel eac60a33-bf37-4255-a9a9-86b88486013c) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_eac60a33", + "[SAM-OS-GEO-002] Warning: Removed 5 duplicate/collinear vertices (11 \u2192 6) (SAM Panel eac60a33-bf37-4255-a9a9-86b88486013c) \u2192 SAM_Surface_SIM_INT_SLD_FLR_8_eac60a33", + "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel b2ceb2f0-16b9-4711-afe8-84c5d5073f4d) \u2192 SAM_Surface_SIM_INT_SLD_FLR_1_b2ceb2f0", + "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel b2ceb2f0-16b9-4711-afe8-84c5d5073f4d) \u2192 SAM_Surface_SIM_INT_SLD_FLR_8_b2ceb2f0", + "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel e651d6ae-0dee-4f8a-bf92-3c63732b821d) \u2192 SAM_Surface_SIM_EXT_SLD_6_e651d6ae", + "[SAM-OS-GEO-002] Warning: Removed 7 duplicate/collinear vertices (12 \u2192 5) (SAM Panel fffe6b7c-6554-4388-8b48-2a1af9321321) \u2192 SAM_Surface_SIM_EXT_SLD_8_fffe6b7c", + "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 55a8c3b8-94de-439b-94d2-2331f5b78bce) \u2192 SAM_Surface_SIM_EXT_SLD_0_55a8c3b8", + "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 6d8d3d15-314a-4dde-bc66-35ba0e30dc36) \u2192 SAM_Surface_SIM_EXT_SLD_0_6d8d3d15", + "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 9516906c-832b-49c0-8960-ad019a33ff78) \u2192 SAM_Surface_SIM_EXT_SLD_6_9516906c", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 0944207e-e492-45ac-a962-06891228f02f) \u2192 SAM_InternalCondition_S21_DomStore_dc8aa269", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 0fce4eca-718f-441e-9262-586b3a86f827) \u2192 SAM_InternalCondition_S15_DomBath_fed079cd", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 4c0a6313-bc80-4388-afe6-c4f43d97f450) \u2192 SAM_InternalCondition_S00_Unconditioned_924d7fd2", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 6be45072-bce2-4765-b85f-2f32862ca20b) \u2192 SAM_InternalCondition_S22_DomToilet_1b0c5a3e", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 7a96ac57-e5e6-4697-b433-52fce9d6e6b5) \u2192 SAM_InternalCondition_S16_DomBed_baece118", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 817fc162-7c50-48a5-be38-2f4b195281be) \u2192 SAM_InternalCondition_S20_DomLiving_28619b20", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition a4fa3b9e-3a47-4b65-9f87-7d5f5a1a7283) \u2192 SAM_InternalCondition_S16_DomBed_8521df7f", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition d27581a5-c45e-4ea8-a7a1-6322e509bf64) \u2192 SAM_InternalCondition_S15_DomBath_8f261a30", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition db2bc3b9-53f7-4c1f-83dd-ba3525e51567) \u2192 SAM_InternalCondition_S17_DomCirculation_3d3b8e9e", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition dd085a1e-ac39-425b-9f37-29506b53f612) \u2192 SAM_InternalCondition_S17_DomCirculation_08f6e1dc", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition de8a8726-58a9-41ad-934e-7ab759aff4f7) \u2192 SAM_InternalCondition_S16_DomBed_4bdd0777", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition ea700b3c-a049-4e12-8acf-da82fd8ac80f) \u2192 SAM_InternalCondition_S35_MechPlant_69a598cf", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition f050b569-0bb0-4582-86ff-fce5e0c1dbdf) \u2192 SAM_InternalCondition_S00_Unconditioned_ab9965d6", + "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition f71a8c74-a5aa-4970-8f9b-5be7be923f41) \u2192 SAM_InternalCondition_S17_DomCirculation_21eac1e6", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027 carries no usable barometric-pressure profile; the elevation-based standard (101025 Pa) is written instead of leaving the EnergyPlus 31000 Pa default", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027: the hourly humidity profile was approximated as a constant dew point (12.6 \u00B0C) from 33% RH at the maximum-dry-bulb hour", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027: the hourly solar profile was approximated by the ASHRAEClearSky model with clearness 1.0; daily dry-bulb range, mean wind/pressure and the SummerDesignDay day type are derived from the SAM hourly profile", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027 carries no usable barometric-pressure profile; the elevation-based standard (101025 Pa) is written instead of leaving the EnergyPlus 31000 Pa default", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027: the hourly humidity profile was approximated as a constant dew point (-5.2 \u00B0C) from 86% RH at the maximum-dry-bulb hour", + "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027: the hourly solar profile was approximated by the ASHRAEClearSky model with clearness 0.0; daily dry-bulb range, mean wind/pressure and the WinterDesignDay day type are derived from the SAM hourly profile" + ], + "notes": [ + "[SAM-OS-RUN-002] Information: Annual weather source: explicit EPW (HUN_CEN_Budapest.Met.Center.128400_TMYx.epw)", + "[SAM-OS-RUN-002] Information: Design-day source: AnalyticalModel heating/cooling design days (2 imported)", + "[SAM-OS-RUN-002] Information: Ground temperatures taken from the SAM model WeatherData (depth 0 m set, 12 monthly values)" + ] + } + }, + "modelMetrics": [ + { + "scope": "model", + "key": "consumptionHeating", + "unit": "kWh", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4331.961588091359, + "available": true, + "unit": "kWh" + }, + "openStudio": { + "value": 1751.1764825992748, + "available": true, + "unit": "kWh" + }, + "absoluteDifference": 2580.785105492084, + "relativeDifference": 0.5957543835537944, + "signedDifference": -2580.785105492084 + }, + { + "scope": "model", + "key": "consumptionCooling", + "unit": "kWh", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "kWh" + }, + "openStudio": { + "value": 5.2499930426550705, + "available": true, + "unit": "kWh" + }, + "absoluteDifference": 5.2499930426550705, + "relativeDifference": 1, + "signedDifference": 5.2499930426550705 + }, + { + "scope": "model", + "key": "peakHeatingLoad", + "unit": "kW", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.93393212890625, + "available": true, + "unit": "kW" + }, + "openStudio": { + "value": 5.720615732872154, + "available": true, + "unit": "kW" + }, + "absoluteDifference": 1.7866836039659035, + "relativeDifference": 0.3123236531513824, + "signedDifference": 1.7866836039659035 + }, + { + "scope": "model", + "key": "peakHeatingHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 775, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 144, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "peakCoolingLoad", + "unit": "kW", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "kW" + }, + "openStudio": { + "value": 0.0402250938503556, + "available": true, + "unit": "kW" + }, + "absoluteDifference": 0.0402250938503556, + "relativeDifference": 1, + "signedDifference": 0.0402250938503556 + }, + { + "scope": "model", + "key": "peakCoolingHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7974, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 786, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "model", + "key": "floorArea", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 167.95195245742798, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 167.95195207345205, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 3.839759301627055E-07, + "relativeDifference": 2.2862248669602973E-09, + "signedDifference": -3.839759301627055E-07 + }, + { + "scope": "model", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 470.1821608543396, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 470.1821620538829, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 1.1995433055744797E-06, + "relativeDifference": 2.5512309959496336E-09, + "signedDifference": 1.1995433055744797E-06 + } + ], + "spaces": [ + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "guid": "025eb80f65d745b799511b78ac2f82fb", + "tasName": "01_18 Void", + "openStudioName": "01_18 Void", + "matchKind": "Guid", + "metrics": [ + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.6920779999999995, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 6.6920779999999995, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.118059598283878, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 9.118059598283878, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "heating.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "025eb80f65d745b799511b78ac2f82fb", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "guid": "0872cd39962f43c3b62a6e031a76c24a", + "tasName": "01_13 Bedroom", + "openStudioName": "01_13 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 21.280010175465385, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 21.280010175465385, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 57.33913699268221, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 57.33913699268221, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 574.0481567382812, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 1132.105378358097, + "available": true, + "unit": "W" + }, + "absoluteDifference": 558.0572216198157, + "relativeDifference": 0.4929375235626663, + "signedDifference": 558.0572216198157 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 565.50927734375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 1185.0374713731348, + "available": true, + "unit": "W" + }, + "absoluteDifference": 619.5281940293848, + "relativeDifference": 0.5227920711330089, + "signedDifference": 619.5281940293848 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 522, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 65, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 65, + "relativeDifference": 1, + "signedDifference": -65 + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 15.588918884437554, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7974, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "0872cd39962f43c3b62a6e031a76c24a", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "guid": "13a0403ab3a543608613b0ce9bf52d92", + "tasName": "GF_08 Bathroom", + "openStudioName": "GF_08 Bathroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.647967799712404, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 3.647967799712404, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 10.396708229180378, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 10.396708229180378, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 199.83349609375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 132.9146643279224, + "available": true, + "unit": "W" + }, + "absoluteDifference": 66.9188317658276, + "relativeDifference": 0.3348729470980844, + "signedDifference": -66.9188317658276 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 199.82528686523438, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 141.7703965184828, + "available": true, + "unit": "W" + }, + "absoluteDifference": 58.05489034675156, + "relativeDifference": 0.2905282472378222, + "signedDifference": -58.05489034675156 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 918, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 523, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4761, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 4761, + "relativeDifference": 1, + "signedDifference": -4761 + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "13a0403ab3a543608613b0ce9bf52d92", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", + "tasName": "01_15 Plantroom", + "openStudioName": "01_15 Plantroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 2.9450000000000016, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 2.9450000000000016, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 5.746544632499992, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 5.746544632499992, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 28.741456985473633, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", + "tasName": "GF_09 WC", + "openStudioName": "GF_09 WC", + "matchKind": "Guid", + "metrics": [ + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 2.355387206192353, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 2.355387206192353, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.712853537648204, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 6.712853537648204, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 69.60518646240234, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 52.024513244628906, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": 52.024513244628906, + "relativeDifference": 1, + "signedDifference": -52.024513244628906 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 1441, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "guid": "354b8786b4714f3c9941cec051a93361", + "tasName": "GF_10 Closet", + "openStudioName": "GF_10 Closet", + "matchKind": "Guid", + "metrics": [ + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 3.0986077487352315, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 3.0986077487352315, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.007679927026205, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 9.007679927026205, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 137.82980346679688, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 241.94985081273356, + "available": true, + "unit": "W" + }, + "absoluteDifference": 104.12004734593668, + "relativeDifference": 0.4303373074882547, + "signedDifference": 104.12004734593668 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 117.870849609375, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 262.9986126690514, + "available": true, + "unit": "W" + }, + "absoluteDifference": 145.1277630596764, + "relativeDifference": 0.5518195004408647, + "signedDifference": 145.1277630596764 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 522, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 67, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 67, + "relativeDifference": 1, + "signedDifference": -67 + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "354b8786b4714f3c9941cec051a93361", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "guid": "4377cbd6712349e68fdae8814ff9c5d9", + "tasName": "01_11 Void", + "openStudioName": "01_11 Void", + "matchKind": "Guid", + "metrics": [ + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 5.03787628653, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 5.03787628653, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 7.564547888993382, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 7.564547888993382, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "heating.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "Neither", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "Neither", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "4377cbd6712349e68fdae8814ff9c5d9", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "h" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + } + ] + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "guid": "6e7ef8487b714da3b730e0f226af927e", + "tasName": "01_17 Gallery", + "openStudioName": "01_17 Gallery", + "matchKind": "Guid", + "metrics": [ + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 23.55004650825489, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 23.55004650825489, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 58.340316505330264, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 58.340316505330264, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 379.9020080566406, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 237.9464743064166, + "available": true, + "unit": "W" + }, + "absoluteDifference": 141.95553375022402, + "relativeDifference": 0.3736635520206555, + "signedDifference": -141.95553375022402 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 132.86868286132812, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 255.3860902131586, + "available": true, + "unit": "W" + }, + "absoluteDifference": 122.51740735183049, + "relativeDifference": 0.4797340655850561, + "signedDifference": 122.51740735183049 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1442, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 523, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "6e7ef8487b714da3b730e0f226af927e", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "guid": "7ee49aac87b24671bb00ed1b291806a1", + "tasName": "01_16 Bathroom", + "openStudioName": "01_16 Bathroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 9.409999999999991, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 9.409999999999991, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 26.966204214472434, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 26.966204214472434, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 366.3569641113281, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 170.60334873223655, + "available": true, + "unit": "W" + }, + "absoluteDifference": 195.75361537909157, + "relativeDifference": 0.5343248103770348, + "signedDifference": -195.75361537909157 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 366.33624267578125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 190.23285633880042, + "available": true, + "unit": "W" + }, + "absoluteDifference": 176.10338633698083, + "relativeDifference": 0.4807151622528315, + "signedDifference": -176.10338633698083 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1444, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 525, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "7ee49aac87b24671bb00ed1b291806a1", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", + "tasName": "GF_05 Stairs", + "openStudioName": "GF_05 Stairs", + "matchKind": "Guid", + "metrics": [ + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 4.634866891556, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 4.634866891556, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 28.16450759620351, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 28.16450759620351, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 69.23214721679688, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 42.95932098398786, + "available": true, + "unit": "W" + }, + "absoluteDifference": 26.272826232809017, + "relativeDifference": 0.37948882548069796, + "signedDifference": -26.272826232809017 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 11.025825500488281, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 39.29050142981706, + "available": true, + "unit": "W" + }, + "absoluteDifference": 28.26467592932878, + "relativeDifference": 0.7193768188430163, + "signedDifference": 28.26467592932878 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1452, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 774, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 678, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", + "tasName": "GF_12 EntranceHall", + "openStudioName": "GF_12 EntranceHall", + "matchKind": "Guid", + "metrics": [ + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 6.955039759539971, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 6.955039759539971, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 20.363019689898447, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 20.363019689898447, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 204.9483642578125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 128.39770298313388, + "available": true, + "unit": "W" + }, + "absoluteDifference": 76.55066127467862, + "relativeDifference": 0.3735119406875703, + "signedDifference": -76.55066127467862 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Warn", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 117.45973205566406, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 133.44180277588939, + "available": true, + "unit": "W" + }, + "absoluteDifference": 15.982070720225323, + "relativeDifference": 0.1197680965616646, + "signedDifference": 15.982070720225323 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1447, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 774, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 673, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "tasName": "GF_07 LivingRoom", + "openStudioName": "GF_07 LivingRoom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 38.98503142608865, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 38.98503142608865, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 121.59536233412693, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 121.59536233412693, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 1065.076171875, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 1826.2977051410624, + "available": true, + "unit": "W" + }, + "absoluteDifference": 761.2215332660624, + "relativeDifference": 0.41681130689876544, + "signedDifference": 761.2215332660624 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 1025.7105712890625, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 1789.9830426085152, + "available": true, + "unit": "W" + }, + "absoluteDifference": 764.2724713194527, + "relativeDifference": 0.4269719059492821, + "signedDifference": 764.2724713194527 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1443, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 775, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 668, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 114, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 114, + "relativeDifference": 1, + "signedDifference": -114 + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", + "tasName": "GF_11 Bedroom", + "openStudioName": "GF_11 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 18.080018143643976, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 18.080018143643976, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 51.52805170938531, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 51.52805170938531, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 564.768798828125, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 917.4634857295723, + "available": true, + "unit": "W" + }, + "absoluteDifference": 352.6946869014473, + "relativeDifference": 0.3844236772224046, + "signedDifference": 352.6946869014473 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 559.67822265625, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 936.5975380258114, + "available": true, + "unit": "W" + }, + "absoluteDifference": 376.91931536956145, + "relativeDifference": 0.4024346638408247, + "signedDifference": 376.91931536956145 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1441, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 727, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 714, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 69, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 69, + "relativeDifference": 1, + "signedDifference": -69 + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 28.70645886203079, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 7991, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "tasName": "01_14 Bedroom", + "openStudioName": "01_14 Bedroom", + "matchKind": "Guid", + "metrics": [ + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "area", + "unit": "m2", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 21.280022127733165, + "available": true, + "unit": "m2" + }, + "openStudio": { + "value": 21.280022127733165, + "available": true, + "unit": "m2" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "volume", + "unit": "m3", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 57.3391691981517, + "available": true, + "unit": "m3" + }, + "openStudio": { + "value": 57.3391691981517, + "available": true, + "unit": "m3" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.designLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 549.6607055664062, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 909.1363260313252, + "available": true, + "unit": "W" + }, + "absoluteDifference": 359.47562046491896, + "relativeDifference": 0.39540342869605327, + "signedDifference": 359.47562046491896 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.peakLoad", + "unit": "W", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 529.828857421875, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": 989.0652526225581, + "available": true, + "unit": "W" + }, + "absoluteDifference": 459.2363952006831, + "relativeDifference": 0.4643135465359781, + "signedDifference": 459.2363952006831 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.peakHour", + "unit": "hourOfYear", + "availability": "Both", + "band": "Fail", + "informational": true, + "notApplicableReason": null, + "circular": true, + "tas": { + "value": 1457, + "available": true, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 919, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": 538, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "heating.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Fail", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 29, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 29, + "relativeDifference": 1, + "signedDifference": -29 + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.designLoad", + "unit": "W", + "availability": "TasOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "W" + }, + "openStudio": { + "value": null, + "available": false, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.peakLoad", + "unit": "W", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": false, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "W" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "W" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.peakHour", + "unit": "hourOfYear", + "availability": "OpenStudioOnly", + "band": "N/A", + "informational": true, + "notApplicableReason": "unavailable", + "circular": false, + "tas": { + "value": null, + "available": false, + "unit": "hourOfYear" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "hourOfYear" + }, + "absoluteDifference": null, + "relativeDifference": null, + "signedDifference": null + }, + { + "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", + "key": "cooling.unmetHours", + "unit": "h", + "availability": "Both", + "band": "Match", + "informational": false, + "notApplicableReason": null, + "circular": false, + "tas": { + "value": 0, + "available": true, + "unit": "h" + }, + "openStudio": { + "value": 0, + "available": true, + "unit": "h" + }, + "absoluteDifference": 0, + "relativeDifference": 0, + "signedDifference": 0 + } + ] + } + ], + "spaceDiagnostics": { + "tasCount": 14, + "openStudioCount": 14, + "matchedByGuid": 14, + "matchedByName": 0, + "onlyInTas": [], + "onlyInOpenStudio": [], + "duplicateTasGuids": [], + "duplicateOpenStudioGuids": [], + "duplicateTasNames": [], + "duplicateOpenStudioNames": [], + "ambiguousNameMatches": [] + }, + "reconciliation": [ + { + "engine": "TAS", + "key": "floorArea", + "unit": "m2", + "modelTotal": 167.95195245742798, + "modelTotalAvailable": true, + "sumOfSpaces": 167.95195207345202, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 3.839759585844149E-07, + "relativeDifference": 2.2862250361855374E-09, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "TAS", + "key": "volume", + "unit": "m3", + "modelTotal": 470.1821608543396, + "modelTotalAvailable": true, + "sumOfSpaces": 470.1821620538828, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 1.199543191887642E-06, + "relativeDifference": 2.5512307541564592E-09, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "OpenStudio", + "key": "floorArea", + "unit": "m2", + "modelTotal": 167.95195207345205, + "modelTotalAvailable": true, + "sumOfSpaces": 167.95195207345202, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 2.842170943040401E-14, + "relativeDifference": 1.6922524019235018E-16, + "band": "Match", + "notApplicableReason": null + }, + { + "engine": "OpenStudio", + "key": "volume", + "unit": "m3", + "modelTotal": 470.1821620538829, + "modelTotalAvailable": true, + "sumOfSpaces": 470.1821620538828, + "contributingSpaces": 14, + "spacesMissingValue": 0, + "excludedSpaces": 0, + "complete": true, + "absoluteDifference": 1.1368683772161603E-13, + "relativeDifference": 2.417931748516387E-16, + "band": "Match", + "notApplicableReason": null + } + ] +} diff --git a/docs/benchmark/reports/2026-07-26-design-loads/comparison.csv b/docs/benchmark/reports/2026-07-26-design-loads/comparison.csv new file mode 100644 index 0000000..061a354 --- /dev/null +++ b/docs/benchmark/reports/2026-07-26-design-loads/comparison.csv @@ -0,0 +1,149 @@ +scope,metric,unit,availability,tasValue,openStudioValue,absoluteDifference,relativeDifference,relativePercent,signedDifference,band,note,toleranceProfile,warnRelative,failRelative,hourWarnAbsolute,hourFailAbsolute,nearZeroFloor,informational +model,consumptionHeating,kWh,Both,4331.961588091359,1751.1764825992748,2580.785105492084,0.5957543835537944,59.57543835537944,-2580.785105492084,Fail,,default,0.05,0.15,1,24,1,false +model,consumptionCooling,kWh,Both,0,5.2499930426550705,5.2499930426550705,1,100,5.2499930426550705,Fail,,default,0.05,0.15,1,24,1,false +model,peakHeatingLoad,kW,Both,3.93393212890625,5.720615732872154,1.7866836039659035,0.3123236531513824,31.23236531513824,1.7866836039659035,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true +model,peakHeatingHour,hourOfYear,Both,919,775,144,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +model,peakCoolingLoad,kW,Both,0,0.0402250938503556,0.0402250938503556,1,100,0.0402250938503556,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true +model,peakCoolingHour,hourOfYear,Both,0,7974,786,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +model,floorArea,m2,Both,167.95195245742798,167.95195207345205,3.839759301627055E-07,2.2862248669602973E-09,2.2862248669602975E-07,-3.839759301627055E-07,Match,,default,0.05,0.15,1,24,0.01,false +model,volume,m3,Both,470.1821608543396,470.1821620538829,1.1995433055744797E-06,2.5512309959496336E-09,2.5512309959496334E-07,1.1995433055744797E-06,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,area,m2,Both,6.6920779999999995,6.6920779999999995,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,volume,m3,Both,9.118059598283878,9.118059598283878,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +025eb80f65d745b799511b78ac2f82fb,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +025eb80f65d745b799511b78ac2f82fb,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +025eb80f65d745b799511b78ac2f82fb,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +025eb80f65d745b799511b78ac2f82fb,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +025eb80f65d745b799511b78ac2f82fb,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +0872cd39962f43c3b62a6e031a76c24a,area,m2,Both,21.280010175465385,21.280010175465385,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0872cd39962f43c3b62a6e031a76c24a,volume,m3,Both,57.33913699268221,57.33913699268221,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +0872cd39962f43c3b62a6e031a76c24a,heating.designLoad,W,Both,574.0481567382812,1132.105378358097,558.0572216198157,0.4929375235626663,49.29375235626663,558.0572216198157,Fail,,default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,heating.peakLoad,W,Both,565.50927734375,1185.0374713731348,619.5281940293848,0.5227920711330089,52.279207113300885,619.5281940293848,Fail,,default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +0872cd39962f43c3b62a6e031a76c24a,heating.unmetHours,h,Both,65,0,65,1,100,-65,Fail,,default,0.05,0.15,1,24,0.5,false +0872cd39962f43c3b62a6e031a76c24a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,cooling.peakLoad,W,OpenStudioOnly,,15.588918884437554,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +0872cd39962f43c3b62a6e031a76c24a,cooling.peakHour,hourOfYear,OpenStudioOnly,,7974,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +0872cd39962f43c3b62a6e031a76c24a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +13a0403ab3a543608613b0ce9bf52d92,area,m2,Both,3.647967799712404,3.647967799712404,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +13a0403ab3a543608613b0ce9bf52d92,volume,m3,Both,10.396708229180378,10.396708229180378,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +13a0403ab3a543608613b0ce9bf52d92,heating.designLoad,W,Both,199.83349609375,132.9146643279224,66.9188317658276,0.3348729470980844,33.48729470980844,-66.9188317658276,Fail,,default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,heating.peakLoad,W,Both,199.82528686523438,141.7703965184828,58.05489034675156,0.2905282472378222,29.052824723782223,-58.05489034675156,Fail,,default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,heating.peakHour,hourOfYear,Both,1441,918,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +13a0403ab3a543608613b0ce9bf52d92,heating.unmetHours,h,Both,4761,0,4761,1,100,-4761,Fail,,default,0.05,0.15,1,24,0.5,false +13a0403ab3a543608613b0ce9bf52d92,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +13a0403ab3a543608613b0ce9bf52d92,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +13a0403ab3a543608613b0ce9bf52d92,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +21bdbb806b4f4e2fbad0632b19fdf14b,area,m2,Both,2.9450000000000016,2.9450000000000016,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +21bdbb806b4f4e2fbad0632b19fdf14b,volume,m3,Both,5.746544632499992,5.746544632499992,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.designLoad,W,TasOnly,28.741456985473633,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +21bdbb806b4f4e2fbad0632b19fdf14b,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +21bdbb806b4f4e2fbad0632b19fdf14b,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +312fab2f1ba64756b47ccc3dcb21ac8a,area,m2,Both,2.355387206192353,2.355387206192353,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +312fab2f1ba64756b47ccc3dcb21ac8a,volume,m3,Both,6.712853537648204,6.712853537648204,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.designLoad,W,TasOnly,69.60518646240234,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakLoad,W,Both,52.024513244628906,0,52.024513244628906,1,100,-52.024513244628906,Fail,,default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakHour,hourOfYear,Both,1441,0,1441,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +312fab2f1ba64756b47ccc3dcb21ac8a,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +312fab2f1ba64756b47ccc3dcb21ac8a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +354b8786b4714f3c9941cec051a93361,area,m2,Both,3.0986077487352315,3.0986077487352315,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +354b8786b4714f3c9941cec051a93361,volume,m3,Both,9.007679927026205,9.007679927026205,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +354b8786b4714f3c9941cec051a93361,heating.designLoad,W,Both,137.82980346679688,241.94985081273356,104.12004734593668,0.4303373074882547,43.03373074882547,104.12004734593668,Fail,,default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,heating.peakLoad,W,Both,117.870849609375,262.9986126690514,145.1277630596764,0.5518195004408647,55.18195004408647,145.1277630596764,Fail,,default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +354b8786b4714f3c9941cec051a93361,heating.unmetHours,h,Both,67,0,67,1,100,-67,Fail,,default,0.05,0.15,1,24,0.5,false +354b8786b4714f3c9941cec051a93361,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +354b8786b4714f3c9941cec051a93361,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +354b8786b4714f3c9941cec051a93361,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +4377cbd6712349e68fdae8814ff9c5d9,area,m2,Both,5.03787628653,5.03787628653,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +4377cbd6712349e68fdae8814ff9c5d9,volume,m3,Both,7.564547888993382,7.564547888993382,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +4377cbd6712349e68fdae8814ff9c5d9,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +4377cbd6712349e68fdae8814ff9c5d9,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false +4377cbd6712349e68fdae8814ff9c5d9,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true +4377cbd6712349e68fdae8814ff9c5d9,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false +6e7ef8487b714da3b730e0f226af927e,area,m2,Both,23.55004650825489,23.55004650825489,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +6e7ef8487b714da3b730e0f226af927e,volume,m3,Both,58.340316505330264,58.340316505330264,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +6e7ef8487b714da3b730e0f226af927e,heating.designLoad,W,Both,379.9020080566406,237.9464743064166,141.95553375022402,0.3736635520206555,37.36635520206555,-141.95553375022402,Fail,,default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,heating.peakLoad,W,Both,132.86868286132812,255.3860902131586,122.51740735183049,0.4797340655850561,47.97340655850561,122.51740735183049,Fail,,default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,heating.peakHour,hourOfYear,Both,1442,919,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +6e7ef8487b714da3b730e0f226af927e,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +6e7ef8487b714da3b730e0f226af927e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +6e7ef8487b714da3b730e0f226af927e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +6e7ef8487b714da3b730e0f226af927e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +7ee49aac87b24671bb00ed1b291806a1,area,m2,Both,9.409999999999991,9.409999999999991,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +7ee49aac87b24671bb00ed1b291806a1,volume,m3,Both,26.966204214472434,26.966204214472434,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +7ee49aac87b24671bb00ed1b291806a1,heating.designLoad,W,Both,366.3569641113281,170.60334873223655,195.75361537909157,0.5343248103770348,53.43248103770348,-195.75361537909157,Fail,,default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,heating.peakLoad,W,Both,366.33624267578125,190.23285633880042,176.10338633698083,0.4807151622528315,48.07151622528315,-176.10338633698083,Fail,,default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,heating.peakHour,hourOfYear,Both,1444,919,525,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +7ee49aac87b24671bb00ed1b291806a1,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +7ee49aac87b24671bb00ed1b291806a1,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +7ee49aac87b24671bb00ed1b291806a1,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +7ee49aac87b24671bb00ed1b291806a1,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +8d2d2a0234584fe4b171e5a9373f1b4d,area,m2,Both,4.634866891556,4.634866891556,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +8d2d2a0234584fe4b171e5a9373f1b4d,volume,m3,Both,28.16450759620351,28.16450759620351,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.designLoad,W,Both,69.23214721679688,42.95932098398786,26.272826232809017,0.37948882548069796,37.9488825480698,-26.272826232809017,Fail,,default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakLoad,W,Both,11.025825500488281,39.29050142981706,28.26467592932878,0.7193768188430163,71.93768188430163,28.26467592932878,Fail,,default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakHour,hourOfYear,Both,1452,774,678,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +8d2d2a0234584fe4b171e5a9373f1b4d,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +8d2d2a0234584fe4b171e5a9373f1b4d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +affc43b0b5b3445f85d5cea8a7b88d4f,area,m2,Both,6.955039759539971,6.955039759539971,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +affc43b0b5b3445f85d5cea8a7b88d4f,volume,m3,Both,20.363019689898447,20.363019689898447,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.designLoad,W,Both,204.9483642578125,128.39770298313388,76.55066127467862,0.3735119406875703,37.35119406875703,-76.55066127467862,Fail,,default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakLoad,W,Both,117.45973205566406,133.44180277588939,15.982070720225323,0.1197680965616646,11.97680965616646,15.982070720225323,Warn,,default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakHour,hourOfYear,Both,1447,774,673,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +affc43b0b5b3445f85d5cea8a7b88d4f,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +affc43b0b5b3445f85d5cea8a7b88d4f,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,area,m2,Both,38.98503142608865,38.98503142608865,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,volume,m3,Both,121.59536233412693,121.59536233412693,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.designLoad,W,Both,1065.076171875,1826.2977051410624,761.2215332660624,0.41681130689876544,41.68113068987655,761.2215332660624,Fail,,default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakLoad,W,Both,1025.7105712890625,1789.9830426085152,764.2724713194527,0.4269719059492821,42.69719059492821,764.2724713194527,Fail,,default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakHour,hourOfYear,Both,1443,775,668,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.unmetHours,h,Both,114,0,114,1,100,-114,Fail,,default,0.05,0.15,1,24,0.5,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +d8294f2a30b445ca9f6ba83c8dcf4903,area,m2,Both,18.080018143643976,18.080018143643976,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d8294f2a30b445ca9f6ba83c8dcf4903,volume,m3,Both,51.52805170938531,51.52805170938531,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.designLoad,W,Both,564.768798828125,917.4634857295723,352.6946869014473,0.3844236772224046,38.44236772224046,352.6946869014473,Fail,,default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakLoad,W,Both,559.67822265625,936.5975380258114,376.91931536956145,0.4024346638408247,40.243466384082474,376.91931536956145,Fail,,default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakHour,hourOfYear,Both,1441,727,714,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +d8294f2a30b445ca9f6ba83c8dcf4903,heating.unmetHours,h,Both,69,0,69,1,100,-69,Fail,,default,0.05,0.15,1,24,0.5,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakLoad,W,OpenStudioOnly,,28.70645886203079,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakHour,hourOfYear,OpenStudioOnly,,7991,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +d8294f2a30b445ca9f6ba83c8dcf4903,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,area,m2,Both,21.280022127733165,21.280022127733165,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,volume,m3,Both,57.3391691981517,57.3391691981517,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.designLoad,W,Both,549.6607055664062,909.1363260313252,359.47562046491896,0.39540342869605327,39.540342869605325,359.47562046491896,Fail,,default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakLoad,W,Both,529.828857421875,989.0652526225581,459.2363952006831,0.4643135465359781,46.431354653597815,459.2363952006831,Fail,,default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakHour,hourOfYear,Both,1457,919,538,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true +d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.unmetHours,h,Both,29,0,29,1,100,-29,Fail,,default,0.05,0.15,1,24,0.5,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true +d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false diff --git a/docs/benchmark/reports/2026-07-26-design-loads/comparison.md b/docs/benchmark/reports/2026-07-26-design-loads/comparison.md new file mode 100644 index 0000000..d562007 --- /dev/null +++ b/docs/benchmark/reports/2026-07-26-design-loads/comparison.md @@ -0,0 +1,292 @@ +# Benchmark comparison: TAS vs OpenStudio + +Independent comparison of two engine-neutral benchmark documents. Tolerance bands are +**provisional reporting buckets**, not validated thresholds. The overall gate is the worst of +the numerical, coverage, provenance and reconciliation statuses. Peak-hour bands and the two +whole-model peak-load bands are informational and are excluded from the numerical status — they are +still calculated and shown, and rows carrying such a band are marked in the Note column; +reconciliation is a separate within-document diagnostic status that also contributes to the +overall gate. + +## Summary + +| Field | Value | +| --- | --- | +| Tolerance profile | default | +| Gate status (overall) | Fail | +| Numerical status | Fail | +| Coverage status | Warn | +| Provenance status | Pass | +| Reconciliation status | Pass | +| Metrics matched | 48 | +| Metrics warned | 1 | +| Metrics failed | 43 | +| Metrics not applicable | 56 | +| Required metrics compared | 92 of 148 | +| Required metrics unavailable | 56 | +| TAS schema version | 1.0.0 | +| OpenStudio schema version | 1.0.0 | + +## Coverage + +How much of the two runs was actually compared. Required metrics are the whole-model metrics plus +the metrics of uniquely matched spaces; the metrics of one-sided, duplicated or ambiguous spaces are +not counted here because those spaces are reported under space alignment instead. An unavailable +metric is never a numerical failure, so it is recorded here — otherwise missing results could read as +a clean pass. + +| Field | Value | +| --- | --- | +| Coverage status | Warn | +| Required metrics | 148 | +| Compared | 92 | +| Unavailable (not compared) | 56 | +| Unmatched spaces | 0 | +| Ambiguous (split) space names | 0 | +| Duplicated space identities | 0 | + +**Coverage is INCOMPLETE**, so this comparison must not be described as a complete result: + +- 56 of 148 required metrics were unavailable on at least one side (reported as N/A, never as a numerical failure). + +## Provenance compatibility + +Compatible: both runs share the same canonical model, weather and design-day basis, with the expected engine and route on each side. + +## Tolerance profile + +Provisional reporting bands (not validated thresholds). Recorded so a changed profile is never hidden behind an unchanged name. + +| Setting | Value | +| --- | --- | +| Name | default | +| Warn (relative) | 0.05 | +| Fail (relative) | 0.15 | +| Peak-hour warn (h) | 1 | +| Peak-hour fail (h) | 24 | +| Default near-zero floor | 0 | +| Near-zero floor (kWh) | 1 | +| Near-zero floor (Wh) | 1000 | +| Near-zero floor (kW) | 0.01 | +| Near-zero floor (W) | 10 | +| Near-zero floor (m2) | 0.01 | +| Near-zero floor (m3) | 0.01 | +| Near-zero floor (h) | 0.5 | + +## Provenance + +| Field | TAS | OpenStudio | +| --- | --- | --- | +| Engine kind | TAS | OpenStudio | +| Engine name | Tas | EnergyPlus | +| Engine version | 9.5.7.0 | 25.1.0 | +| SDK version | | 3.10.0 | +| Route | Native-TAS | Native-OpenStudio | +| Weather | HUN_CEN_Budapest.Met.Center.128400_TMYx | HUN_CEN_Budapest.Met.Center.128400_TMYx | +| Design-day source | EmbeddedModel | EmbeddedModel | +| Run state | Success | Success | +| Source model | 000000_SAM_AnalyticalModel | 000000_SAM_AnalyticalModel | +| Source model GUID | a527e890fceb465fbb50f4b6874f3a4c | a527e890fceb465fbb50f4b6874f3a4c | +| SAM commit | abcd617887f6792047023820f3efd2463fc5c6b3 | abcd617887f6792047023820f3efd2463fc5c6b3 | + +## Model metrics + +> **Caveats (apply even when values pass):** whole-model peak-load semantics differ between +> engines — OpenStudio reports a coincident total, TAS a building-profile maximum — so peak-load +> comparisons are informational, not equivalence claims. `peakHeatingLoad` and `peakCoolingLoad` +> are therefore banded and shown below but **excluded from the numerical status**, exactly like the +> peak-hour metrics; promoting them into the gate needs the B4 corpus and energy-modeller review. +> Every other metric — annual energy, per-space peak loads, unmet hours, geometry — gates normally. +> The primary conditioning pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing) +> is a **candidate** pending validation. A tolerance profile cannot remove these limitations. + +| Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | +| --- | --- | --- | --- | --- | --- | --- | --- | +| consumptionHeating | kWh | 4331.961588091359 | 1751.1764825992748 | 2580.785105492084 | 59.57543835537944 | Fail | | +| consumptionCooling | kWh | 0 | 5.2499930426550705 | 5.2499930426550705 | 100 | Fail | | +| peakHeatingLoad | kW | 3.93393212890625 | 5.720615732872154 | 1.7866836039659035 | 31.23236531513824 | Fail | informational (excluded from the numerical status) | +| peakHeatingHour | hourOfYear | 919 | 775 | 144 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| peakCoolingLoad | kW | 0 | 0.0402250938503556 | 0.0402250938503556 | 100 | Fail | informational (excluded from the numerical status) | +| peakCoolingHour | hourOfYear | 0 | 7974 | 786 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| floorArea | m2 | 167.95195245742798 | 167.95195207345205 | 3.839759301627055E-07 | 2.2862248669602975E-07 | Match | | +| volume | m3 | 470.1821608543396 | 470.1821620538829 | 1.1995433055744797E-06 | 2.5512309959496334E-07 | Match | | + +## Model-total vs sum-of-spaces reconciliation + +Additive quantities only; non-additive peak loads are not reconciled. Only uniquely-matched spaces are summed; unmatched or ambiguous spaces make a reconciliation incomplete. + +| Engine | Quantity | Unit | Model total | Sum of spaces | Abs diff | Band | Used/Missing/Excluded | Complete | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| TAS | floorArea | m2 | 167.95195245742798 | 167.95195207345202 | 3.839759585844149E-07 | Match | 14/0/0 | yes | +| TAS | volume | m3 | 470.1821608543396 | 470.1821620538828 | 1.199543191887642E-06 | Match | 14/0/0 | yes | +| OpenStudio | floorArea | m2 | 167.95195207345205 | 167.95195207345202 | 2.842170943040401E-14 | Match | 14/0/0 | yes | +| OpenStudio | volume | m3 | 470.1821620538829 | 470.1821620538828 | 1.1368683772161603E-13 | Match | 14/0/0 | yes | + +## Space alignment + +| Field | Value | +| --- | --- | +| TAS spaces | 14 | +| OpenStudio spaces | 14 | +| Matched by GUID | 14 | +| Matched by name | 0 | + +**Only in TAS:** none + +**Only in OpenStudio:** none + +**Duplicate TAS GUIDs:** none + +**Duplicate OpenStudio GUIDs:** none + +**Duplicate TAS names:** none + +**Duplicate OpenStudio names:** none + +**Ambiguous (split) name matches:** none + +## Space metrics + +| Space | Match | Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 025eb80f65d745b799511b78ac2f82fb | Guid | area | m2 | 6.6920779999999995 | 6.6920779999999995 | 0 | 0 | Match | | +| 025eb80f65d745b799511b78ac2f82fb | Guid | volume | m3 | 9.118059598283878 | 9.118059598283878 | 0 | 0 | Match | | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | area | m2 | 21.280010175465385 | 21.280010175465385 | 0 | 0 | Match | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | volume | m3 | 57.33913699268221 | 57.33913699268221 | 0 | 0 | Match | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.designLoad | W | 574.0481567382812 | 1132.105378358097 | 558.0572216198157 | 49.29375235626663 | Fail | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakLoad | W | 565.50927734375 | 1185.0374713731348 | 619.5281940293848 | 52.279207113300885 | Fail | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.unmetHours | h | 65 | 0 | 65 | 100 | Fail | | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakLoad | W | | 15.588918884437554 | | | N/A | unavailable (OpenStudioOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakHour | hourOfYear | | 7974 | | | N/A | unavailable (OpenStudioOnly) | +| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | area | m2 | 3.647967799712404 | 3.647967799712404 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | volume | m3 | 10.396708229180378 | 10.396708229180378 | 0 | 0 | Match | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.designLoad | W | 199.83349609375 | 132.9146643279224 | 66.9188317658276 | 33.48729470980844 | Fail | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakLoad | W | 199.82528686523438 | 141.7703965184828 | 58.05489034675156 | 29.052824723782223 | Fail | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakHour | hourOfYear | 1441 | 918 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.unmetHours | h | 4761 | 0 | 4761 | 100 | Fail | | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | area | m2 | 2.9450000000000016 | 2.9450000000000016 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | volume | m3 | 5.746544632499992 | 5.746544632499992 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.designLoad | W | 28.741456985473633 | | | | N/A | unavailable (TasOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | area | m2 | 2.355387206192353 | 2.355387206192353 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | volume | m3 | 6.712853537648204 | 6.712853537648204 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.designLoad | W | 69.60518646240234 | | | | N/A | unavailable (TasOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakLoad | W | 52.024513244628906 | 0 | 52.024513244628906 | 100 | Fail | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakHour | hourOfYear | 1441 | 0 | 1441 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | area | m2 | 3.0986077487352315 | 3.0986077487352315 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | volume | m3 | 9.007679927026205 | 9.007679927026205 | 0 | 0 | Match | | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.designLoad | W | 137.82980346679688 | 241.94985081273356 | 104.12004734593668 | 43.03373074882547 | Fail | | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakLoad | W | 117.870849609375 | 262.9986126690514 | 145.1277630596764 | 55.18195004408647 | Fail | | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 354b8786b4714f3c9941cec051a93361 | Guid | heating.unmetHours | h | 67 | 0 | 67 | 100 | Fail | | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | area | m2 | 5.03787628653 | 5.03787628653 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | volume | m3 | 7.564547888993382 | 7.564547888993382 | 0 | 0 | Match | | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | +| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | area | m2 | 23.55004650825489 | 23.55004650825489 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | volume | m3 | 58.340316505330264 | 58.340316505330264 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.designLoad | W | 379.9020080566406 | 237.9464743064166 | 141.95553375022402 | 37.36635520206555 | Fail | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakLoad | W | 132.86868286132812 | 255.3860902131586 | 122.51740735183049 | 47.97340655850561 | Fail | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakHour | hourOfYear | 1442 | 919 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | area | m2 | 9.409999999999991 | 9.409999999999991 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | volume | m3 | 26.966204214472434 | 26.966204214472434 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.designLoad | W | 366.3569641113281 | 170.60334873223655 | 195.75361537909157 | 53.43248103770348 | Fail | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakLoad | W | 366.33624267578125 | 190.23285633880042 | 176.10338633698083 | 48.07151622528315 | Fail | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakHour | hourOfYear | 1444 | 919 | 525 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | area | m2 | 4.634866891556 | 4.634866891556 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | volume | m3 | 28.16450759620351 | 28.16450759620351 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.designLoad | W | 69.23214721679688 | 42.95932098398786 | 26.272826232809017 | 37.9488825480698 | Fail | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakLoad | W | 11.025825500488281 | 39.29050142981706 | 28.26467592932878 | 71.93768188430163 | Fail | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakHour | hourOfYear | 1452 | 774 | 678 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | area | m2 | 6.955039759539971 | 6.955039759539971 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | volume | m3 | 20.363019689898447 | 20.363019689898447 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.designLoad | W | 204.9483642578125 | 128.39770298313388 | 76.55066127467862 | 37.35119406875703 | Fail | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakLoad | W | 117.45973205566406 | 133.44180277588939 | 15.982070720225323 | 11.97680965616646 | Warn | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakHour | hourOfYear | 1447 | 774 | 673 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | area | m2 | 38.98503142608865 | 38.98503142608865 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | volume | m3 | 121.59536233412693 | 121.59536233412693 | 0 | 0 | Match | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.designLoad | W | 1065.076171875 | 1826.2977051410624 | 761.2215332660624 | 41.68113068987655 | Fail | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakLoad | W | 1025.7105712890625 | 1789.9830426085152 | 764.2724713194527 | 42.69719059492821 | Fail | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakHour | hourOfYear | 1443 | 775 | 668 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.unmetHours | h | 114 | 0 | 114 | 100 | Fail | | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | area | m2 | 18.080018143643976 | 18.080018143643976 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | volume | m3 | 51.52805170938531 | 51.52805170938531 | 0 | 0 | Match | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.designLoad | W | 564.768798828125 | 917.4634857295723 | 352.6946869014473 | 38.44236772224046 | Fail | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakLoad | W | 559.67822265625 | 936.5975380258114 | 376.91931536956145 | 40.243466384082474 | Fail | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakHour | hourOfYear | 1441 | 727 | 714 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.unmetHours | h | 69 | 0 | 69 | 100 | Fail | | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakLoad | W | | 28.70645886203079 | | | N/A | unavailable (OpenStudioOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakHour | hourOfYear | | 7991 | | | N/A | unavailable (OpenStudioOnly) | +| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | area | m2 | 21.280022127733165 | 21.280022127733165 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | volume | m3 | 57.3391691981517 | 57.3391691981517 | 0 | 0 | Match | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.designLoad | W | 549.6607055664062 | 909.1363260313252 | 359.47562046491896 | 39.540342869605325 | Fail | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakLoad | W | 529.828857421875 | 989.0652526225581 | 459.2363952006831 | 46.431354653597815 | Fail | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakHour | hourOfYear | 1457 | 919 | 538 | | Fail | circular hour difference; informational (excluded from the numerical status) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.unmetHours | h | 29 | 0 | 29 | 100 | Fail | | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | +| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | + From b0b28b863afc7593cd36f63373db6ec7b86d03e8 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Mon, 27 Jul 2026 11:21:13 +0200 Subject: [PATCH 08/11] docs: draft the design-day-audit-v1 neutral contract (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: draft the design-day-audit-v1 neutral contract Defines design-day-audit-.json: an engine-neutral record of the design days each engine actually used and how each engine responded. Docs only - no schema types, no producers, no comparator, per the agreed sequence that the TAS and OpenStudio producer branches wait until this is settled. Built around the three representations, declared and never inferred: ObservedHourly (TAS, from TBD - nothing fitted), ProcessedParametric (EnergyPlus, echoed back in eplusout.eio - not a series) and DerivedHourly (reconstructed offline from those parameters). The rule that a DerivedHourly series was never "reported by" the engine is normative, and comparing a parametric definition hour-by-hour against an observed series is prohibited. Collapse loss - an ObservedHourly summarised into parametric terms against the engine's own echo - needs no reconstruction and is therefore the strongest available evidence. Stage A (conditions) and Stage B (sizing response) are structurally separate: separate top-level objects, separate statuses, joined only by designDayKey, with the rule that a Stage B difference is uninterpretable without its Stage A pairing. Provenance is stricter than the benchmark contract, because the recorded runs showed a samCommit whose code was demonstrably not in the loaded binaries: source files and hashes, LOADED ASSEMBLY HASHES as the authoritative record (commit strings are subordinate), per-design-day identity and location (per day, not per document - the recorded fixture carries London design days with Budapest weather), model vs weather-file elevations with an explicit elevationAuthority that may be Unknown, sizing factors with a required reportedLoadBasis, and a value basis of Observed / EngineEchoed / Reconstructed on every number. reportedLoadBasis is the field that makes the pre/post-sizing-factor question answerable, and the contract states it is settled per engine against that engine's own unfactored result - never by a cross-engine ratio. Validation makes an unlabelled or over-claimed value an error rather than a reporting judgement: a missing basis, an EngineEchoed series, a reconstruction without a declared algorithm, or an omitted basis field are all invalid, while Unknown is always a legal answer. Records what the TAS side already provides (DesignDataDictionary yields peak scalars and the owning design data) and three constraints on it: MaxValueDictionary collapses the per-design-day dimension, the GetPeakZoneGains index base and span are unverified so peakHour stays unavailable until pinned, and YearlyValues.cs must not be reused for the 24-hour series. Section 11 lists five open questions that need a decision before implementation. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * docs: fix four contract defects found by Codex review All four were real gaps in the draft. P1 - cross-document alignment was undefined. `key` is only stable within one document and `name` is deliberately the unmodified engine label, so a translated or renamed design day had no deterministic way to be paired across the two documents, leaving both Stage A comparisons and the Stage B interpretation resting on them ambiguous. Added a required `alignmentKey` derived from the SHARED SAM SOURCE design day - the common ancestor - since SAM DesignDay derives from Weather.WeatherDay and carries no GUID: the key is `|` with a specified normalisation (trim, collapse internal whitespace, ToUpperInvariant) and a versioned `alignmentKeyVersion`. Pairing is on exact equality only, with no fuzzy fallback; an untraceable day gets a null key and is reported one-sided rather than guessed; a duplicated key excludes the whole group from pairing, mirroring the duplicate-GUID space rule. P1 - Stage B series were invalid by construction. `HourlySeries.quantity` was restricted to the Stage A token list, so the promised TAS `zoneSensibleLoad`/`zoneTemperature` series would fail validation rule 4 whenever emitted. Added a Stage B token table (zoneSensibleLoad, zoneLatentLoad, zoneTemperature, zoneHumidityRatio, zoneSetpointTemperature) and scoped the rule to each stage's own list, with crossing prohibited. P2 - the parametric block declared every field an AuditValue, whose value is a number, while the contract then listed strings (dryBulbRangeModifierType, humidityConditionType, solarModel) and booleans. No implementation could satisfy both. Split into three shapes sharing the available/basis discipline: AuditValue (number, unit required), AuditToken (string, no unit) and AuditFlag (boolean, no unit), with tokens and flags compared for exact ordinal equality only. Named `dimensionless` as a canonical unit and applied it to clearness, tau and the sizing factors; ashraeCoefficientA is W/m2. P2 - the Stage B sort key was not total. One space yields a record per design day and load type, so guid+name left ties that would serialize in producer-enumeration order. Sort is now the total key (guid, name, designDayKey, loadType) with null designDayKey last - the same lesson already paid for in PR #20. Also added validation rules for unit/type correctness, a self-checking rule that `alignmentKey` must equal what its declared version's rules produce, a rule making non-total ordering a contract error, and open question 6 on whether name plus loadType is a strong enough key given the absent GUID. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * docs: make the availability discipline consistent across all shapes Four more Codex findings, all real, and all consequences of the previous round's tightening. P1 - the new type rule rejected every unavailable field. Rules 1-2 require `value` to be null when unavailable, but rule 6 then rejected null because it is neither number, string nor boolean - invalidating every unavailable field, including the `peakHour` the contract explicitly instructs the TAS producer to emit as unavailable. Rule 6 now fires only on non-null values, and states that a null is governed by rule 1 alone. P1 - an unavailable HourlySeries was impossible to express. The shape required `basis` to stay `Observed` or `Reconstructed` while rule 2 rejects any non-null basis when unavailable, so the unavailable series that §7 instructs producers to emit could not satisfy the contract. Series `basis` is now nullable on exactly the same "if and only if unavailable" condition as `AuditValue`: an unavailable series keeps its `quantity` and `unit` and carries `basis: null`, `values: null`, so the declaration survives without claiming a basis for something that does not exist. P2 - a DDY run could not be recorded. `origin` may be `Ddy`, but no `sourceFiles` role identified the DDY file, and filing it under `Weather` would conflate two distinct inputs with distinct hashes. Added a `Ddy` role plus new rule 15 requiring it whenever `origin` is `Ddy`. P2 - `absoluteDifference` had no truthful basis. It is producer arithmetic over two `Observed` elevations, which is not `Observed`, not `EngineEchoed`, and not `Reconstructed` (reserved for modelling what the engine would have done from echoed inputs). Added a deliberately narrow `Calculated` basis for plain arithmetic over values already in the same document, explicitly not a softer synonym for `Reconstructed`, and new rule 16 pinning every `Calculated` value to its operands so it can never contradict them and must be unavailable when either operand is. This also covers the parametric summary floated in open question 5. Also corrected the parametric block, which asserted every field carries `EngineEchoed` - true only of available fields. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * docs: specify the root document and make declarations enforceable Five more Codex findings, all real. P1 - the root document structure was never specified. Field-level rules alone would let the TAS and OpenStudio producers satisfy every one of them and still emit structurally incompatible JSON, because nothing said where sourceFiles, assemblies, sizing factors, run provenance, stageA, stageB or auditSchemaVersion live. Added a normative Top-level document subsection under Scope - four root properties plus a full nesting skeleton - placed as an unnumbered subsection to mirror SCHEMA.md and keep representations first, as agreed. Specifying the root exposed a duplication I had introduced: the model-versus-weather elevation comparison sat under each design day's location, though it is a property of the run. It now lives once at provenance.site, and designDay.location.elevation is just that day's own elevation. P1 - representations were declared but never checked against the payload, so a producer could declare ObservedHourly while emitting reconstructed series. New rule 18 binds each token to its evidence in both directions - ProcessedParametric to a parametric block, ObservedHourly to an available Observed series, DerivedHourly to an available Reconstructed series - and rejects an empty or unknown token list. This is what makes "declared, never inferred" enforceable rather than a matter of good intentions. P1 - `key` uniqueness was unstated while duplicate alignmentKey was handled explicitly. Since `key` is the Stage B join target, a duplicate would let one designDayKey resolve to several days, leaving a response attributable to no single set of conditions. New rule 17. P2 - Stage B required a non-null guid although METHODOLOGY.md states shared-gbXML translation may lose SAM GUIDs, so a legitimate shared-gbXML audit could not conform. guid is now string-or-null with null sorting last in any component that permits it, name is required and non-empty, and new rule 19 rejects a record that has neither. P2 - several newly introduced arrays had no declared sort key while §8 required one for every array, and `limitations` was only called "deterministically ordered". Added an Array ordering table giving a total key for all nine arrays, and pinned ordinal to StringComparer.Ordinal so output cannot depend on machine locale. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * docs: separate engine echoes from engine results in the basis taxonomy Two more Codex findings, both real. P1 - the basis taxonomy had no valid state for an engine RESULT. Observed is authored input, EngineEchoed was defined as the engine's echo of what it accepted, Reconstructed is producer modelling, and Calculated is arithmetic - so a TSD design-day zone load had no truthful basis, and rule 7 additionally forbade EngineEchoed on a series. The Stage B hourly series that §7 promises was therefore unexpressible. The same hole affected the Stage B SCALARS, which I had implicitly treated as EngineEchoed although ZoneSizes design loads are results too. Fixed by splitting the engine-sourced basis in two: EngineEchoed is the engine restating an INPUT it accepted (the .eio design-day parameter echo), EngineReported is a RESULT the engine computed (a TSD zone load, a ZoneSizes sizing load). That distinction is the axis this audit turns on - an echo says what the engine was asked to simulate, a report says what it produced - and collapsing them would hide the difference between a design day being mistranslated and a design day being simulated differently. Stage A is largely echoes, Stage B is entirely reports, which is also why the two stages cannot share a status. Series basis is now Observed, Reconstructed or EngineReported, with a table fixing which applies per stage and source, and EngineEchoed still forbidden on a series. Rule 7 additionally requires every available Stage B value and series to be EngineReported. P2 - the Stage B compound sort key was called total but nothing forbade duplicates, so two records sharing (guid, name, designDayKey, loadType) would tie and serialization would fall back to producer enumeration order. New rule 21 requires uniqueness: one space, one design day and one load type yield exactly one record, so a duplicate is an extraction defect. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * docs: allow DDY-shared alignment and enforce the parametric echo basis Three more Codex findings, all real. P1 - the alignment rule made the Ddy origin uncomparable. I had written that a null key is expected for `Ddy`, which forced every design day on an explicitly supported route to be reported one-sided, even though both producers can read the SAME DDY file and its sourceFiles hash proves the bytes are identical. The key now derives from whichever ancestor the two routes share, declared by a new `alignmentKeySource` of SamModel, DdyFile or None, and a null key is reserved for a genuinely untraceable day (`origin: Unknown`). Pairing gained two safety conditions the DDY case needs: the two days' `alignmentKeySource` values must agree, so a SamModel key and a DdyFile key are never interchangeable; and a DdyFile pairing additionally requires the two documents' Ddy sourceFiles hashes to be equal, because ASHRAE station names repeat across weather files and identically named days from different DDY files must not pair. Rule 10 now also cross-checks `alignmentKeySource` against `origin` and against the key's own nullability. P1 - the parametric echo basis was normative prose with no rule behind it. Rule 7's first half constrained only series and its second half only Stage B, so a producer could label a Stage A parametric field Observed, Calculated or even EngineReported and pass validation - defeating the input-echo distinction the contract had just been restructured around. Rule 7 is now three clauses, the third requiring every available parametric value, token and flag to be EngineEchoed. P2 - Stage B uniqueness used the sort key rather than space identity, so two records with the same GUID, design day and load type but different names both passed while double-counting one space. Rule 21 now keys on the EFFECTIVE identity: (guid, designDayKey, loadType) when a GUID exists, falling back to name only when it does not, matching the GUID-first identity rule. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * docs: require a provably shared ancestor before pairing design days Three more Codex findings, all real. P1 - the embedded-model pairing had no equivalent of the DDY hash guard I had just added, so two audits of DIFFERENT SAM models that happen to reuse a conventional design-day name would produce the same alignmentKey, both declare SamModel, and pair. Design-day names are conventional labels - London_TRY ANN HTG 100% CONDS DB is a CIBSE name, not an identifier - so this was likely rather than theoretical. Added provenance.model carrying sourceModelName, sourceModelGuid, canonicalModelHash and canonicalizationVersion, computed exactly as for benchmark-.json, and made every pairing conditional on the ancestor being provably shared: SamModel requires equal canonicalModelHash AND equal canonicalizationVersion (hashes from different canonicalizers are not comparable evidence, per SCHEMA.md), DdyFile requires equal Ddy file hash. A name match alone now never authorises a pair. P2 - Stage B `conditioned` and `setpointState` were bare boolean and string, so they could carry neither `available` nor the required EngineReported basis, and null encoded absence outside the availability discipline. They are now AuditFlag and AuditToken; AuditToken's definition widened to cover a verbatim engine label as well as an enumeration token, which avoids a fourth shape for the same comparison semantics. §4 now states the general rule that no audit field is ever a bare JSON scalar, and new rule 22 enforces it. P2 - one-series-per-quantity was assumed by the "already total" series sort key but never required, so two competing profiles for one quantity would tie and leave byte order dependent on producer enumeration. New rule 21. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 * docs: make collapse loss genuinely one-sided Four more Codex findings, all real; the first corrected a substantive claim. P1 - collapse loss was not one-sided as advertised. ObservedHourly was defined as TAS-only from TBD, so comparing it against EnergyPlus's echo folded any SAM -> gbXML -> TBD translation change into the result and could misattribute it to OpenStudio. Fixed without adding a fourth representation: ObservedHourly now declares `observedFrom`, either SamModel (the authored SAM DesignDay, before any engine route) or EngineStore (the engine's own store, carrying that route's translation). Both producers load the same SAM model, so EITHER can emit a SamModel profile - including the OpenStudio producer alongside its own ProcessedParametric and DerivedHourly, which makes collapse loss a within-document comparison needing no cross-document pairing at all. Collapse loss now requires the SamModel profile. Comparing a TBD profile against the EnergyPlus echo is a distinct third pairing - route translation - and the contract says so rather than letting it pass as collapse loss. New rule 21 enforces `observedFrom`. P2 - the humidity shape could not express two EnergyPlus condition types. Added `J/kg` for Enthalpy and a `humidityConditionScheduleName` token for schedule-based types, with an explicit statement that v1 preserves the echoed schedule identity but does NOT reconstruct schedule-based humidity: the humidity series is unavailable and reconstruction.limitations must record it. Recording the identity and declaring the reconstruction unavailable beats fitting a profile the engine did not use. P2 - the DDY hash precondition was per-document while an audit may draw days from several DDY files, so the comparator could not tell which hash had to match. Each day now names its contributing file in `alignmentKeySourceFile` and only that file's hash is compared; rule 10 validates the reference resolves to a Ddy-role entry. P2 - ProcessedParametric could be declared over an all-unavailable block, advertising evidence the engine never echoed, unlike the hourly representations which require an available series. Rule 18 now requires at least one available value, token or flag. Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Opus 5 --------- Co-authored-by: Claude Opus 5 --- docs/benchmark/DESIGN-DAY-AUDIT.md | 791 +++++++++++++++++++++++++++++ docs/benchmark/GLOSSARY.md | 28 + 2 files changed, 819 insertions(+) create mode 100644 docs/benchmark/DESIGN-DAY-AUDIT.md diff --git a/docs/benchmark/DESIGN-DAY-AUDIT.md b/docs/benchmark/DESIGN-DAY-AUDIT.md new file mode 100644 index 0000000..9d7b287 --- /dev/null +++ b/docs/benchmark/DESIGN-DAY-AUDIT.md @@ -0,0 +1,791 @@ +# Design-day audit contract `design-day-audit-v1` — DRAFT + +**Status: draft for agreement.** Nothing is implemented against this document yet — no schema types, no +producers, no comparator. Per the agreed sequence, the TAS and OpenStudio producer branches are not +created until this contract is settled. Field names, enum tokens and required/optional status are all +open to revision here, and only here; once producers exist, changing them costs a schema version. + +## 1. Scope + +This contract defines `design-day-audit-.json`: a portable, engine-neutral record of **the +design days each engine actually used, and how each engine responded to them**. + +It exists because the benchmark's cross-engine load disagreement is unexplained, and the leading +hypothesis is that the two engines are not sizing from equivalent design days. The recorded runs +already show that the OpenStudio route collapses SAM's 24-hour hourly design-day profiles into a +parametric `SizingPeriod:DesignDay` — humidity to a single dew point, solar to `ASHRAEClearSky` — while +TAS consumes the hourly profile directly. This contract makes that difference measurable instead of +narrated. + +### What it is not + +- **Not a gate.** A design-day audit document carries evidence, not a pass/fail verdict on either + engine. It must never contribute to the benchmark gate defined in + [TOLERANCES.md](TOLERANCES.md). +- **Not part of the benchmark metric contract.** It is a separate document type with its own version + line. It does not extend `benchmark-.json` + ([SCHEMA.md](SCHEMA.md)), and its values use a deliberately distinct type + ([§4](#4-auditvalue-and-hourlyseries)) so an audit value can never be fed to the gating comparator by + accident. +- **Not a re-simulation.** v1 is derivable from artefacts the existing producers already leave behind + (TBD, TSD, `eplusout.eio`, `eplusout.sql`). Adding EnergyPlus `Output:Variable` requests to capture + sizing-period weather is **out of scope for v1** — it would change converter output and require a new + simulation to serve the audit. + +### Top-level document + +`design-day-audit-.json` has four root properties. Field-level rules are not enough on their own +— two producers could satisfy every one of them and still emit structurally incompatible JSON — so the +nesting below is normative: + +| Root property | Type | Requirement | +|---|---|---| +| `auditSchemaVersion` | string | Required semantic version, independent of `schemaVersion` in the benchmark contract; v1 is `1.0.0` | +| `provenance` | object | Required; [§5](#5-provenance) | +| `stageA` | object | Required; `designDays` array plus optional `reconstruction` ([§6](#6-stage-a--conditions)) | +| `stageB` | object | Required; `spaces` array ([§7](#7-stage-b--sizing-response)) | + +```text +{ + "auditSchemaVersion": "1.0.0", + "provenance": { + "sourceFiles": [ { role, identity, hash } ], // §5.1 + "model": { sourceModelName, sourceModelGuid, + canonicalModelHash, canonicalizationVersion }, // §5.1 + "assemblies": [ { name, hash, fileVersion, informationalVersion } ], // §5.2 + "site": { … elevations and pressure … }, // §5.4 + "sizingFactors": { … factors and reportedLoadBasis … }, // §5.5 + "engine": { kind, name, version, sdkVersion }, // §5.6 + "route": …, "samCommit": …, "runnerCommit": …, + "runTimestampUtc": …, "durationSeconds": …, "state": …, + "warnings": [ … ], "notes": [ … ] + }, + "stageA": { + "designDays": [ { + key, alignmentKey, alignmentKeySource, alignmentKeySourceFile, + alignmentKeyVersion, name, sourceDesignDayName, + loadType, dayType, month, dayOfMonth, origin, location, // §5.3 + representations: [ … ], parametric: { … }, series: [ … ] // §6 + } ], + "reconstruction": { algorithm, algorithmVersion, inputs, limitations } // §6 + }, + "stageB": { + "spaces": [ { guid, name, designDayKey, loadType, …, series: [ … ] } ] // §7 + } +} +``` + +`stageA` and `stageB` are siblings and never nest inside one another. Unknown root or object properties +do not change the meaning of known ones, and a writer must not emit non-finite JSON numbers. + +## 2. The three representations + +Every quantity in a design-day audit belongs to exactly one of three representations. These are +declared, never inferred, and they are the core of the contract: a comparison that does not say which +representation each side contributed is not interpretable. + +| Representation | Meaning | Who can supply it | Source | +|---|---|---|---| +| `ObservedHourly` | A 24-hour series as authored, with nothing fitted or modelled. | **Either producer** from the SAM model; TAS additionally from its own store | SAM `DesignDay` (which derives from `Weather.WeatherDay`), or TBD design days via `SAM.Analytical.Tas.Query.DesignDays` | +| `ProcessedParametric` | The parametric design-day definition the engine **accepted and echoed back**, after its own input processing. Not a series. | EnergyPlus | `eplusout.eio` — `Environment:Design Day Data` and `Environment:Design Day Misc` | +| `DerivedHourly` | A 24-hour series **reconstructed** from a `ProcessedParametric` definition using the engine's documented design-day algorithms. | EnergyPlus | computed offline from the `.eio` parameters | + +### The wording rule is normative + +A `DerivedHourly` series was **not reported by the engine**. Any producer output, report, commit +message or README describing one must say it was *reconstructed from the engine's processed +parameters using the documented design-day algorithms*. Writing that EnergyPlus "reported" an hourly +sizing-weather series would be false: the recorded runs prove the SQL contains no sizing-period +outdoor conditions, because the producer never requests them. + +The corresponding name for the whole exercise is an **"EnergyPlus processed design-day parameter audit +with reconstructed hourly conditions"** — not an "hourly weather comparison". + +### An `ObservedHourly` series must declare where it was read + +`observedFrom` is required on every series of `basis: Observed`, and it decides which comparisons the +series can support: + +| `observedFrom` | Read from | Carries translation? | +|---|---|---| +| `SamModel` | the SAM `DesignDay` in the loaded model | **No** — this is the authored profile, before any engine route | +| `EngineStore` | the engine's own store, e.g. TBD design days | **Yes** — it has been through that route's translation (for TAS: SAM → gbXML → TBD) | + +This distinction matters because both producers load the same SAM model, so **either can emit a +`SamModel` observed profile** — including the OpenStudio producer, alongside its own +`ProcessedParametric` and `DerivedHourly`. + +### Legitimate pairings + +| Comparison | Sides | What it establishes | +|---|---|---| +| **Collapse loss** | a `SamModel` `ObservedHourly` → its own parametric summary, against `ProcessedParametric` **in the same document** | What the hourly → parametric translation discarded, genuinely on the OpenStudio side alone. Needs no reconstruction and no cross-document pairing, and is therefore the strongest evidence available. | +| **Instantiated conditions** | `ObservedHourly` vs `DerivedHourly`, across documents | Whether the two engines sized from equivalent 24-hour conditions. Weaker: one side is reconstructed. | +| **Route translation** | a `SamModel` `ObservedHourly` vs an `EngineStore` `ObservedHourly` | What a route's own translation changed before the engine saw it — for TAS, what SAM → gbXML → TBD did. | + +**A TAS `EngineStore` profile is not a substitute for the authored one.** Comparing a TBD profile against +EnergyPlus's echo folds any gbXML/TBD translation change into the result and would misattribute it to +OpenStudio. That pairing is a **cross-route** comparison and must be labelled as such — it is the third +row above, not collapse loss. Collapse loss requires the `SamModel` profile. + +A `ProcessedParametric` definition must never be compared hour-by-hour against an `ObservedHourly` +series — they are not the same kind of object. Reconstruct first, and label the result. + +## 3. Stage A and Stage B are separate + +The document has two independent top-level sections, and they must stay separate through extraction, +serialization, comparison and reporting: + +| Stage | Question | Content | +|---|---|---| +| **Stage A — conditions** | What weather did each engine size from? | Design-day identity, location, parametric definition, hourly series | +| **Stage B — sizing response** | What did each engine do with it? | Per-space peak sizing load, peak time, zone and outdoor conditions at peak, conditioned classification | + +Rules: + +- Stage A and Stage B are **never merged into one comparison object** and never share a status. +- A Stage B difference is **not interpretable without its Stage A pairing** — if the conditions differ, + a differing response is expected, not a finding. A report that shows Stage B without the + corresponding Stage A must say so explicitly. +- Stage A carries no space-level data; Stage B carries no weather series. Each Stage B space references + the design day that drove it by `designDayKey` ([§7](#7-stage-b--sizing-response)). + +## 4. `AuditValue` and `HourlySeries` + +`AuditValue` is the audit counterpart of `MetricValue` ([SCHEMA.md](SCHEMA.md#metricvalue)) — the same +availability discipline, plus a mandatory basis: + +| Field | Type | Requirement | +|---|---|---| +| `value` | number or null | Finite; `null` if and only if `available` is `false` | +| `unit` | string | Canonical unit token, always present, never inferred from the field name | +| `available` | boolean | `true` only when the source genuinely supplied it; a measured zero is available | +| `basis` | string or null | `Observed`, `EngineEchoed`, `EngineReported`, `Reconstructed` or `Calculated`; `null` if and only if `available` is `false` | + +`basis` records **how the number came to exist**, independently of the representation it sits in: + +| `basis` | Meaning | +|---|---| +| `Observed` | Read from the model or an input file as authored. No engine processing, no fitting. | +| `EngineEchoed` | The engine restating an **input** it accepted, after its own input processing — the `.eio` design-day parameter echo. | +| `EngineReported` | A **result** the engine computed and reported — a TSD design-day zone load, a `ZoneSizes` sizing load. | +| `Reconstructed` | Modelled by the producer from `EngineEchoed` inputs using a documented **engine** algorithm — what the engine would have done. Never the engine's own output. | +| `Calculated` | Plain arithmetic over other values **in the same document**, with no modelling assumption — for example an absolute difference between two `Observed` elevations. | + +`EngineEchoed` and `EngineReported` are separated deliberately, and the distinction is the axis this whole +audit turns on: an echo tells you **what the engine was asked to simulate**, a report tells you **what it +produced**. Stage A is largely echoes and Stage B is entirely reports, which is why the two stages cannot +share a status. Collapsing them into one "came from the engine" basis would hide the difference between a +design day being mistranslated and a design day being simulated differently. + +`Calculated` is deliberately narrow, and is not a softer synonym for `Reconstructed`. A `Reconstructed` +value substitutes for something the engine never reported and can therefore be wrong about the engine; +a `Calculated` value is derived from values already present and can only be wrong about arithmetic. Every +`Calculated` field in this contract carries a validation rule fixing it to its operands +([§9](#9-validation-rules)), so it can never contradict them. + +A separate type from `MetricValue` is deliberate: it keeps audit evidence structurally incapable of +entering the gating comparator, and it forces the basis to be stated on every number rather than +implied by which file it came from. + +`AuditValue` holds numbers only. Two sibling shapes carry non-numeric evidence with the same +`available`/`basis` discipline but **no `unit`** — `AuditToken` (a string, whether an enumeration token or +a verbatim engine label) and `AuditFlag` (a boolean indicator). Their field layout is set out in +[§6](#6-stage-a--conditions); both stages use them, and both are compared for exact ordinal equality only, +never banded. **No audit field is ever a bare JSON string, number or boolean** — every value in the +document carries its own `available` and `basis`, so absence and provenance are never left implicit. + +`HourlySeries` carries a 24-hour profile. The basis is declared once for the series, because a +reconstruction is uniformly reconstructed — per-hour bases would suggest a mixture the algorithms +cannot produce: + +| Field | Type | Requirement | +|---|---|---| +| `quantity` | string | Quantity token for the series' own stage — [§6](#6-stage-a--conditions) for Stage A, [§7](#stage-b-quantity-tokens) for Stage B | +| `unit` | string | Canonical unit token; present even when the series is unavailable, because the quantity is still declared | +| `basis` | string or null | `Observed`, `Reconstructed` or `EngineReported`; **`null` if and only if `available` is `false`**. Never `EngineEchoed` — an echo is an input parameter, not a profile | +| `observedFrom` | string or null | `SamModel` or `EngineStore`; required when `basis` is `Observed`, `null` otherwise ([§2](#an-observedhourly-series-must-declare-where-it-was-read)) | +| `available` | boolean | `false` when the source cannot supply this quantity at all | +| `values` | array or null | Exactly 24 finite numbers, hour 0 to hour 23 local standard time; `null` if and only if `available` is `false` | + +An unavailable series therefore carries its `quantity` and `unit` with `basis: null` and `values: null` — +the declaration survives, and nothing claims a basis for a series that does not exist. This is the same +availability discipline as `AuditValue`, applied at series granularity. + +Which basis a series carries follows from its stage: + +| Series | Basis | +|---|---| +| Stage A, outdoor conditions read from the SAM model | `Observed`, `observedFrom: SamModel` | +| Stage A, TAS outdoor conditions from TBD | `Observed`, `observedFrom: EngineStore` | +| Stage A, EnergyPlus outdoor conditions reconstructed from `.eio` | `Reconstructed` | +| Stage B, TAS zone response from TSD | `EngineReported` | + +Partial series are invalid: 24 values or `null`. A quantity the source does not carry is +`available: false`, never zero-filled — a zero-filled solar profile is indistinguishable from a real +`clearness 0.0` heating day, which is exactly the defect this audit is meant to expose. + +## 5. Provenance + +Design-day provenance is stricter than benchmark provenance, because the recorded runs showed +`benchmark-.json` asserting a `samCommit` whose code was demonstrably not in the loaded +binaries. Every field below is required unless marked otherwise. + +### 5.1 Source files and hashes + +`sourceFiles` is an array, sorted by `role` then `identity`, of every file that contributed evidence: + +| Field | Type | Meaning | +|---|---|---| +| `role` | string | `SourceModel`, `Weather`, `Ddy`, `Tbd`, `Tsd`, `Eio`, `Sql`, `Idf`, `Osm` | +| `identity` | string | Portable identity — file name or station label, **never an absolute path** | +| `hash` | string | `sha256:` plus 64 lowercase hexadecimal characters, over the exact bytes read | + +`Ddy` is a distinct role from `Weather`: a DDY file supplies design days while the EPW supplies the +annual year, they are separate files with separate hashes, and a run whose `origin` is `Ddy` must record +it under its own role rather than conflating the two inputs. + +The model and weather entries must agree with the corresponding `benchmark-.json` hashes when +the two documents describe the same run. + +`provenance.model` additionally carries the neutral model identity, so pairing can establish that two +documents describe the **same model** and not merely the same file name: + +| Field | Type | Meaning | +|---|---|---| +| `sourceModelName` | string | Portable model label; not a filesystem path | +| `sourceModelGuid` | string or null | SAM model GUID, 32 lowercase hexadecimal characters | +| `canonicalModelHash` | string | `sha256:` of the canonical SAM representation | +| `canonicalizationVersion` | string | Semantic version of the canonicalization rules used | + +These are the same two-hash semantics as the benchmark contract +([SCHEMA.md](SCHEMA.md#canonical-model-hashing)): the `SourceModel` file hash proves byte equality of the +input, while `canonicalModelHash` proves the two runs describe the same model ignoring non-semantic +formatting. A producer computes them exactly as it does for `benchmark-.json`, so a paired audit +and benchmark document for one run carry identical values. + +### 5.2 Loaded assembly hashes + +`assemblies` is an array, sorted by `name`, recording **what actually executed** rather than a claim +about a repository: + +| Field | Type | Meaning | +|---|---|---| +| `name` | string | Assembly simple name, e.g. `SAM.Analytical` | +| `hash` | string | `sha256:` of the loaded assembly file bytes | +| `fileVersion` | string or null | File version; `1.0.0.0` for an unstamped local build | +| `informationalVersion` | string or null | Informational version when present | + +This closes a known weakness. `samCommit` and `runnerCommit` are operator-supplied strings that no +producer can verify, and local SAM assemblies are unstamped `1.0.0.0`, so a commit field can name code +that is not in the binary. They may still be recorded for continuity, but **`assemblies` is the +authoritative record**, and a consumer comparing two audit documents must compare assembly hashes, not +commit strings. + +### 5.3 Design-day identity and location + +Per design day, in Stage A ([§6](#6-stage-a--conditions)) — listed here because it is provenance, not +measurement: + +| Field | Type | Meaning | +|---|---|---| +| `key` | string | Stable **within-document** key; the join target for Stage B. Not comparable across documents | +| `alignmentKey` | string or null | **Cross-document** pairing key; see [§5.3.1](#531-cross-document-alignment) | +| `alignmentKeySource` | string | `SamModel`, `DdyFile` or `None` — which common ancestor the key was derived from | +| `alignmentKeySourceFile` | string or null | The `identity` of the `sourceFiles` entry that contributed the ancestor name; required when `alignmentKeySource` is `DdyFile`, `null` otherwise | +| `alignmentKeyVersion` | string | Semantic version of the derivation rules below; required whenever `alignmentKey` is non-null | +| `name` | string | Verbatim engine/model name, unmodified — e.g. `London_TRY ANN HTG 100% CONDS DB` | +| `sourceDesignDayName` | string or null | The common-ancestor design-day name the key was derived from; `null` when the day cannot be traced to one | +| `loadType` | string | `Heating` or `Cooling` | +| `dayType` | string or null | Engine day-type token, e.g. `WinterDesignDay` | +| `month`, `dayOfMonth` | number or null | Calendar position when declared | +| `origin` | string | `EmbeddedModel`, `Ddy` or `Unknown` — where the day came from | +| `location` | object | See below | + +`location` is recorded per design day, not once per document, because a design day can carry a +location unrelated to the annual weather — the recorded HungaryHouse runs use **London** design days +with **Budapest** annual weather, and a document that stored one shared site would hide exactly that: + +| Field | Type | Meaning | +|---|---|---| +| `name` | string or null | Location label as carried by the design day | +| `latitude`, `longitude` | number or null | Degrees; positive north and east | +| `timeZone` | number or null | Hours offset from UTC | +| `elevation` | `AuditValue` | The elevation carried by **this design day**, `m` | + +The model-versus-weather-file elevation comparison is a property of the run, not of one design day, so it +lives once at `provenance.site` ([§5.4](#54-site-elevations-and-pressure)) rather than being repeated on +every day. + +#### 5.3.1 Cross-document alignment + +`key` is stable only within one document, and `name` is deliberately the **unmodified engine label** — so +neither can pair a TAS day with an EnergyPlus day. Without a defined cross-document identity, both +Stage A comparisons and the Stage B interpretation that rests on them would be ambiguous. `alignmentKey` +supplies it. + +**The key is derived from whatever the two routes share.** Neither SAM `DesignDay` (which derives from +`Weather.WeatherDay`) nor a DDY design day carries a GUID, so the key is composed from stable attributes +rather than an identifier: + +```text +alignmentKey = "|" +``` + +where `LoadType` is `Heating` or `Cooling`, and `NormalisedSourceName` is `sourceDesignDayName`: + +1. trimmed of leading and trailing white space; +2. every internal run of white space collapsed to one space (U+0020); +3. upper-cased with `ToUpperInvariant`. + +`alignmentKeySource` declares **which common ancestor** the name came from, so the basis of a pairing is +never guessed: + +| `origin` | `alignmentKeySource` | `sourceDesignDayName` is | +|---|---|---| +| `EmbeddedModel` | `SamModel` | the SAM source design-day name | +| `Ddy` | `DdyFile` | the design-day name as carried in the DDY file | +| `Unknown` | `None` | `null`, and `alignmentKey` is `null` | + +A DDY-sourced key is legitimate precisely because both producers can read the **same DDY file**, and the +`Ddy` `sourceFiles` hash proves it is the same bytes. Excluding the `Ddy` origin would make a route the +contract explicitly supports impossible to compare. + +Producers must never derive the key from a name the engine assigned or rewrote. `alignmentKeyVersion` is +`1.0.0` for these rules; a changed normalisation requires a new version, so an algorithm change can never +masquerade as a changed design day — the same discipline `canonicalizationVersion` applies to model +hashes. + +Pairing rules, all normative: + +- The comparator pairs **only** on exactly equal `alignmentKey`. There is no fuzzy, positional or + nearest-name fallback. +- Two days pair only when their `alignmentKeySource` values also agree. A `SamModel` key and a `DdyFile` + key are not interchangeable even if the names coincide. +- **Every pairing requires the shared ancestor itself to be provably shared**, because design-day names + are conventional and repeat freely across models and weather files — `London_TRY ANN HTG 100% CONDS DB` + is a CIBSE label, not a unique identifier. So the name match alone never authorises a pair: + + | `alignmentKeySource` | Additional precondition on the two documents | + |---|---| + | `SamModel` | equal `provenance.model.canonicalModelHash`, and equal `canonicalizationVersion` — hashes from different canonicalizers are not comparable evidence | + | `DdyFile` | equal hash on the **specific** `sourceFiles` entry each day names in `alignmentKeySourceFile` | + + The `DdyFile` condition is per-day, not per-document, because one audit may draw design days from more + than one DDY file. Comparing whole `Ddy` hash *sets* would both pair unrelated days when any one hash + matched and reject valid pairs when the sets merely differed, so each day names the file it came from and + only that file's hash is compared. + + Without these, two audits of *different* models or *different* DDY files would pair unrelated days and + every Stage A and Stage B comparison built on them would be invalid. +- `alignmentKey` is `null` only when the day has no traceable common ancestor, i.e. `origin` of + `Unknown`. A null-keyed day is **not pairable**: it is reported one-sided and never guessed into a pair. +- If two days in one document share an `alignmentKey`, **every** member of that group is excluded from + pairing and reported as ambiguous. This mirrors the duplicate-GUID rule for space alignment in + [METHODOLOGY.md](METHODOLOGY.md#space-identity-and-alignment) — first-match would silently pair the + wrong day. +- An unpaired or ambiguous day is a **coverage** observation, never a numerical difference. + +### 5.4 Site elevations and pressure + +`provenance.site`, recorded once per document. Elevation is first-class, because site elevation feeds the +design-day standard barometric pressure, and the recorded runs carry an EnergyPlus warning of +`Elevation difference=[416.00] percent, [104.00] meters` between model and weather file: + +| Field | Type | Meaning | +|---|---|---| +| `modelSiteElevation` | `AuditValue` | Elevation carried by the SAM model, `m` | +| `weatherFileElevation` | `AuditValue` | Elevation carried by the weather file, `m` | +| `absoluteDifference` | `AuditValue` | Absolute difference, `m`; `basis: Calculated`, and `available: false` whenever either operand is unavailable | +| `engineElevationUsed` | `AuditValue` | The elevation the engine states it used, `m` | +| `elevationAuthority` | string | `Model`, `WeatherFile`, `TranslatedSite` or `Unknown` | +| `barometricPressureUsed` | `AuditValue` | Pressure the engine states it used, `Pa` | +| `pressureBasis` | string | `ModelProfile`, `ElevationStandard`, `EngineDefault` or `Unknown` | + +`elevationAuthority` **may be `Unknown`, and `Unknown` is the correct v1 answer** until it is +established which site is authoritative for each engine. The contract requires the question to be +recorded, not answered. No elevation discrepancy is to be "fixed" before this audit says which side +each engine honoured. + +### 5.5 Sizing factors + +This block exists to settle an open question the benchmark cannot currently answer — whether a +reported sizing load is pre- or post-sizing-factor. The recorded runs show EnergyPlus applying +`Sizing:Parameters` of 1.25 heating and 1.15 cooling, while TBD's treatment of `maxHeatingLoad` is +unknown: + +| Field | Type | Meaning | +|---|---|---| +| `heatingSizingFactor` | `AuditValue` | Factor the engine applied, unit `dimensionless` | +| `coolingSizingFactor` | `AuditValue` | Factor the engine applied, unit `dimensionless` | +| `factorSource` | string | `EngineEcho`, `ModelSetting` or `Unknown` | +| `reportedLoadBasis` | string | `PreSizingFactor`, `PostSizingFactor` or `Unknown` | + +`reportedLoadBasis` is **required and may be `Unknown`**. A producer that cannot establish whether its +own reported load is pre- or post-factor must say `Unknown` rather than guess. This is the field that +makes the question answerable: it is settled per engine by comparing a reported load against that +engine's own unfactored result, **not** by comparing the two engines with each other — cross-engine +ratios cannot separate a sizing margin from a physical difference when the two engines are not +evaluating equivalent conditions. + +### 5.6 Run provenance + +`engine` (kind/name/version/sdkVersion), `route`, `runTimestampUtc`, `durationSeconds`, `state`, +`warnings` and `notes` follow [SCHEMA.md](SCHEMA.md#provenance) unchanged. `samCommit` and +`runnerCommit` are optional here and, if present, are subordinate to `assemblies` +([§5.2](#52-loaded-assembly-hashes)). + +## 6. Stage A — conditions + +`stageA.designDays` is an array sorted by `key`. Each entry carries its identity and location +([§5.3](#53-design-day-identity-and-location)), a declared `representations` array, and whichever of +the two payloads it can supply. + +`parametric` is present when the entry offers `ProcessedParametric`. Every **available** field carries +`basis: EngineEchoed`, since the whole block is the engine's own echo; an unavailable field carries +`basis: null` like any other. **Not every field is numeric**, so the block uses three shapes, all sharing +the `available`/`basis` discipline of [§4](#4-auditvalue-and-hourlyseries) and differing only in the type +of `value`: + +| Shape | `value` type | `unit` | Used for | +|---|---|---|---| +| `AuditValue` | number or null | required | measured quantities | +| `AuditToken` | string or null | **absent** | engine string values — an enumeration token, or a verbatim engine label such as a setpoint state | +| `AuditFlag` | boolean or null | **absent** | engine indicator flags | + +`AuditToken` and `AuditFlag` carry no `unit`, because a token has no unit and a fabricated one would +invite meaningless comparison. Their values are compared for **exact ordinal equality** only, never +banded. + +Numeric fields (`AuditValue`): + +| Field | Unit | Notes | +|---|---|---| +| `maximumDryBulb` | `degC` | | +| `dailyDryBulbRange` | `deltaC` | A range of `0.00` means constant temperature for 24 hours | +| `humidityConditionValue` | `degC`, `kgWater/kgDryAir` or `J/kg` | Unit follows `humidityConditionType` — `degC` for wet-bulb and dew-point forms, `kgWater/kgDryAir` for a humidity ratio, `J/kg` for `Enthalpy`. Unavailable for schedule-based types, which carry no single value | +| `barometricPressure` | `Pa` | | +| `windSpeed` | `m/s` | | +| `windDirection` | `deg` | | +| `skyClearness` | `dimensionless` | A clearness of `0.00` means zero solar for 24 hours | +| `ashraeTauB`, `ashraeTauD` | `dimensionless` | Optional; present for `ASHRAETau` models | +| `ashraeCoefficientA` | `W/m2` | Optional; apparent solar irradiation at air mass zero | +| `ashraeCoefficientB`, `ashraeCoefficientC` | `dimensionless` | Optional; echoed extinction and diffuse factors | + +Token fields (`AuditToken`): + +| Field | Example | +|---|---| +| `dryBulbRangeModifierType` | `DefaultMultipliers` | +| `humidityConditionType` | `Dewpoint` | +| `humidityConditionScheduleName` | the echoed schedule identity, for schedule-based humidity types | +| `solarModel` | `ASHRAEClearSky` | + +`humidityConditionScheduleName` is available only for schedule-based humidity types +(`RelativeHumiditySchedule`, `WetBulbProfileMultiplierSchedule` and similar). **v1 preserves the echoed +schedule identity but does not reconstruct schedule-based humidity**: the humidity series in a +`DerivedHourly` entry is then `available: false`, and `stageA.reconstruction.limitations` must record that +the humidity profile was not reconstructed for that design day. The parametric echo is never discarded — +recording the identity and declaring the reconstruction unavailable is preferable to fitting a profile the +engine did not use. + +Flag fields (`AuditFlag`), all optional: `rainIndicator`, `snowIndicator`, `daylightSavingIndicator`. + +`dimensionless` is a canonical unit token in this contract, used where a quantity is a genuine ratio. It +is never omitted and never left empty — an absent unit on an `AuditValue` is a validation error +([§9](#9-validation-rules)). + +`series` is present when the entry offers `ObservedHourly` or `DerivedHourly`: an array of +`HourlySeries` ([§4](#4-auditvalue-and-hourlyseries)), sorted by `quantity`, drawn from these quantity +tokens: + +| Quantity | Unit | +|---|---| +| `dryBulbTemperature` | `degC` | +| `relativeHumidity` | `percent` | +| `dewPointTemperature` | `degC` | +| `humidityRatio` | `kgWater/kgDryAir` | +| `globalHorizontalRadiation` | `W/m2` | +| `diffuseHorizontalRadiation` | `W/m2` | +| `directNormalRadiation` | `W/m2` | +| `windSpeed` | `m/s` | +| `windDirection` | `deg` | +| `cloudCover` | `tenths` | +| `barometricPressure` | `Pa` | + +An entry may offer both `ProcessedParametric` and `DerivedHourly` — that is the expected EnergyPlus +shape, and the pair is what makes the reconstruction auditable. + +### Reconstruction declaration + +Any document containing a `Reconstructed` series must carry `stageA.reconstruction`: + +| Field | Type | Meaning | +|---|---|---| +| `algorithm` | string | Named algorithm, e.g. `EnergyPlus-DefaultMultipliers-ASHRAEClearSky` | +| `algorithmVersion` | string | Semantic version of the producer's implementation | +| `inputs` | array of strings | The `parametric` field names consumed | +| `limitations` | array of strings | Known fidelity limits, deterministically ordered | + +A reconstruction with no declared algorithm is invalid. The declaration is what lets a later reader +decide whether a difference is physics or reconstruction error. + +## 7. Stage B — sizing response + +Space identity and alignment follow +[METHODOLOGY.md](METHODOLOGY.md#space-identity-and-alignment) — GUID first, name only as a declared +fallback. + +`stageB.spaces` is an array sorted by the **total** key `(guid, name, designDayKey, loadType)`, every +component ordinal, with `null` sorting **last** in any component that permits it. One space produces a +separate record per design day and load type, so GUID and name alone leave ties, and tied records would +serialize in producer-enumeration order — which is not deterministic and would break artefact diffing. +The lesson is already paid for in this programme: **never claim deterministic ordering from a non-total +comparator**. + +| Field | Type | Unit | Meaning | +|---|---|---|---| +| `guid` | string or null | — | SAM space GUID; `null` when the route could not preserve it, which shared-gbXML translation may legitimately do | +| `name` | string | — | Space name; required and non-empty, and the only identity left when `guid` is `null` | +| `designDayKey` | string or null | — | The Stage A day that drove this result; `null` when the engine does not attribute it | +| `loadType` | string | — | `Heating` or `Cooling` | +| `designLoad` | `AuditValue` | `W` | Sizing load, on the basis declared by `reportedLoadBasis` | +| `unfactoredDesignLoad` | `AuditValue` | `W` | Optional; the same load before any sizing factor, when the engine exposes both | +| `designFlow` | `AuditValue` | `m3/s` | Optional | +| `peakHour` | `AuditValue` | `hourOfDay` | `0..23` — a design-day hour, **not** an hour of year. Unavailable on the TAS side until the `GetPeakZoneGains` index convention is pinned (see below) | +| `peakMinute` | `AuditValue` | `min` | Optional; EnergyPlus reports sub-hourly peak times | +| `zoneTemperatureAtPeak` | `AuditValue` | `degC` | | +| `outdoorTemperatureAtPeak` | `AuditValue` | `degC` | | +| `zoneHumidityRatioAtPeak` | `AuditValue` | `kgWater/kgDryAir` | Optional | +| `conditioned` | `AuditFlag` | — | Engine's classification | +| `setpointState` | `AuditToken` | — | Engine's setpoint/thermostat state at peak, verbatim | +| `series` | array or null | — | Optional `HourlySeries` per space, drawn from the Stage B quantity tokens below | + +`peakHour` is an hour **of the design day**, deliberately a different unit token from the benchmark's +`hourOfYear`, so the two can never be silently compared. + +Every available Stage B value carries `basis: EngineReported` — Stage B is entirely engine **results**, +not echoed inputs. `EngineEchoed` is never valid here, and `Reconstructed` would mean the producer had +modelled the engine's response rather than read it, which v1 does not do. + +### Stage B quantity tokens + +`HourlySeries.quantity` is drawn from a **stage-specific** token list. Stage A quantities describe +outdoor conditions ([§6](#6-stage-a--conditions)); Stage B quantities describe the zone response, and a +Stage A series may never use a Stage B token or the reverse: + +| Quantity | Unit | +|---|---| +| `zoneSensibleLoad` | `W` | +| `zoneLatentLoad` | `W` | +| `zoneTemperature` | `degC` | +| `zoneHumidityRatio` | `kgWater/kgDryAir` | +| `zoneSetpointTemperature` | `degC` | + +`zoneLatentLoad`, `zoneHumidityRatio` and `zoneSetpointTemperature` are optional; a producer that cannot +supply one emits `available: false` rather than omitting the entry silently or zero-filling it. + +### The two sides are asymmetric, and that is recorded not hidden + +| Side | Scalars | 24-hour series | +|---|---|---| +| TAS | from TSD `HeatingDesignData` / `CoolingDesignData` | **available** — TSD carries hourly design-day data | +| EnergyPlus | from SQL `ZoneSizes` and `.eio` `` | **not available in v1** — would need added `Output:Variable` requests and a re-run | + +So Stage B in v1 compares scalars cross-engine, and carries hourly series on the TAS side only. A +report must not present a TAS 24-hour profile beside a reconstructed outdoor series as though both were +engine responses. + +### What the TAS side already provides, and what it does not + +`SAM.Analytical.Tas.Query.DesignDataDictionary` already yields, per zone GUID, the peak load, an index, +and the owning `HeatingDesignData` / `CoolingDesignData` — via `MaxValueDictionary` over +`GetPeakZoneGains`. So the Stage B **scalars are available with no new TAS traversal**, and +`designDayKey` is attributable from the owning design-data object. + +Three constraints follow, and the producer must respect all three: + +1. **`MaxValueDictionary` collapses the per-design-day dimension**: it keeps only the maximum across all + design days per zone. The audit needs a value *per design day*, so the existing helper cannot be + reused as-is for Stage B — iterate the design-data collections directly and keep each day. +2. **The index returned by `GetPeakZoneGains` has an unverified base and span.** Nothing in the + repository documents whether it is 0- or 1-based, or whether it indexes hours of the design day. + `peakHour` must not be populated from it until that is pinned against a known case; until then it is + `available: false`, and the raw index is carried as a producer diagnostic. +3. **`SAM_Tas/.../Query/YearlyValues.cs` must not be reused** for the 24-hour series: it always + allocates 8760 values and loops 365 days via `GetDailyZoneResult` even when the receiver is + `HeatingDesignData`. A dedicated 24-hour extractor is required. + +### Values carried verbatim, unmapped + +`ZoneSizes.PeakTemp` is carried as `outdoorTemperatureAtPeak` **only if** the audit establishes it is an +outdoor value. Until then it is carried verbatim under a producer-diagnostic field and mapped to +nothing: EnergyPlus documents the zone sizing peak temperature as a *zone* value +(`ZoneTempAtHeatPeak`), yet every row of the recorded run carried a value bit-identical to the design +day's *outdoor* maximum dry bulb. Settling that is a purpose of this audit, not an assumption of it. + +## 8. Determinism and serialization + +Identical to the benchmark contract, and required for the same reason — a recorded artefact must be +diffable: + +- System.Text.Json, invariant culture, deterministic property order, deterministic array order. +- Full finite `double` precision; no rounding of recorded values. +- No non-finite JSON numbers. +- LF endings, UTF-8 without BOM. +- Committed artefacts are LF-pinned under `docs/benchmark/reports/**` in `.gitattributes`. + +### Array ordering + +No array order is incidental. Every array in the contract has a **total** sort key, so two conforming +producers given equivalent evidence emit byte-identical output: + +| Array | Total sort key | +|---|---| +| `provenance.sourceFiles` | `(role, identity, hash)`, ordinal | +| `provenance.assemblies` | `(name, hash)`, ordinal | +| `provenance.warnings`, `provenance.notes` | ordinal string comparison | +| `stageA.designDays` | `key`, ordinal — unique, so already total | +| `designDay.representations` | ordinal string comparison of the representation tokens | +| `designDay.series`, `space.series` | `quantity`, ordinal — one series per quantity, so already total | +| `stageA.reconstruction.inputs` | ordinal string comparison | +| `stageA.reconstruction.limitations` | ordinal string comparison | +| `stageB.spaces` | `(guid, name, designDayKey, loadType)`, ordinal, `null` last | + +Ordinal means `StringComparer.Ordinal` — not culture-aware comparison, which would make output depend on +the machine's locale. Where a key is described as "already total", that is because the contract forbids +duplicates on it; a producer must not rely on that without enforcing the corresponding validation rule. + +## 9. Validation rules + +A document is invalid if any of the following holds: + +1. `available` is `true` and `value` is `null`, or `available` is `false` and `value` is non-null. +2. `available` is `true` and `basis` is `null`, or `available` is `false` and `basis` is non-null. +3. A `HourlySeries` is `available: true` with `null` `values`, or `available: false` with non-null + `values`, or has `values` whose length is not exactly 24. +4. A `HourlySeries` `quantity` token is absent, or is not in the token list **for its own stage** — + Stage A quantities in Stage A, Stage B quantities in Stage B, never crossed. +5. An `AuditValue` or `HourlySeries` has no `unit`, or its `unit` is not a token named in this document. + An `AuditToken` or `AuditFlag` carries a `unit` at all. +6. **A non-null** `AuditToken` value is not a string, or a non-null `AuditFlag` value is not a boolean, + or a non-null `AuditValue` value is not a finite number. A `null` value is governed by rule 1 alone, + so an unavailable field of any shape is valid. +7. A basis contradicts the block it sits in: + - a non-null series `basis` is anything other than `Observed`, `Reconstructed` or `EngineReported` — + `EngineEchoed` is never valid on a series; + - any available `stageB` value or series carries a basis other than `EngineReported`; + - any available `parametric` value, token or flag carries a basis other than `EngineEchoed`. The + block is by definition the engine's echo of its accepted inputs, so `Observed`, `EngineReported`, + `Reconstructed` and `Calculated` are all invalid there. +8. A `Reconstructed` series exists without `stageA.reconstruction`. +9. A `stageB` record names a `designDayKey` absent from `stageA.designDays`. +10. The alignment key is internally inconsistent: + - `alignmentKey` is non-null and `alignmentKeyVersion` is absent, or `sourceDesignDayName` is `null`; + - `alignmentKey` does not equal the value the declared `alignmentKeyVersion` rules produce from + `loadType` and `sourceDesignDayName`; + - `alignmentKeySource` is absent, or disagrees with `origin` — `EmbeddedModel` requires `SamModel`, + `Ddy` requires `DdyFile`, `Unknown` requires `None`; + - `alignmentKeySource` is `None` while `alignmentKey` is non-null, or is `SamModel`/`DdyFile` while + `alignmentKey` is `null`; + - `alignmentKeySource` is `DdyFile` and `alignmentKeySourceFile` is absent, names no `sourceFiles` + entry, or names one whose `role` is not `Ddy`; or `alignmentKeySource` is not `DdyFile` and + `alignmentKeySourceFile` is non-null. +11. `reportedLoadBasis`, `pressureBasis`, `elevationAuthority` or `factorSource` is missing. `Unknown` is + a valid value; omission is not. +12. A hash is not `sha256:` plus 64 lowercase hexadecimal characters. +13. An array is not ordered by the total sort key this document declares for it. +14. `stageA` and `stageB` content is interleaved in one object. +15. `origin` is `Ddy` and no `sourceFiles` entry carries the `Ddy` role. +16. A `Calculated` value does not equal the arithmetic its definition states over its operands, or is + `available: true` while any operand is `available: false`. For `site.absoluteDifference` the operands + are `modelSiteElevation` and `weatherFileElevation`, and the arithmetic is the absolute difference. +17. Two entries in `stageA.designDays` share a `key`. Unlike `alignmentKey`, `key` is the Stage B join + target, so a duplicate would let one `designDayKey` resolve to several days and leave a response + attributable to no single set of conditions. +18. `representations` disagrees with the payload actually emitted: + - `ProcessedParametric` is declared without a `parametric` block, or with a block in which **no** + value, token or flag is available — an all-unavailable block advertises evidence the engine never + echoed — or a `parametric` block exists without the declaration; + - `ObservedHourly` is declared without at least one available series of `basis: Observed`, or such a + series exists without the declaration; + - `DerivedHourly` is declared without at least one available series of `basis: Reconstructed`, or + such a series exists without the declaration; + - `representations` is empty, or names a token other than these three. +19. A `stageB` record has a `null` `guid` **and** a missing or empty `name`, leaving it unidentifiable. +20. A root property other than `auditSchemaVersion`, `provenance`, `stageA`, `stageB` is required by a + producer for correct interpretation, or `stageA`/`stageB` is nested inside the other. +21. `observedFrom` is absent on a series whose `basis` is `Observed`, is non-null on any other series, or + is a token other than `SamModel` or `EngineStore`. +22. Two series in the same `designDay.series` or `space.series` array share a `quantity`. One series per + quantity is what makes that sort key total, and two competing profiles for one quantity leave a + consumer with no defined choice between them. +23. A field this contract types as `AuditValue`, `AuditToken`, `AuditFlag` or `HourlySeries` is serialized + as a bare JSON number, string, boolean or array instead of the wrapper — it would carry no `available` + and no `basis`, so absence and provenance would both be implicit. +24. Two `stageB.spaces` records share the same **effective space identity** for one design day and load + type — that is, the same `(guid, designDayKey, loadType)` when `guid` is non-null, or the same + `(name, designDayKey, loadType)` when it is `null`. Uniqueness follows the GUID-first identity rule, + not the sort key: two records with one GUID and two different names would satisfy a + `(guid, name, …)` check while still double-counting a single space. One space, one design day and one + load type yield exactly one record, so a duplicate is an extraction defect — and it would leave the + declared sort key tied, making serialization fall back to producer enumeration order and the document + non-deterministic despite the key being called total. + +Rules 2, 5, 6, 7, 8 and 11 exist to make the central failure mode — an unlabelled, mistyped or +over-claimed value — a validation error rather than a reporting judgement. Rules 1, 2, 3 and 6 together +give every shape one availability discipline: unavailable means `value`/`values` **and** `basis` are +`null`, and no type rule fires on a `null`. Rule 10 makes the cross-document pairing key self-checking, +so a producer cannot hand-write a key that its own declared rules would not produce. Rule 13 makes +non-determinism a contract error rather than something a reader discovers from a noisy diff. Rule 16 +makes a `Calculated` value unable to contradict the values it was derived from. Rules 17, 22 and 24 supply +the uniqueness the "already total" sort keys depend on, and rule 23 stops a producer dropping the wrapper +shapes altogether. **Rule 18 is what makes +"declared, never inferred" enforceable** — without it a producer could declare `ObservedHourly` while +emitting reconstructed series, and the contract's central discipline would rest on good intentions. + +A duplicated `alignmentKey` is deliberately **not** an error: it is a legitimate model condition, handled +by excluding the whole group from pairing ([§5.3.1](#531-cross-document-alignment)) and reporting it as +ambiguous. + +## 10. Deliberately excluded from v1 + +| Excluded | Why | +|---|---| +| EnergyPlus hourly sizing-period weather from the engine itself | Needs added `Output:Variable` requests and a re-run; would change converter output | +| EnergyPlus per-space 24-hour sizing profiles | Same reason | +| System and plant sizing (`SystemSizes`, `ComponentSizes`) | Zone-level first; these are available with no re-run and can follow | +| Any gate contribution | This contract is evidence, not a verdict | +| Tolerance bands for design-day quantities | Cannot be set before the first audit shows the real spread | + +Adding the first two requires an explicit **benchmark audit mode**, agreed separately, and only if the +reconstruction leaves material ambiguity. + +## 11. Open questions for agreement + +These need a decision before implementation, and are the reason this document is a draft: + +1. **One document per engine, or one per engine per design day?** This draft assumes one document per + engine carrying all its design days, mirroring `benchmark-.json`. +2. **Is `design-day-audit-v1` versioned independently of the benchmark schema?** This draft assumes yes, + with its own `auditSchemaVersion`. +3. **Should `unfactoredDesignLoad` be required on the EnergyPlus side?** It is available + (`ZoneSizes.CalcDesLoad` alongside `UserDesLoad`) and would settle `reportedLoadBasis` on that side + immediately. Making it required would force the TAS side to declare `Unknown` explicitly, which may + be the more useful outcome. +4. **Does the audit live beside the benchmark reports** in `docs/benchmark/reports/-…/`, or in its + own `docs/benchmark/design-day-audit/` tree? +5. **Does `ObservedHourly` need a parametric summary field**, so collapse loss can be computed without + the comparator re-deriving max/range/mean from the series each time? Such a summary would carry + `basis: Calculated` and fall under validation rule 16, so it could not disagree with the series it + summarises. +6. **Is `loadType` + normalised name a strong enough `alignmentKey`?** SAM `DesignDay` has no GUID, so + the key rests on the name. Adding `month` and `dayOfMonth` would harden it against two same-named + days, at the cost of failing to pair when one route drops the calendar position. The recorded + fixture does not exercise the collision — its days are distinctly named — so this is a judgement + about models not yet seen. + +## 12. Glossary additions + +Terms added to [GLOSSARY.md](GLOSSARY.md) by this contract: *Alignment key*, *Collapse loss*, +*Derived hourly*, *Observed hourly*, *Processed parametric*, *Reported load basis*, *Value basis*. diff --git a/docs/benchmark/GLOSSARY.md b/docs/benchmark/GLOSSARY.md index ae25787..d45662c 100644 --- a/docs/benchmark/GLOSSARY.md +++ b/docs/benchmark/GLOSSARY.md @@ -1,5 +1,9 @@ # Benchmark glossary +## Alignment key + +The cross-document identity that pairs one engine's design day with another's, derived from the ancestor the two routes share — the SAM source design day, or a shared DDY file — rather than from either engine's own label. The document declares which ancestor was used, and a DDY-derived pairing additionally requires the two DDY file hashes to match, since station names repeat across weather files. Its derivation rules are versioned, so a changed normalisation cannot masquerade as a changed design day. A day with no traceable ancestor has no alignment key and is reported one-sided rather than guessed into a pair. See [DESIGN-DAY-AUDIT.md](DESIGN-DAY-AUDIT.md). + ## Canonicalization The deterministic process that converts a SAM model into the representation hashed by `canonicalModelHash`. Its rules are identified by `canonicalizationVersion` so a changed algorithm cannot masquerade as a changed model. @@ -8,10 +12,18 @@ The deterministic process that converts a SAM model into the representation hash The unit required by the benchmark contract for a metric. Producers convert values at emission time and include the unit token on every metric; consumers never infer a unit from a field name. +## Collapse loss + +What an hourly → parametric design-day translation discards. Measured on one engine's side alone, by summarising an `ObservedHourly` series into parametric terms and comparing that against the `ProcessedParametric` definition the engine echoed. It needs no reconstruction, so it is the strongest design-day evidence available. See [DESIGN-DAY-AUDIT.md](DESIGN-DAY-AUDIT.md). + ## Conditioning pairing The two engine configurations intended to represent comparable space conditioning. The B0 candidate is OpenStudio Ideal Loads versus Tas thermostats, IZAMs, and TBD sizing. It remains provisional until the B2 `SingleBox` checkpoint and energy-modeller review. +## Derived hourly + +A 24-hour design-day series **reconstructed** by a producer from an engine's `ProcessedParametric` definition using that engine's documented design-day algorithms. It is never the engine's own output, and describing one as "reported by" the engine is prohibited. Requires a declared algorithm and version. See [DESIGN-DAY-AUDIT.md](DESIGN-DAY-AUDIT.md). + ## Engine The independent simulation implementation that produces physical results. In this programme the engine families are OpenStudio/EnergyPlus and EDSL Tas. Engine identity and version belong in provenance. @@ -24,10 +36,22 @@ The overall comparator outcome derived from contract validity, coverage diagnost Whether a producer obtained a real metric from its route. Available means `available: true` with a non-null value, including a measured zero. Unavailable means `available: false` with `value: null`. +## Observed hourly + +A 24-hour design-day series an engine actually consumed, as authored in the model, with nothing fitted or modelled. In this programme TAS supplies it from TBD design days. See [DESIGN-DAY-AUDIT.md](DESIGN-DAY-AUDIT.md). + +## Processed parametric + +The parametric design-day definition an engine accepted and echoed back after its own input processing — maximum dry bulb, daily range, humidity condition, solar model and clearness, and so on. It is not a series, and must never be compared hour-by-hour against one. EnergyPlus supplies it from `eplusout.eio`. See [DESIGN-DAY-AUDIT.md](DESIGN-DAY-AUDIT.md). + ## Provenance The evidence describing what was run and how: model and weather identities and hashes, canonicalization version, code revisions, engine versions, route, design-day source, run time, state, warnings, and notes. +## Reported load basis + +Whether a sizing load a producer reports is stated before or after that engine's sizing factor (`PreSizingFactor`, `PostSizingFactor` or `Unknown`). It is settled per engine, by comparing a reported load against that engine's own unfactored result — never by comparing two engines with each other, because a cross-engine ratio cannot separate a sizing margin from a physical difference. See [DESIGN-DAY-AUDIT.md](DESIGN-DAY-AUDIT.md). + ## Route The complete model-translation and simulation path used to produce a benchmark document. A route is distinct from an engine because one engine may be exercised through native SAM translation or a shared interchange model. @@ -36,6 +60,10 @@ The complete model-translation and simulation path used to produce a benchmark d A configurable reporting classification based on the difference between two available, unit-compatible values. B0 defines provisional pass/warn/fail bands; it does not validate them as acceptance thresholds. +## Value basis + +How an audited number came to exist, declared on every audit value: `Observed` (read as authored), `EngineEchoed` (the engine restating an input it accepted), `EngineReported` (a result the engine computed), `Reconstructed` (modelled by the producer from echoed inputs using a documented engine algorithm) or `Calculated` (plain arithmetic over values already in the same document). The echo/report distinction is the axis the design-day audit turns on — an echo says what the engine was asked to simulate, a report says what it produced. It is recorded independently of the representation the value sits in, so an over-claimed value is a validation error rather than a matter of wording. See [DESIGN-DAY-AUDIT.md](DESIGN-DAY-AUDIT.md). + ## Whole-model reconciliation A diagnostic comparison between a reported model total and an aggregation of matched space results where the metric semantics permit aggregation. A reconciliation result is kept separate from the direct cross-engine comparison. From 002ff0ee4a3df5dbf2dd28667a68bc780b2c41d2 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Tue, 18 Aug 2026 10:11:04 +0200 Subject: [PATCH 09/11] Add AI agent instructions and project progress tracking Generated by Michal Dengusiak and CodeClaude Co-Authored-By: Claude Sonnet 5 --- AGENTS.md | 54 +++++++++++++++++++++++++++++++++++++++++++++ PROJECT_PROGRESS.md | 28 +++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 AGENTS.md create mode 100644 PROJECT_PROGRESS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2dbb955 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,54 @@ +# Project Instructions + +This repository may be worked on by different AI agents, +models, sessions, accounts, and computers. + +Git and committed repository files are the authoritative project state. +Do not rely on previous conversation memory. + +## Before starting work + +Before making significant changes: + +1. Read this `AGENTS.md`. +2. Read `PROJECT_PROGRESS.md`. +3. Understand the relevant existing code, architecture, tests, and conventions. +4. Review the current Git branch and working tree. +5. Continue from the documented current state. + +Prefer small, safe, targeted changes consistent with existing conventions. +Avoid unrelated refactoring unless it is required for the task. + +## Project continuity + +After each meaningful implementation, debugging, research, +testing, or validation checkpoint, update `PROJECT_PROGRESS.md`. + +Do not update it for trivial actions such as opening files, +searching the repository, or reading documentation. + +Keep `PROJECT_PROGRESS.md` concise and sufficient for another AI +agent on another computer to continue without access to the current conversation. + +`PROJECT_PROGRESS.md` must contain: + +- current status; +- work completed; +- important decisions and assumptions; +- files changed; +- tests, builds, checks, or validation performed and results; +- unresolved issues, risks, or blockers; +- exact recommended next step. + +When updating `PROJECT_PROGRESS.md`, preserve still-relevant information +from previous sessions. Remove or replace information only when it is +obsolete, resolved, or superseded. + +Before ending meaningful work, verify that `PROJECT_PROGRESS.md` +accurately represents the current repository state. + +When appropriate, remind the user to commit and push changes before +switching computers, accounts, sessions, or AI agents. + +Never assume conversation history, terminal history, generated files, +or uncommitted local changes will exist on another computer. \ No newline at end of file diff --git a/PROJECT_PROGRESS.md b/PROJECT_PROGRESS.md new file mode 100644 index 0000000..496b4b8 --- /dev/null +++ b/PROJECT_PROGRESS.md @@ -0,0 +1,28 @@ +# Project Progress + +## Branch +`sow/2026-Q3` + +## Last updated +Not updated yet. + +## Current status +No work recorded yet for this branch. + +## Completed +- None yet. + +## Decisions / assumptions +- None yet. + +## Files changed +- None yet. + +## Validation +- Not run yet. + +## Issues / blockers +- None known. + +## Next step +- Review the current repository state and continue with the next planned task. \ No newline at end of file From 198a5165511750fa9dabe129141755be25b801bc Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Thu, 1 Oct 2026 16:51:55 +0200 Subject: [PATCH 10/11] chore: remove generated cross-engine report outputs and internal agent files from upstream contribution Dated report outputs (comparison/benchmark JSON, CSV, MD) are reproducible run results, not referenced by source or tests. Co-Authored-By: Claude Sonnet 5.5 --- AGENTS.md | 54 - PROJECT_PROGRESS.md | 28 - .../README.md | 156 - .../benchmark-OpenStudio.json | 888 ---- .../benchmark-TAS.json | 890 ---- .../comparison-summary.json | 3728 ---------------- .../comparison.csv | 149 - .../comparison.md | 292 -- .../2026-07-25-cross-engine-smoke/README.md | 118 - .../benchmark-OpenStudio.json | 888 ---- .../benchmark-TAS.json | 890 ---- .../comparison-summary.json | 3734 ---------------- .../comparison.csv | 149 - .../comparison.md | 296 -- .../reports/2026-07-26-design-loads/README.md | 223 - .../benchmark-OpenStudio.json | 958 ----- .../benchmark-TAS.json | 890 ---- .../comparison-summary.json | 3798 ----------------- .../2026-07-26-design-loads/comparison.csv | 149 - .../2026-07-26-design-loads/comparison.md | 292 -- 20 files changed, 18570 deletions(-) delete mode 100644 AGENTS.md delete mode 100644 PROJECT_PROGRESS.md delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/README.md delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-OpenStudio.json delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-TAS.json delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison-summary.json delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.csv delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.md delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/README.md delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-OpenStudio.json delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-TAS.json delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison-summary.json delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.csv delete mode 100644 docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.md delete mode 100644 docs/benchmark/reports/2026-07-26-design-loads/README.md delete mode 100644 docs/benchmark/reports/2026-07-26-design-loads/benchmark-OpenStudio.json delete mode 100644 docs/benchmark/reports/2026-07-26-design-loads/benchmark-TAS.json delete mode 100644 docs/benchmark/reports/2026-07-26-design-loads/comparison-summary.json delete mode 100644 docs/benchmark/reports/2026-07-26-design-loads/comparison.csv delete mode 100644 docs/benchmark/reports/2026-07-26-design-loads/comparison.md diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 2dbb955..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,54 +0,0 @@ -# Project Instructions - -This repository may be worked on by different AI agents, -models, sessions, accounts, and computers. - -Git and committed repository files are the authoritative project state. -Do not rely on previous conversation memory. - -## Before starting work - -Before making significant changes: - -1. Read this `AGENTS.md`. -2. Read `PROJECT_PROGRESS.md`. -3. Understand the relevant existing code, architecture, tests, and conventions. -4. Review the current Git branch and working tree. -5. Continue from the documented current state. - -Prefer small, safe, targeted changes consistent with existing conventions. -Avoid unrelated refactoring unless it is required for the task. - -## Project continuity - -After each meaningful implementation, debugging, research, -testing, or validation checkpoint, update `PROJECT_PROGRESS.md`. - -Do not update it for trivial actions such as opening files, -searching the repository, or reading documentation. - -Keep `PROJECT_PROGRESS.md` concise and sufficient for another AI -agent on another computer to continue without access to the current conversation. - -`PROJECT_PROGRESS.md` must contain: - -- current status; -- work completed; -- important decisions and assumptions; -- files changed; -- tests, builds, checks, or validation performed and results; -- unresolved issues, risks, or blockers; -- exact recommended next step. - -When updating `PROJECT_PROGRESS.md`, preserve still-relevant information -from previous sessions. Remove or replace information only when it is -obsolete, resolved, or superseded. - -Before ending meaningful work, verify that `PROJECT_PROGRESS.md` -accurately represents the current repository state. - -When appropriate, remind the user to commit and push changes before -switching computers, accounts, sessions, or AI agents. - -Never assume conversation history, terminal history, generated files, -or uncommitted local changes will exist on another computer. \ No newline at end of file diff --git a/PROJECT_PROGRESS.md b/PROJECT_PROGRESS.md deleted file mode 100644 index 496b4b8..0000000 --- a/PROJECT_PROGRESS.md +++ /dev/null @@ -1,28 +0,0 @@ -# Project Progress - -## Branch -`sow/2026-Q3` - -## Last updated -Not updated yet. - -## Current status -No work recorded yet for this branch. - -## Completed -- None yet. - -## Decisions / assumptions -- None yet. - -## Files changed -- None yet. - -## Validation -- Not run yet. - -## Issues / blockers -- None known. - -## Next step -- Review the current repository state and continue with the next planned task. \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/README.md b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/README.md deleted file mode 100644 index bdbb4e9..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/README.md +++ /dev/null @@ -1,156 +0,0 @@ -# B3 — cross-engine comparison repeated on the fixed OpenStudio producer - -This repeats the [first real cross-engine run](../2026-07-25-cross-engine-smoke/README.md) — same -source model, same weather file, same workstation — now that the OpenStudio producer's design-day -provenance defect is fixed ([SAM_OpenStudio #17](https://github.com/SAM-BIM/SAM_OpenStudio/pull/17), -merged to `sow/2026-Q3` as `1ae8637`). - -Its purpose is narrow: establish whether the provenance gate now accepts the pair, and whether -correcting the declared design-day basis moves any number. It remains a **mechanical validation of -the comparator and the producers**, not a validated engine-equivalence claim. - -## Run - -- **Date:** 2026-07-25 (TAS `14:35:23Z`, OpenStudio `14:36:36Z`). -- **Machine:** the same licensed EDSL Tas workstation as the previous run. -- **Model:** `HungaryHouse-WeahterDDY.sam` — 14 spaces, carrying embedded design days. -- **Weather:** `HUN_CEN_Budapest.Met.Center.128400_TMYx.epw`. -- **Engines:** Tas `9.5.7.0` (producer duration 60.8 s); EnergyPlus `25.1.0` / OpenStudio SDK - `3.10.0` (producer duration 21.5 s). -- **Producers built from:** `SAM_Tas@ff00497` and `SAM_OpenStudio@1ae8637`, both on `sow/2026-Q3`. -- **Comparator:** `SAM_Validation@c3eec2cafaf42267a506579a9efd078cb7fcc473` (the B3 squash merge). -- Both producers exited `0` with `state = Success`; the comparator exited `0`. - -Artefacts in this folder are the verbatim outputs: the two neutral input documents and the three -generated reports. - -## Result 1 — the provenance gate now passes - -| Field | Previous run | This run | -|---|---|---| -| TAS `designDaySource` | `EmbeddedModel` | `EmbeddedModel` | -| OpenStudio `designDaySource` | `None` (hardcoded — the defect) | **`EmbeddedModel`** (derived from the route) | -| **Provenance status** | **Fail** | **Pass** | - -The two documents also agree on `canonicalModelHash` -(`sha256:07a3ef15…`), `sourceModelGuid` and the weather hash, so the comparator now confirms in full -that the pair is a valid comparison: same canonical model, same weather, same design-day basis, one -engine and route on each side. - -## Result 2 — not one simulated number changed - -The complete generated `comparison.csv` is **byte-identical** to the previous run — not merely equal on -the value columns. The two files share one Git blob: - -```text -git hash-object ../2026-07-25-cross-engine-smoke/comparison.csv comparison.csv - 840655621cd7c9e95d24233687c229f50c8f3cca - 840655621cd7c9e95d24233687c229f50c8f3cca -``` - -Every metric value, absolute and relative difference, band, note and tolerance column therefore agrees -exactly. `comparison.md` and `comparison-summary.json` do differ, as they must: they carry the -provenance block, which is precisely what changed. - -Two things follow, and they matter more than the passing gate: - -1. **The defect was exactly what it was diagnosed as** — a *reporting* defect. The OpenStudio route - was already building design days from the embedded model; only the declared `designDaySource` was - wrong. Had the fix altered the sizing basis, the loads would have moved. -2. **The pipeline is reproducible.** Two independent full runs of both engines, hours apart, produced - identical metrics down to the last binary64 digit — across TAS COM, a gbXML round trip, - EnergyPlus, and both producers' extraction paths. - -## Result 3 — the candidate conditioning pairing is still not equivalent - -The overall gate is still `Fail`, now on the numerical status alone: - -| Status | Value | -|---|---| -| Numerical | **Fail** | -| Coverage | Warn (82 of 148 required metrics comparable; 66 unavailable) | -| Provenance | Pass | -| Reconciliation | Pass | -| **Overall** | **Fail** | - -This is the point of the exercise. Previously the `Fail` was confounded: a provenance mismatch made the -comparison invalid regardless of the numbers, so the numerical disagreement could be set aside as -"probably the declared design-day basis". That specific objection is answered — **the neutral inputs are -provably identical, the declared design-day basis now agrees, and the numbers still disagree.** - -What this does *not* establish is covered under -[what remains open](#what-this-run-does-not-establish-about-design-days) below. - -| Metric | TAS | OpenStudio | Rel. diff | Band | Gates? | -|---|---:|---:|---:|---|---| -| Annual heating (kWh) | 4331.96 | 1751.18 | **59.6 %** | Fail | **yes** | -| Annual cooling (kWh) | 0 | 5.25 | 100 % | Fail | **yes** | -| Peak heating load (kW) | 3.934 | 5.721 | 31.2 % | Fail | no — designated informational | -| Peak cooling load (kW) | 0 | 0.040 | 100 % | Fail | no — designated informational | -| Floor area | 167.95195245742798 | 167.95195207345205 | 2.3e-9 (= 2.3e-7 %) | Match | yes | - -Among comparable per-space heating peak-load rows, relative differences span approximately **12–100 %** -(11 comparable rows). The previously quoted 29 % and 52 % values are selected examples, not the complete -range. The cooling rows compare a TAS *measured zero* against a -near-zero OpenStudio value, so their 100 % figures are not meaningful magnitudes. The two whole-model -peak loads are banded and shown but excluded from the numerical status -(TOLERANCES.md → Informational metrics), so they are not what fails this comparison — annual energy, -per-space peak loads and unmet hours are. - -**Conclusion:** the D10 candidate pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD -sizing) is **not** validated. It must stay labelled a *candidate* and be investigated against the B4 -corpus, metric by metric — not by adjusting tolerances. - -### What this run does *not* establish about design days - -It would be wrong to read this run as eliminating design-day handling as a cause of the load gap. Two -things were shown: the two runs declare the same design-day *category*, and the provenance fix changed -no result. Neither is evidence that the engines built **equivalent design days** after translation. - -- `canonicalModelHash` describes the neutral loaded SAM model and is computed *before any - engine-specific modification* ([METHODOLOGY.md](../../METHODOLOGY.md) → The two model hashes). It - says nothing about what either engine did with the embedded design days afterwards. -- `designDaySource` records only the coarse category `Ddy` / `EmbeddedModel` / `None`. Both sides - reporting `EmbeddedModel` means both drew on the model's own design days, not that they instantiated - the same ones. -- Identical metrics before and after a provenance-only fix show that *the fix* did not alter results — - by construction, since it only changed a reported field. - -**Differing design-day translation therefore remains an open hypothesis**, and a leading one: TAS sizes -through TBD while OpenStudio emits `SizingPeriod:DesignDay` objects into the EnergyPlus model, and a -differing sizing basis moves heating loads. Closing it needs engine-level evidence — compare the design -days actually instantiated on each side (the TBD sizing inputs against the generated OSM/IDF -`SizingPeriod:DesignDay` objects), which is outside this run's provenance-only scope and belongs with -the B4 investigation. - -## Carried forward - -The 66 unavailable required metrics break down as follows (all 14 spaces matched, so none of this is -an alignment problem): - -| Count | Metrics | Availability | Meaning | -|---:|---|---|---| -| 28 | `heating.designLoad` + `cooling.designLoad`, all 14 spaces | TasOnly | OpenStudio emits no per-space design load on the native route | -| 24 | `cooling.peakLoad` + `cooling.peakHour`, 12 spaces | OpenStudioOnly | TAS reports no cooling result where OpenStudio does | -| 8 | `heating`/`cooling` `peakLoad` + `peakHour`, 2 spaces | Neither | neither engine reports these two spaces | -| 4 | `heating`/`cooling` `unmetHours`, 2 spaces | TasOnly | | -| 2 | `heating.peakLoad` + `heating.peakHour`, 1 space | OpenStudioOnly | | - -- OpenStudio's missing per-space design loads account for **28 of the 66** unavailable required metrics - and are the largest single source of incomplete coverage (a documented route limitation). -- TAS still reports `peakCoolingHour = 0` with `available: true` for a measured-zero cooling load; a - peak *hour* for a zero load carries no information (a B2 producer-semantics question). -- The engines still disagree about which spaces are conditioned, and the table above quantifies it: 24 - metrics are unavailable because TAS reports no cooling result where OpenStudio does, and one space - reports `heating.peakLoad` as available `0` from OpenStudio while TAS reports it unavailable. This is - a concrete, bounded lead for the B4 investigation. -- **Design-day translation is an open hypothesis** for the load gap (see above). It needs engine-level - evidence comparing the design days each engine actually instantiated, not provenance fields. - -## Status - -- **Provenance gate: closed.** The defect it caught is fixed and the fix is verified end to end. -- **Comparator: validated twice on real two-engine data**, now including a reproducibility check. -- **Engine equivalence: open.** The declared-provenance objection is answered, but design-day - *translation* remains untested, so the cause of the load gap is still unidentified. Next: the B4 - corpus (`SAM_Samples`) and energy-modeller review. diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-OpenStudio.json b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-OpenStudio.json deleted file mode 100644 index 332192d..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-OpenStudio.json +++ /dev/null @@ -1,888 +0,0 @@ -{ - "schemaVersion": "1.0.0", - "provenance": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "1ae8637ad19accd4d2740a5aba0106463882752f", - "engine": { - "kind": "OpenStudio", - "name": "EnergyPlus", - "version": "25.1.0", - "sdkVersion": "3.10.0" - }, - "route": "Native-OpenStudio", - "weather": { - "identity": "Budapest", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-25T14:36:36.3746344Z", - "durationSeconds": 21.5130193, - "state": "Success", - "resultSources": [ - "SAM.Analytical.OpenStudio" - ], - "warnings": [], - "notes": [] - }, - "model": { - "consumptionHeating": { - "value": 1751.1764825992748, - "unit": "kWh", - "available": true - }, - "consumptionCooling": { - "value": 5.2499930426550705, - "unit": "kWh", - "available": true - }, - "peakHeatingLoad": { - "value": 5.720615732872154, - "unit": "kW", - "available": true - }, - "peakHeatingHour": { - "value": 775, - "unit": "hourOfYear", - "available": true - }, - "peakCoolingLoad": { - "value": 0.0402250938503556, - "unit": "kW", - "available": true - }, - "peakCoolingHour": { - "value": 7974, - "unit": "hourOfYear", - "available": true - }, - "floorArea": { - "value": 167.95195207345205, - "unit": "m2", - "available": true - }, - "volume": { - "value": 470.1821620538829, - "unit": "m3", - "available": true - } - }, - "spaces": [ - { - "guid": "025eb80f65d745b799511b78ac2f82fb", - "name": "01_18 Void", - "area": { - "value": 6.6920779999999995, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.118059598283878, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - } - }, - { - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "name": "01_13 Bedroom", - "area": { - "value": 21.280010175465385, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.33913699268221, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 1185.0374713731348, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 15.588918884437554, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 7974, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "name": "GF_08 Bathroom", - "area": { - "value": 3.647967799712404, - "unit": "m2", - "available": true - }, - "volume": { - "value": 10.396708229180378, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 141.7703965184828, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 918, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "name": "01_15 Plantroom", - "area": { - "value": 2.9450000000000016, - "unit": "m2", - "available": true - }, - "volume": { - "value": 5.746544632499992, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "name": "GF_09 WC", - "area": { - "value": 2.355387206192353, - "unit": "m2", - "available": true - }, - "volume": { - "value": 6.712853537648204, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "354b8786b4714f3c9941cec051a93361", - "name": "GF_10 Closet", - "area": { - "value": 3.0986077487352315, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.007679927026205, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 262.9986126690514, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "name": "01_11 Void", - "area": { - "value": 5.03787628653, - "unit": "m2", - "available": true - }, - "volume": { - "value": 7.564547888993382, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - } - }, - { - "guid": "6e7ef8487b714da3b730e0f226af927e", - "name": "01_17 Gallery", - "area": { - "value": 23.55004650825489, - "unit": "m2", - "available": true - }, - "volume": { - "value": 58.340316505330264, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 255.3860902131586, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "name": "01_16 Bathroom", - "area": { - "value": 9.409999999999991, - "unit": "m2", - "available": true - }, - "volume": { - "value": 26.966204214472434, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 190.23285633880042, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "name": "GF_05 Stairs", - "area": { - "value": 4.634866891556, - "unit": "m2", - "available": true - }, - "volume": { - "value": 28.16450759620351, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 39.29050142981706, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 774, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "name": "GF_12 EntranceHall", - "area": { - "value": 6.955039759539971, - "unit": "m2", - "available": true - }, - "volume": { - "value": 20.363019689898447, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 133.44180277588939, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 774, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "name": "GF_07 LivingRoom", - "area": { - "value": 38.98503142608865, - "unit": "m2", - "available": true - }, - "volume": { - "value": 121.59536233412693, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 1789.9830426085152, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 775, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "name": "GF_11 Bedroom", - "area": { - "value": 18.080018143643976, - "unit": "m2", - "available": true - }, - "volume": { - "value": 51.52805170938531, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 936.5975380258114, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 727, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 28.70645886203079, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 7991, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "name": "01_14 Bedroom", - "area": { - "value": 21.280022127733165, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.3391691981517, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 989.0652526225581, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - } - ] -} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-TAS.json b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-TAS.json deleted file mode 100644 index dc68c19..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/benchmark-TAS.json +++ /dev/null @@ -1,890 +0,0 @@ -{ - "schemaVersion": "1.0.0", - "provenance": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", - "engine": { - "kind": "TAS", - "name": "Tas", - "version": "9.5.7.0", - "sdkVersion": null - }, - "route": "Native-TAS", - "weather": { - "identity": "Budapest", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-25T14:35:23.4577336Z", - "durationSeconds": 60.7963213, - "state": "Success", - "resultSources": [ - "SAM.Analytical.Tas" - ], - "warnings": [], - "notes": [ - "Shared gbXML was exported from the source model (no --gbxml supplied)." - ] - }, - "model": { - "consumptionHeating": { - "value": 4331.961588091359, - "unit": "kWh", - "available": true - }, - "consumptionCooling": { - "value": 0, - "unit": "kWh", - "available": true - }, - "peakHeatingLoad": { - "value": 3.93393212890625, - "unit": "kW", - "available": true - }, - "peakHeatingHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "peakCoolingLoad": { - "value": 0, - "unit": "kW", - "available": true - }, - "peakCoolingHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "floorArea": { - "value": 167.95195245742798, - "unit": "m2", - "available": true - }, - "volume": { - "value": 470.1821608543396, - "unit": "m3", - "available": true - } - }, - "spaces": [ - { - "guid": "025eb80f65d745b799511b78ac2f82fb", - "name": "01_18 Void", - "area": { - "value": 6.6920779999999995, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.118059598283878, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "name": "01_13 Bedroom", - "area": { - "value": 21.280010175465385, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.33913699268221, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 574.0481567382812, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 565.50927734375, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 65, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "name": "GF_08 Bathroom", - "area": { - "value": 3.647967799712404, - "unit": "m2", - "available": true - }, - "volume": { - "value": 10.396708229180378, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 199.83349609375, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 199.82528686523438, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 4761, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "name": "01_15 Plantroom", - "area": { - "value": 2.9450000000000016, - "unit": "m2", - "available": true - }, - "volume": { - "value": 5.746544632499992, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 28.741456985473633, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "name": "GF_09 WC", - "area": { - "value": 2.355387206192353, - "unit": "m2", - "available": true - }, - "volume": { - "value": 6.712853537648204, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 69.60518646240234, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 52.024513244628906, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "354b8786b4714f3c9941cec051a93361", - "name": "GF_10 Closet", - "area": { - "value": 3.0986077487352315, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.007679927026205, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 137.82980346679688, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 117.870849609375, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 67, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "name": "01_11 Void", - "area": { - "value": 5.03787628653, - "unit": "m2", - "available": true - }, - "volume": { - "value": 7.564547888993382, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "6e7ef8487b714da3b730e0f226af927e", - "name": "01_17 Gallery", - "area": { - "value": 23.55004650825489, - "unit": "m2", - "available": true - }, - "volume": { - "value": 58.340316505330264, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 379.9020080566406, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 132.86868286132812, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1442, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "name": "01_16 Bathroom", - "area": { - "value": 9.409999999999991, - "unit": "m2", - "available": true - }, - "volume": { - "value": 26.966204214472434, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 366.3569641113281, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 366.33624267578125, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1444, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "name": "GF_05 Stairs", - "area": { - "value": 4.634866891556, - "unit": "m2", - "available": true - }, - "volume": { - "value": 28.16450759620351, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 69.23214721679688, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 11.025825500488281, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1452, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "name": "GF_12 EntranceHall", - "area": { - "value": 6.955039759539971, - "unit": "m2", - "available": true - }, - "volume": { - "value": 20.363019689898447, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 204.9483642578125, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 117.45973205566406, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1447, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "name": "GF_07 LivingRoom", - "area": { - "value": 38.98503142608865, - "unit": "m2", - "available": true - }, - "volume": { - "value": 121.59536233412693, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 1065.076171875, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 1025.7105712890625, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1443, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 114, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "name": "GF_11 Bedroom", - "area": { - "value": 18.080018143643976, - "unit": "m2", - "available": true - }, - "volume": { - "value": 51.52805170938531, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 564.768798828125, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 559.67822265625, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 69, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "name": "01_14 Bedroom", - "area": { - "value": 21.280022127733165, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.3391691981517, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 549.6607055664062, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 529.828857421875, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1457, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 29, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - } - ] -} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison-summary.json b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison-summary.json deleted file mode 100644 index cf3836c..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison-summary.json +++ /dev/null @@ -1,3728 +0,0 @@ -{ - "summarySchemaVersion": "1.0.0", - "toleranceProfile": "default", - "toleranceProfileDetail": { - "name": "default", - "warnRelative": 0.05, - "failRelative": 0.15, - "hourWarnAbsolute": 1, - "hourFailAbsolute": 24, - "defaultNearZeroFloor": 0, - "nearZeroFloors": { - "kWh": 1, - "Wh": 1000, - "kW": 0.01, - "W": 10, - "m2": 0.01, - "m3": 0.01, - "h": 0.5 - } - }, - "gate": "Fail", - "statuses": { - "overall": "Fail", - "numerical": "Fail", - "coverage": "Warn", - "provenance": "Pass", - "reconciliation": "Pass" - }, - "provenanceCompatibility": { - "compatible": true, - "mismatches": [] - }, - "counts": { - "match": 48, - "warn": 1, - "fail": 33, - "notApplicable": 66 - }, - "coverage": { - "status": "Warn", - "requiredMetricCount": 148, - "comparableMetricCount": 82, - "unavailableRequiredMetricCount": 66, - "unmatchedSpaceCount": 0, - "ambiguousSpaceCount": 0, - "duplicateSpaceIdentityCount": 0 - }, - "schema": { - "tas": "1.0.0", - "openStudio": "1.0.0", - "driftNote": null - }, - "provenance": { - "tas": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", - "engine": { - "kind": "TAS", - "name": "Tas", - "version": "9.5.7.0", - "sdkVersion": null - }, - "route": "Native-TAS", - "weather": { - "identity": "Budapest", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-25T14:35:23.4577336Z", - "durationSeconds": 60.7963213, - "state": "Success", - "resultSources": [ - "SAM.Analytical.Tas" - ], - "warnings": [], - "notes": [ - "Shared gbXML was exported from the source model (no --gbxml supplied)." - ] - }, - "openStudio": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "1ae8637ad19accd4d2740a5aba0106463882752f", - "engine": { - "kind": "OpenStudio", - "name": "EnergyPlus", - "version": "25.1.0", - "sdkVersion": "3.10.0" - }, - "route": "Native-OpenStudio", - "weather": { - "identity": "Budapest", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-25T14:36:36.3746344Z", - "durationSeconds": 21.5130193, - "state": "Success", - "resultSources": [ - "SAM.Analytical.OpenStudio" - ], - "warnings": [], - "notes": [] - } - }, - "modelMetrics": [ - { - "scope": "model", - "key": "consumptionHeating", - "unit": "kWh", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4331.961588091359, - "available": true, - "unit": "kWh" - }, - "openStudio": { - "value": 1751.1764825992748, - "available": true, - "unit": "kWh" - }, - "absoluteDifference": 2580.785105492084, - "relativeDifference": 0.5957543835537944, - "signedDifference": -2580.785105492084 - }, - { - "scope": "model", - "key": "consumptionCooling", - "unit": "kWh", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "kWh" - }, - "openStudio": { - "value": 5.2499930426550705, - "available": true, - "unit": "kWh" - }, - "absoluteDifference": 5.2499930426550705, - "relativeDifference": 1, - "signedDifference": 5.2499930426550705 - }, - { - "scope": "model", - "key": "peakHeatingLoad", - "unit": "kW", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.93393212890625, - "available": true, - "unit": "kW" - }, - "openStudio": { - "value": 5.720615732872154, - "available": true, - "unit": "kW" - }, - "absoluteDifference": 1.7866836039659035, - "relativeDifference": 0.3123236531513824, - "signedDifference": 1.7866836039659035 - }, - { - "scope": "model", - "key": "peakHeatingHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 775, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 144, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "model", - "key": "peakCoolingLoad", - "unit": "kW", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "kW" - }, - "openStudio": { - "value": 0.0402250938503556, - "available": true, - "unit": "kW" - }, - "absoluteDifference": 0.0402250938503556, - "relativeDifference": 1, - "signedDifference": 0.0402250938503556 - }, - { - "scope": "model", - "key": "peakCoolingHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7974, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 786, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "model", - "key": "floorArea", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 167.95195245742798, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 167.95195207345205, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 3.839759301627055E-07, - "relativeDifference": 2.2862248669602973E-09, - "signedDifference": -3.839759301627055E-07 - }, - { - "scope": "model", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 470.1821608543396, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 470.1821620538829, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 1.1995433055744797E-06, - "relativeDifference": 2.5512309959496336E-09, - "signedDifference": 1.1995433055744797E-06 - } - ], - "spaces": [ - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "guid": "025eb80f65d745b799511b78ac2f82fb", - "tasName": "01_18 Void", - "openStudioName": "01_18 Void", - "matchKind": "Guid", - "metrics": [ - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.6920779999999995, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 6.6920779999999995, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.118059598283878, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 9.118059598283878, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - } - ] - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "tasName": "01_13 Bedroom", - "openStudioName": "01_13 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 21.280010175465385, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 21.280010175465385, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 57.33913699268221, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 57.33913699268221, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 574.0481567382812, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 565.50927734375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 1185.0374713731348, - "available": true, - "unit": "W" - }, - "absoluteDifference": 619.5281940293848, - "relativeDifference": 0.5227920711330089, - "signedDifference": 619.5281940293848 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 522, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 65, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 65, - "relativeDifference": 1, - "signedDifference": -65 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 15.588918884437554, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7974, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "tasName": "GF_08 Bathroom", - "openStudioName": "GF_08 Bathroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.647967799712404, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 3.647967799712404, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 10.396708229180378, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 10.396708229180378, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 199.83349609375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 199.82528686523438, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 141.7703965184828, - "available": true, - "unit": "W" - }, - "absoluteDifference": 58.05489034675156, - "relativeDifference": 0.2905282472378222, - "signedDifference": -58.05489034675156 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 918, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 523, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4761, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 4761, - "relativeDifference": 1, - "signedDifference": -4761 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "tasName": "01_15 Plantroom", - "openStudioName": "01_15 Plantroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 2.9450000000000016, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 2.9450000000000016, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 5.746544632499992, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 5.746544632499992, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 28.741456985473633, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "tasName": "GF_09 WC", - "openStudioName": "GF_09 WC", - "matchKind": "Guid", - "metrics": [ - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 2.355387206192353, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 2.355387206192353, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.712853537648204, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 6.712853537648204, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 69.60518646240234, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 52.024513244628906, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": 52.024513244628906, - "relativeDifference": 1, - "signedDifference": -52.024513244628906 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 1441, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "guid": "354b8786b4714f3c9941cec051a93361", - "tasName": "GF_10 Closet", - "openStudioName": "GF_10 Closet", - "matchKind": "Guid", - "metrics": [ - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.0986077487352315, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 3.0986077487352315, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.007679927026205, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 9.007679927026205, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 137.82980346679688, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 117.870849609375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 262.9986126690514, - "available": true, - "unit": "W" - }, - "absoluteDifference": 145.1277630596764, - "relativeDifference": 0.5518195004408647, - "signedDifference": 145.1277630596764 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 522, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 67, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 67, - "relativeDifference": 1, - "signedDifference": -67 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "tasName": "01_11 Void", - "openStudioName": "01_11 Void", - "matchKind": "Guid", - "metrics": [ - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 5.03787628653, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 5.03787628653, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 7.564547888993382, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 7.564547888993382, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - } - ] - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "guid": "6e7ef8487b714da3b730e0f226af927e", - "tasName": "01_17 Gallery", - "openStudioName": "01_17 Gallery", - "matchKind": "Guid", - "metrics": [ - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 23.55004650825489, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 23.55004650825489, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 58.340316505330264, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 58.340316505330264, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 379.9020080566406, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 132.86868286132812, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 255.3860902131586, - "available": true, - "unit": "W" - }, - "absoluteDifference": 122.51740735183049, - "relativeDifference": 0.4797340655850561, - "signedDifference": 122.51740735183049 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1442, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 523, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "tasName": "01_16 Bathroom", - "openStudioName": "01_16 Bathroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.409999999999991, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 9.409999999999991, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 26.966204214472434, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 26.966204214472434, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 366.3569641113281, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 366.33624267578125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 190.23285633880042, - "available": true, - "unit": "W" - }, - "absoluteDifference": 176.10338633698083, - "relativeDifference": 0.4807151622528315, - "signedDifference": -176.10338633698083 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1444, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 525, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "tasName": "GF_05 Stairs", - "openStudioName": "GF_05 Stairs", - "matchKind": "Guid", - "metrics": [ - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4.634866891556, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 4.634866891556, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 28.16450759620351, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 28.16450759620351, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 69.23214721679688, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 11.025825500488281, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 39.29050142981706, - "available": true, - "unit": "W" - }, - "absoluteDifference": 28.26467592932878, - "relativeDifference": 0.7193768188430163, - "signedDifference": 28.26467592932878 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1452, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 774, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 678, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "tasName": "GF_12 EntranceHall", - "openStudioName": "GF_12 EntranceHall", - "matchKind": "Guid", - "metrics": [ - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.955039759539971, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 6.955039759539971, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 20.363019689898447, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 20.363019689898447, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 204.9483642578125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Warn", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 117.45973205566406, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 133.44180277588939, - "available": true, - "unit": "W" - }, - "absoluteDifference": 15.982070720225323, - "relativeDifference": 0.1197680965616646, - "signedDifference": 15.982070720225323 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1447, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 774, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 673, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "tasName": "GF_07 LivingRoom", - "openStudioName": "GF_07 LivingRoom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 38.98503142608865, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 38.98503142608865, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 121.59536233412693, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 121.59536233412693, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 1065.076171875, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 1025.7105712890625, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 1789.9830426085152, - "available": true, - "unit": "W" - }, - "absoluteDifference": 764.2724713194527, - "relativeDifference": 0.4269719059492821, - "signedDifference": 764.2724713194527 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1443, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 775, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 668, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 114, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 114, - "relativeDifference": 1, - "signedDifference": -114 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "tasName": "GF_11 Bedroom", - "openStudioName": "GF_11 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 18.080018143643976, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 18.080018143643976, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 51.52805170938531, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 51.52805170938531, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 564.768798828125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 559.67822265625, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 936.5975380258114, - "available": true, - "unit": "W" - }, - "absoluteDifference": 376.91931536956145, - "relativeDifference": 0.4024346638408247, - "signedDifference": 376.91931536956145 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 727, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 714, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 69, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 69, - "relativeDifference": 1, - "signedDifference": -69 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 28.70645886203079, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7991, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "tasName": "01_14 Bedroom", - "openStudioName": "01_14 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 21.280022127733165, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 21.280022127733165, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 57.3391691981517, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 57.3391691981517, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 549.6607055664062, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 529.828857421875, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 989.0652526225581, - "available": true, - "unit": "W" - }, - "absoluteDifference": 459.2363952006831, - "relativeDifference": 0.4643135465359781, - "signedDifference": 459.2363952006831 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1457, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 538, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 29, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 29, - "relativeDifference": 1, - "signedDifference": -29 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - } - ], - "spaceDiagnostics": { - "tasCount": 14, - "openStudioCount": 14, - "matchedByGuid": 14, - "matchedByName": 0, - "onlyInTas": [], - "onlyInOpenStudio": [], - "duplicateTasGuids": [], - "duplicateOpenStudioGuids": [], - "duplicateTasNames": [], - "duplicateOpenStudioNames": [], - "ambiguousNameMatches": [] - }, - "reconciliation": [ - { - "engine": "TAS", - "key": "floorArea", - "unit": "m2", - "modelTotal": 167.95195245742798, - "modelTotalAvailable": true, - "sumOfSpaces": 167.95195207345202, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 3.839759585844149E-07, - "relativeDifference": 2.2862250361855374E-09, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "TAS", - "key": "volume", - "unit": "m3", - "modelTotal": 470.1821608543396, - "modelTotalAvailable": true, - "sumOfSpaces": 470.1821620538828, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 1.199543191887642E-06, - "relativeDifference": 2.5512307541564592E-09, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "OpenStudio", - "key": "floorArea", - "unit": "m2", - "modelTotal": 167.95195207345205, - "modelTotalAvailable": true, - "sumOfSpaces": 167.95195207345202, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 2.842170943040401E-14, - "relativeDifference": 1.6922524019235018E-16, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "OpenStudio", - "key": "volume", - "unit": "m3", - "modelTotal": 470.1821620538829, - "modelTotalAvailable": true, - "sumOfSpaces": 470.1821620538828, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 1.1368683772161603E-13, - "relativeDifference": 2.417931748516387E-16, - "band": "Match", - "notApplicableReason": null - } - ] -} diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.csv b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.csv deleted file mode 100644 index 8406556..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.csv +++ /dev/null @@ -1,149 +0,0 @@ -scope,metric,unit,availability,tasValue,openStudioValue,absoluteDifference,relativeDifference,relativePercent,signedDifference,band,note,toleranceProfile,warnRelative,failRelative,hourWarnAbsolute,hourFailAbsolute,nearZeroFloor,informational -model,consumptionHeating,kWh,Both,4331.961588091359,1751.1764825992748,2580.785105492084,0.5957543835537944,59.57543835537944,-2580.785105492084,Fail,,default,0.05,0.15,1,24,1,false -model,consumptionCooling,kWh,Both,0,5.2499930426550705,5.2499930426550705,1,100,5.2499930426550705,Fail,,default,0.05,0.15,1,24,1,false -model,peakHeatingLoad,kW,Both,3.93393212890625,5.720615732872154,1.7866836039659035,0.3123236531513824,31.23236531513824,1.7866836039659035,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true -model,peakHeatingHour,hourOfYear,Both,919,775,144,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -model,peakCoolingLoad,kW,Both,0,0.0402250938503556,0.0402250938503556,1,100,0.0402250938503556,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true -model,peakCoolingHour,hourOfYear,Both,0,7974,786,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -model,floorArea,m2,Both,167.95195245742798,167.95195207345205,3.839759301627055E-07,2.2862248669602973E-09,2.2862248669602975E-07,-3.839759301627055E-07,Match,,default,0.05,0.15,1,24,0.01,false -model,volume,m3,Both,470.1821608543396,470.1821620538829,1.1995433055744797E-06,2.5512309959496336E-09,2.5512309959496334E-07,1.1995433055744797E-06,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,area,m2,Both,6.6920779999999995,6.6920779999999995,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,volume,m3,Both,9.118059598283878,9.118059598283878,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -025eb80f65d745b799511b78ac2f82fb,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -025eb80f65d745b799511b78ac2f82fb,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -025eb80f65d745b799511b78ac2f82fb,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -0872cd39962f43c3b62a6e031a76c24a,area,m2,Both,21.280010175465385,21.280010175465385,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -0872cd39962f43c3b62a6e031a76c24a,volume,m3,Both,57.33913699268221,57.33913699268221,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -0872cd39962f43c3b62a6e031a76c24a,heating.designLoad,W,TasOnly,574.0481567382812,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,heating.peakLoad,W,Both,565.50927734375,1185.0374713731348,619.5281940293848,0.5227920711330089,52.279207113300885,619.5281940293848,Fail,,default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -0872cd39962f43c3b62a6e031a76c24a,heating.unmetHours,h,Both,65,0,65,1,100,-65,Fail,,default,0.05,0.15,1,24,0.5,false -0872cd39962f43c3b62a6e031a76c24a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,cooling.peakLoad,W,OpenStudioOnly,,15.588918884437554,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,cooling.peakHour,hourOfYear,OpenStudioOnly,,7974,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -0872cd39962f43c3b62a6e031a76c24a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -13a0403ab3a543608613b0ce9bf52d92,area,m2,Both,3.647967799712404,3.647967799712404,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -13a0403ab3a543608613b0ce9bf52d92,volume,m3,Both,10.396708229180378,10.396708229180378,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -13a0403ab3a543608613b0ce9bf52d92,heating.designLoad,W,TasOnly,199.83349609375,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,heating.peakLoad,W,Both,199.82528686523438,141.7703965184828,58.05489034675156,0.2905282472378222,29.052824723782223,-58.05489034675156,Fail,,default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,heating.peakHour,hourOfYear,Both,1441,918,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -13a0403ab3a543608613b0ce9bf52d92,heating.unmetHours,h,Both,4761,0,4761,1,100,-4761,Fail,,default,0.05,0.15,1,24,0.5,false -13a0403ab3a543608613b0ce9bf52d92,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -13a0403ab3a543608613b0ce9bf52d92,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -21bdbb806b4f4e2fbad0632b19fdf14b,area,m2,Both,2.9450000000000016,2.9450000000000016,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -21bdbb806b4f4e2fbad0632b19fdf14b,volume,m3,Both,5.746544632499992,5.746544632499992,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.designLoad,W,TasOnly,28.741456985473633,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -21bdbb806b4f4e2fbad0632b19fdf14b,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -312fab2f1ba64756b47ccc3dcb21ac8a,area,m2,Both,2.355387206192353,2.355387206192353,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -312fab2f1ba64756b47ccc3dcb21ac8a,volume,m3,Both,6.712853537648204,6.712853537648204,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.designLoad,W,TasOnly,69.60518646240234,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakLoad,W,Both,52.024513244628906,0,52.024513244628906,1,100,-52.024513244628906,Fail,,default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakHour,hourOfYear,Both,1441,0,1441,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -312fab2f1ba64756b47ccc3dcb21ac8a,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -354b8786b4714f3c9941cec051a93361,area,m2,Both,3.0986077487352315,3.0986077487352315,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -354b8786b4714f3c9941cec051a93361,volume,m3,Both,9.007679927026205,9.007679927026205,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -354b8786b4714f3c9941cec051a93361,heating.designLoad,W,TasOnly,137.82980346679688,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,heating.peakLoad,W,Both,117.870849609375,262.9986126690514,145.1277630596764,0.5518195004408647,55.18195004408647,145.1277630596764,Fail,,default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -354b8786b4714f3c9941cec051a93361,heating.unmetHours,h,Both,67,0,67,1,100,-67,Fail,,default,0.05,0.15,1,24,0.5,false -354b8786b4714f3c9941cec051a93361,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -354b8786b4714f3c9941cec051a93361,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -4377cbd6712349e68fdae8814ff9c5d9,area,m2,Both,5.03787628653,5.03787628653,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -4377cbd6712349e68fdae8814ff9c5d9,volume,m3,Both,7.564547888993382,7.564547888993382,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -4377cbd6712349e68fdae8814ff9c5d9,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -4377cbd6712349e68fdae8814ff9c5d9,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -4377cbd6712349e68fdae8814ff9c5d9,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -6e7ef8487b714da3b730e0f226af927e,area,m2,Both,23.55004650825489,23.55004650825489,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -6e7ef8487b714da3b730e0f226af927e,volume,m3,Both,58.340316505330264,58.340316505330264,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -6e7ef8487b714da3b730e0f226af927e,heating.designLoad,W,TasOnly,379.9020080566406,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,heating.peakLoad,W,Both,132.86868286132812,255.3860902131586,122.51740735183049,0.4797340655850561,47.97340655850561,122.51740735183049,Fail,,default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,heating.peakHour,hourOfYear,Both,1442,919,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -6e7ef8487b714da3b730e0f226af927e,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -6e7ef8487b714da3b730e0f226af927e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -6e7ef8487b714da3b730e0f226af927e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -7ee49aac87b24671bb00ed1b291806a1,area,m2,Both,9.409999999999991,9.409999999999991,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -7ee49aac87b24671bb00ed1b291806a1,volume,m3,Both,26.966204214472434,26.966204214472434,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -7ee49aac87b24671bb00ed1b291806a1,heating.designLoad,W,TasOnly,366.3569641113281,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,heating.peakLoad,W,Both,366.33624267578125,190.23285633880042,176.10338633698083,0.4807151622528315,48.07151622528315,-176.10338633698083,Fail,,default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,heating.peakHour,hourOfYear,Both,1444,919,525,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -7ee49aac87b24671bb00ed1b291806a1,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -7ee49aac87b24671bb00ed1b291806a1,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -7ee49aac87b24671bb00ed1b291806a1,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -8d2d2a0234584fe4b171e5a9373f1b4d,area,m2,Both,4.634866891556,4.634866891556,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -8d2d2a0234584fe4b171e5a9373f1b4d,volume,m3,Both,28.16450759620351,28.16450759620351,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.designLoad,W,TasOnly,69.23214721679688,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakLoad,W,Both,11.025825500488281,39.29050142981706,28.26467592932878,0.7193768188430163,71.93768188430163,28.26467592932878,Fail,,default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakHour,hourOfYear,Both,1452,774,678,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -8d2d2a0234584fe4b171e5a9373f1b4d,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -affc43b0b5b3445f85d5cea8a7b88d4f,area,m2,Both,6.955039759539971,6.955039759539971,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -affc43b0b5b3445f85d5cea8a7b88d4f,volume,m3,Both,20.363019689898447,20.363019689898447,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.designLoad,W,TasOnly,204.9483642578125,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakLoad,W,Both,117.45973205566406,133.44180277588939,15.982070720225323,0.1197680965616646,11.97680965616646,15.982070720225323,Warn,,default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakHour,hourOfYear,Both,1447,774,673,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -affc43b0b5b3445f85d5cea8a7b88d4f,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,area,m2,Both,38.98503142608865,38.98503142608865,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,volume,m3,Both,121.59536233412693,121.59536233412693,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.designLoad,W,TasOnly,1065.076171875,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakLoad,W,Both,1025.7105712890625,1789.9830426085152,764.2724713194527,0.4269719059492821,42.69719059492821,764.2724713194527,Fail,,default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakHour,hourOfYear,Both,1443,775,668,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.unmetHours,h,Both,114,0,114,1,100,-114,Fail,,default,0.05,0.15,1,24,0.5,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -d8294f2a30b445ca9f6ba83c8dcf4903,area,m2,Both,18.080018143643976,18.080018143643976,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d8294f2a30b445ca9f6ba83c8dcf4903,volume,m3,Both,51.52805170938531,51.52805170938531,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.designLoad,W,TasOnly,564.768798828125,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakLoad,W,Both,559.67822265625,936.5975380258114,376.91931536956145,0.4024346638408247,40.243466384082474,376.91931536956145,Fail,,default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakHour,hourOfYear,Both,1441,727,714,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -d8294f2a30b445ca9f6ba83c8dcf4903,heating.unmetHours,h,Both,69,0,69,1,100,-69,Fail,,default,0.05,0.15,1,24,0.5,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakLoad,W,OpenStudioOnly,,28.70645886203079,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakHour,hourOfYear,OpenStudioOnly,,7991,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,area,m2,Both,21.280022127733165,21.280022127733165,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,volume,m3,Both,57.3391691981517,57.3391691981517,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.designLoad,W,TasOnly,549.6607055664062,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakLoad,W,Both,529.828857421875,989.0652526225581,459.2363952006831,0.4643135465359781,46.431354653597815,459.2363952006831,Fail,,default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakHour,hourOfYear,Both,1457,919,538,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.unmetHours,h,Both,29,0,29,1,100,-29,Fail,,default,0.05,0.15,1,24,0.5,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.md b/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.md deleted file mode 100644 index e7771d3..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-provenance-fixed/comparison.md +++ /dev/null @@ -1,292 +0,0 @@ -# Benchmark comparison: TAS vs OpenStudio - -Independent comparison of two engine-neutral benchmark documents. Tolerance bands are -**provisional reporting buckets**, not validated thresholds. The overall gate is the worst of -the numerical, coverage, provenance and reconciliation statuses. Peak-hour bands and the two -whole-model peak-load bands are informational and are excluded from the numerical status — they are -still calculated and shown, and rows carrying such a band are marked in the Note column; -reconciliation is a separate within-document diagnostic status that also contributes to the -overall gate. - -## Summary - -| Field | Value | -| --- | --- | -| Tolerance profile | default | -| Gate status (overall) | Fail | -| Numerical status | Fail | -| Coverage status | Warn | -| Provenance status | Pass | -| Reconciliation status | Pass | -| Metrics matched | 48 | -| Metrics warned | 1 | -| Metrics failed | 33 | -| Metrics not applicable | 66 | -| Required metrics compared | 82 of 148 | -| Required metrics unavailable | 66 | -| TAS schema version | 1.0.0 | -| OpenStudio schema version | 1.0.0 | - -## Coverage - -How much of the two runs was actually compared. Required metrics are the whole-model metrics plus -the metrics of uniquely matched spaces; the metrics of one-sided, duplicated or ambiguous spaces are -not counted here because those spaces are reported under space alignment instead. An unavailable -metric is never a numerical failure, so it is recorded here — otherwise missing results could read as -a clean pass. - -| Field | Value | -| --- | --- | -| Coverage status | Warn | -| Required metrics | 148 | -| Compared | 82 | -| Unavailable (not compared) | 66 | -| Unmatched spaces | 0 | -| Ambiguous (split) space names | 0 | -| Duplicated space identities | 0 | - -**Coverage is INCOMPLETE**, so this comparison must not be described as a complete result: - -- 66 of 148 required metrics were unavailable on at least one side (reported as N/A, never as a numerical failure). - -## Provenance compatibility - -Compatible: both runs share the same canonical model, weather and design-day basis, with the expected engine and route on each side. - -## Tolerance profile - -Provisional reporting bands (not validated thresholds). Recorded so a changed profile is never hidden behind an unchanged name. - -| Setting | Value | -| --- | --- | -| Name | default | -| Warn (relative) | 0.05 | -| Fail (relative) | 0.15 | -| Peak-hour warn (h) | 1 | -| Peak-hour fail (h) | 24 | -| Default near-zero floor | 0 | -| Near-zero floor (kWh) | 1 | -| Near-zero floor (Wh) | 1000 | -| Near-zero floor (kW) | 0.01 | -| Near-zero floor (W) | 10 | -| Near-zero floor (m2) | 0.01 | -| Near-zero floor (m3) | 0.01 | -| Near-zero floor (h) | 0.5 | - -## Provenance - -| Field | TAS | OpenStudio | -| --- | --- | --- | -| Engine kind | TAS | OpenStudio | -| Engine name | Tas | EnergyPlus | -| Engine version | 9.5.7.0 | 25.1.0 | -| SDK version | | 3.10.0 | -| Route | Native-TAS | Native-OpenStudio | -| Weather | Budapest | Budapest | -| Design-day source | EmbeddedModel | EmbeddedModel | -| Run state | Success | Success | -| Source model | 000000_SAM_AnalyticalModel | 000000_SAM_AnalyticalModel | -| Source model GUID | a527e890fceb465fbb50f4b6874f3a4c | a527e890fceb465fbb50f4b6874f3a4c | -| SAM commit | abcd617887f6792047023820f3efd2463fc5c6b3 | abcd617887f6792047023820f3efd2463fc5c6b3 | - -## Model metrics - -> **Caveats (apply even when values pass):** whole-model peak-load semantics differ between -> engines — OpenStudio reports a coincident total, TAS a building-profile maximum — so peak-load -> comparisons are informational, not equivalence claims. `peakHeatingLoad` and `peakCoolingLoad` -> are therefore banded and shown below but **excluded from the numerical status**, exactly like the -> peak-hour metrics; promoting them into the gate needs the B4 corpus and energy-modeller review. -> Every other metric — annual energy, per-space peak loads, unmet hours, geometry — gates normally. -> The primary conditioning pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing) -> is a **candidate** pending validation. A tolerance profile cannot remove these limitations. - -| Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | -| --- | --- | --- | --- | --- | --- | --- | --- | -| consumptionHeating | kWh | 4331.961588091359 | 1751.1764825992748 | 2580.785105492084 | 59.57543835537944 | Fail | | -| consumptionCooling | kWh | 0 | 5.2499930426550705 | 5.2499930426550705 | 100 | Fail | | -| peakHeatingLoad | kW | 3.93393212890625 | 5.720615732872154 | 1.7866836039659035 | 31.23236531513824 | Fail | informational (excluded from the numerical status) | -| peakHeatingHour | hourOfYear | 919 | 775 | 144 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| peakCoolingLoad | kW | 0 | 0.0402250938503556 | 0.0402250938503556 | 100 | Fail | informational (excluded from the numerical status) | -| peakCoolingHour | hourOfYear | 0 | 7974 | 786 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| floorArea | m2 | 167.95195245742798 | 167.95195207345205 | 3.839759301627055E-07 | 2.2862248669602975E-07 | Match | | -| volume | m3 | 470.1821608543396 | 470.1821620538829 | 1.1995433055744797E-06 | 2.5512309959496334E-07 | Match | | - -## Model-total vs sum-of-spaces reconciliation - -Additive quantities only; non-additive peak loads are not reconciled. Only uniquely-matched spaces are summed; unmatched or ambiguous spaces make a reconciliation incomplete. - -| Engine | Quantity | Unit | Model total | Sum of spaces | Abs diff | Band | Used/Missing/Excluded | Complete | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | -| TAS | floorArea | m2 | 167.95195245742798 | 167.95195207345202 | 3.839759585844149E-07 | Match | 14/0/0 | yes | -| TAS | volume | m3 | 470.1821608543396 | 470.1821620538828 | 1.199543191887642E-06 | Match | 14/0/0 | yes | -| OpenStudio | floorArea | m2 | 167.95195207345205 | 167.95195207345202 | 2.842170943040401E-14 | Match | 14/0/0 | yes | -| OpenStudio | volume | m3 | 470.1821620538829 | 470.1821620538828 | 1.1368683772161603E-13 | Match | 14/0/0 | yes | - -## Space alignment - -| Field | Value | -| --- | --- | -| TAS spaces | 14 | -| OpenStudio spaces | 14 | -| Matched by GUID | 14 | -| Matched by name | 0 | - -**Only in TAS:** none - -**Only in OpenStudio:** none - -**Duplicate TAS GUIDs:** none - -**Duplicate OpenStudio GUIDs:** none - -**Duplicate TAS names:** none - -**Duplicate OpenStudio names:** none - -**Ambiguous (split) name matches:** none - -## Space metrics - -| Space | Match | Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 025eb80f65d745b799511b78ac2f82fb | Guid | area | m2 | 6.6920779999999995 | 6.6920779999999995 | 0 | 0 | Match | | -| 025eb80f65d745b799511b78ac2f82fb | Guid | volume | m3 | 9.118059598283878 | 9.118059598283878 | 0 | 0 | Match | | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | area | m2 | 21.280010175465385 | 21.280010175465385 | 0 | 0 | Match | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | volume | m3 | 57.33913699268221 | 57.33913699268221 | 0 | 0 | Match | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.designLoad | W | 574.0481567382812 | | | | N/A | unavailable (TasOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakLoad | W | 565.50927734375 | 1185.0374713731348 | 619.5281940293848 | 52.279207113300885 | Fail | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.unmetHours | h | 65 | 0 | 65 | 100 | Fail | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakLoad | W | | 15.588918884437554 | | | N/A | unavailable (OpenStudioOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakHour | hourOfYear | | 7974 | | | N/A | unavailable (OpenStudioOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | area | m2 | 3.647967799712404 | 3.647967799712404 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | volume | m3 | 10.396708229180378 | 10.396708229180378 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.designLoad | W | 199.83349609375 | | | | N/A | unavailable (TasOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakLoad | W | 199.82528686523438 | 141.7703965184828 | 58.05489034675156 | 29.052824723782223 | Fail | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakHour | hourOfYear | 1441 | 918 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.unmetHours | h | 4761 | 0 | 4761 | 100 | Fail | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | area | m2 | 2.9450000000000016 | 2.9450000000000016 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | volume | m3 | 5.746544632499992 | 5.746544632499992 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.designLoad | W | 28.741456985473633 | | | | N/A | unavailable (TasOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | area | m2 | 2.355387206192353 | 2.355387206192353 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | volume | m3 | 6.712853537648204 | 6.712853537648204 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.designLoad | W | 69.60518646240234 | | | | N/A | unavailable (TasOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakLoad | W | 52.024513244628906 | 0 | 52.024513244628906 | 100 | Fail | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakHour | hourOfYear | 1441 | 0 | 1441 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | area | m2 | 3.0986077487352315 | 3.0986077487352315 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | volume | m3 | 9.007679927026205 | 9.007679927026205 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.designLoad | W | 137.82980346679688 | | | | N/A | unavailable (TasOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakLoad | W | 117.870849609375 | 262.9986126690514 | 145.1277630596764 | 55.18195004408647 | Fail | | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.unmetHours | h | 67 | 0 | 67 | 100 | Fail | | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | area | m2 | 5.03787628653 | 5.03787628653 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | volume | m3 | 7.564547888993382 | 7.564547888993382 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | area | m2 | 23.55004650825489 | 23.55004650825489 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | volume | m3 | 58.340316505330264 | 58.340316505330264 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.designLoad | W | 379.9020080566406 | | | | N/A | unavailable (TasOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakLoad | W | 132.86868286132812 | 255.3860902131586 | 122.51740735183049 | 47.97340655850561 | Fail | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakHour | hourOfYear | 1442 | 919 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | area | m2 | 9.409999999999991 | 9.409999999999991 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | volume | m3 | 26.966204214472434 | 26.966204214472434 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.designLoad | W | 366.3569641113281 | | | | N/A | unavailable (TasOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakLoad | W | 366.33624267578125 | 190.23285633880042 | 176.10338633698083 | 48.07151622528315 | Fail | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakHour | hourOfYear | 1444 | 919 | 525 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | area | m2 | 4.634866891556 | 4.634866891556 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | volume | m3 | 28.16450759620351 | 28.16450759620351 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.designLoad | W | 69.23214721679688 | | | | N/A | unavailable (TasOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakLoad | W | 11.025825500488281 | 39.29050142981706 | 28.26467592932878 | 71.93768188430163 | Fail | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakHour | hourOfYear | 1452 | 774 | 678 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | area | m2 | 6.955039759539971 | 6.955039759539971 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | volume | m3 | 20.363019689898447 | 20.363019689898447 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.designLoad | W | 204.9483642578125 | | | | N/A | unavailable (TasOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakLoad | W | 117.45973205566406 | 133.44180277588939 | 15.982070720225323 | 11.97680965616646 | Warn | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakHour | hourOfYear | 1447 | 774 | 673 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | area | m2 | 38.98503142608865 | 38.98503142608865 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | volume | m3 | 121.59536233412693 | 121.59536233412693 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.designLoad | W | 1065.076171875 | | | | N/A | unavailable (TasOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakLoad | W | 1025.7105712890625 | 1789.9830426085152 | 764.2724713194527 | 42.69719059492821 | Fail | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakHour | hourOfYear | 1443 | 775 | 668 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.unmetHours | h | 114 | 0 | 114 | 100 | Fail | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | area | m2 | 18.080018143643976 | 18.080018143643976 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | volume | m3 | 51.52805170938531 | 51.52805170938531 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.designLoad | W | 564.768798828125 | | | | N/A | unavailable (TasOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakLoad | W | 559.67822265625 | 936.5975380258114 | 376.91931536956145 | 40.243466384082474 | Fail | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakHour | hourOfYear | 1441 | 727 | 714 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.unmetHours | h | 69 | 0 | 69 | 100 | Fail | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakLoad | W | | 28.70645886203079 | | | N/A | unavailable (OpenStudioOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakHour | hourOfYear | | 7991 | | | N/A | unavailable (OpenStudioOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | area | m2 | 21.280022127733165 | 21.280022127733165 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | volume | m3 | 57.3391691981517 | 57.3391691981517 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.designLoad | W | 549.6607055664062 | | | | N/A | unavailable (TasOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakLoad | W | 529.828857421875 | 989.0652526225581 | 459.2363952006831 | 46.431354653597815 | Fail | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakHour | hourOfYear | 1457 | 919 | 538 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.unmetHours | h | 29 | 0 | 29 | 100 | Fail | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | - diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/README.md b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/README.md deleted file mode 100644 index 724345a..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/README.md +++ /dev/null @@ -1,118 +0,0 @@ -# B3 — first real cross-engine comparison (same model, same weather) - -This records the first end-to-end run of the **B3 comparator** over two real engine documents -produced from the **same source model and the same weather file** — the numeric pairing that the B2 -/ D10 checkpoint deferred (it had to use a geographically mismatched EPW). - -It is a **mechanical validation of the comparator**, not a validated engine-equivalence claim. The -tolerance bands remain provisional reporting buckets. - -## Run - -- **Date:** 2026-07-25 -- **Machine:** licensed EDSL Tas install + OpenStudio/EnergyPlus on the same workstation. -- **Model:** `HungaryHouse-WeahterDDY.sam` — real Revit-derived SAM `AnalyticalModel`, 14 spaces - (conditioned rooms + unconditioned voids), carrying embedded design days. -- **Weather:** `HUN_CEN_Budapest.Met.Center.128400_TMYx.epw` — geographically correct for this model - (the D10 checkpoint used a Boston EPW purely to exercise the pipeline). -- **Engines:** Tas `9.5.7.0`; EnergyPlus `25.1.0`. -- **Commands** (both producers, then the comparator): - - ``` - benchmark-tas --model HungaryHouse.sam --weather Budapest.epw --tbd hungary-budapest.tbd \ - --out benchmark-TAS.json --sam-commit --runner-commit - benchmark-openstudio --model HungaryHouse.sam --weather Budapest.epw \ - --out benchmark-OpenStudio.json --work \ - --sam-commit --runner-commit - benchmark-compare --tas benchmark-TAS.json --openstudio benchmark-OpenStudio.json \ - --out report --tolerance-profile default - ``` - -- **Result:** both producers exited `0` with `state = Success`; the comparator exited `0` and wrote - all three reports. Durations: TAS ≈ 58 s, OpenStudio ≈ 21 s. -- **Note:** the two neutral input documents are the verbatim producer outputs of that run. The three - comparison reports were **regenerated from those same committed inputs** after three comparator - corrections landed in this PR (unavailable required metrics now count towards coverage; peak-hour - differences now use the profile's absolute fail threshold; the two whole-model peak loads are now - designated informational and excluded from the numerical status). The simulations were not re-run, - and no input byte changed — only the comparator's classification of the same data. - -## What the comparator got right (its acceptance evidence) - -| Check | Outcome | -|---|---| -| Space alignment | **14/14 matched by GUID**, 0 by name, none one-sided, none ambiguous | -| Geometry agreement | floor area `167.95195245742798` vs `167.95195207345205` m² → rel. diff **2.3e-7**, `Match` | -| Reconciliation | model total vs sum of matched spaces **complete (14/0/0)** and `Match` for both engines, both quantities | -| Circular peak hour | TAS hour `0` vs OpenStudio `7974` correctly reported as **786 h** apart (wrapped), not 7974 | -| Unavailable ⇒ N/A | 66 metrics N/A, none contributing a numerical failure (e.g. OpenStudio emits no per-space *design* loads — a documented route limitation, so every `heating.designLoad` is `TasOnly`) | -| Coverage counted honestly | only **82 of 148** required metrics were comparable, so coverage is `Warn`: perfect space alignment does not let 66 missing measurements read as a complete result | -| Determinism | reports written LF / UTF-8 no BOM, full-precision values | - -The near-identical geometry is the strongest signal that the whole chain is sound: two independent -engines, two independent producers, one neutral schema, and the areas/volumes agree to seven -significant figures. - -## Finding 1 — the provenance gate caught a real producer defect - -The comparator reported **provenance INCOMPATIBLE** on a single field: - -| Field | TAS | OpenStudio | -|---|---|---| -| `designDaySource` | `EmbeddedModel` | `None` | - -This is **not** a genuine input difference — both runs used the identical model, and the OpenStudio -run demonstrably did construct design days. The OpenStudio producer **hardcodes** -`DesignDaySource.None` (`SAM_OpenStudio/benchmark/SAM.Analytical.OpenStudio.Benchmark/Program.cs:86`) -instead of deriving it as the TAS producer does. - -So the provenance gate did exactly the job it was added for: it refused to bless a comparison whose -declared inputs did not line up, and pointed at the responsible field. **Fix belongs in -SAM_OpenStudio** (separate repo, separate PR) — tracked as a follow-up; nothing to change in B3. - -## Finding 2 — the candidate conditioning pairing is NOT yet numerically equivalent - -With the correct weather and an aligned model, the whole-model numbers still diverge well beyond the -provisional fail band: - -| Metric | TAS | OpenStudio | Rel. diff | Band | Gates? | -|---|---:|---:|---:|---|---| -| Annual heating (kWh) | 4331.96 | 1751.18 | **59.6 %** | Fail | **yes** | -| Annual cooling (kWh) | 0 | 5.25 | 100 % | Fail | **yes** | -| Peak heating load (kW) | 3.934 | 5.721 | **31.2 %** | Fail | no — designated informational | -| Peak cooling load (kW) | 0 | 0.040 | 100 % | Fail | no — designated informational | -| Peak heating hour | 919 | 775 | 144 h | Fail | no — `hourOfYear` | -| Floor area / volume | — | — | ~2e-7 | Match | yes | - -The whole-model peak loads and peak hours are banded and shown but excluded from the numerical status -(TOLERANCES.md → Informational metrics), so they are *not* what fails this comparison. The numerical -status is `Fail` on the gating metrics alone — annual heating and cooling, plus per-space peak loads -and unmet hours — which is why narrowing the exclusion did not rescue the result. - -Per-space heating peaks diverge similarly (sampled: 29 %, 52 %). Note TAS reports a *measured zero* -for cooling on this heating-dominated model, so the cooling rows compare 0 against a near-zero -OpenStudio value — the 100 % figures there are not meaningful magnitudes. - -**Conclusion:** the D10 candidate pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD -sizing) is **not** validated by this run and must remain labelled a *candidate*. The design-day -reporting defect above is a plausible contributor (a differing sizing basis changes loads), so the -pairing should be re-assessed only after that fix, and then investigated properly against the -B4 corpus — not by tuning tolerances. - -Two further observations worth carrying into that work: - -- TAS emits `peakCoolingHour = 0` with `available: true` while its cooling load is a measured zero; - a peak *hour* for a zero load carries no information and would be better reported unavailable - (a B2 producer-semantics question, not a comparator one). -- One space reported `heating.peakLoad` as available `0` from OpenStudio while TAS reported it - unavailable, i.e. the two engines disagree about which spaces are conditioned. - -## Status - -- **B3 comparator: validated on real two-engine data.** Alignment, banding, circular hours, - reconciliation, availability handling, determinism and the provenance gate all behaved as specified. -- **Engine equivalence: open.** Deliberately out of B3's scope; it needs the producer fix, then the - B4 corpus and an energy-modeller review. - -Artefacts in this folder are the verbatim outputs of the run above: the two neutral input documents -and the three generated reports. diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-OpenStudio.json b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-OpenStudio.json deleted file mode 100644 index b8f336d..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-OpenStudio.json +++ /dev/null @@ -1,888 +0,0 @@ -{ - "schemaVersion": "1.0.0", - "provenance": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "3de8a4c7793f96d2118513bfb4fb42349a89ca1f", - "engine": { - "kind": "OpenStudio", - "name": "EnergyPlus", - "version": "25.1.0", - "sdkVersion": "3.10.0" - }, - "route": "Native-OpenStudio", - "weather": { - "identity": "Budapest", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "None", - "runTimestampUtc": "2026-07-25T08:10:07.0886114Z", - "durationSeconds": 21.1734065, - "state": "Success", - "resultSources": [ - "SAM.Analytical.OpenStudio" - ], - "warnings": [], - "notes": [] - }, - "model": { - "consumptionHeating": { - "value": 1751.1764825992748, - "unit": "kWh", - "available": true - }, - "consumptionCooling": { - "value": 5.2499930426550705, - "unit": "kWh", - "available": true - }, - "peakHeatingLoad": { - "value": 5.720615732872154, - "unit": "kW", - "available": true - }, - "peakHeatingHour": { - "value": 775, - "unit": "hourOfYear", - "available": true - }, - "peakCoolingLoad": { - "value": 0.0402250938503556, - "unit": "kW", - "available": true - }, - "peakCoolingHour": { - "value": 7974, - "unit": "hourOfYear", - "available": true - }, - "floorArea": { - "value": 167.95195207345205, - "unit": "m2", - "available": true - }, - "volume": { - "value": 470.1821620538829, - "unit": "m3", - "available": true - } - }, - "spaces": [ - { - "guid": "025eb80f65d745b799511b78ac2f82fb", - "name": "01_18 Void", - "area": { - "value": 6.6920779999999995, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.118059598283878, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - } - }, - { - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "name": "01_13 Bedroom", - "area": { - "value": 21.280010175465385, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.33913699268221, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 1185.0374713731348, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 15.588918884437554, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 7974, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "name": "GF_08 Bathroom", - "area": { - "value": 3.647967799712404, - "unit": "m2", - "available": true - }, - "volume": { - "value": 10.396708229180378, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 141.7703965184828, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 918, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "name": "01_15 Plantroom", - "area": { - "value": 2.9450000000000016, - "unit": "m2", - "available": true - }, - "volume": { - "value": 5.746544632499992, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "name": "GF_09 WC", - "area": { - "value": 2.355387206192353, - "unit": "m2", - "available": true - }, - "volume": { - "value": 6.712853537648204, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "354b8786b4714f3c9941cec051a93361", - "name": "GF_10 Closet", - "area": { - "value": 3.0986077487352315, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.007679927026205, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 262.9986126690514, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "name": "01_11 Void", - "area": { - "value": 5.03787628653, - "unit": "m2", - "available": true - }, - "volume": { - "value": 7.564547888993382, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - } - }, - { - "guid": "6e7ef8487b714da3b730e0f226af927e", - "name": "01_17 Gallery", - "area": { - "value": 23.55004650825489, - "unit": "m2", - "available": true - }, - "volume": { - "value": 58.340316505330264, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 255.3860902131586, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "name": "01_16 Bathroom", - "area": { - "value": 9.409999999999991, - "unit": "m2", - "available": true - }, - "volume": { - "value": 26.966204214472434, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 190.23285633880042, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "name": "GF_05 Stairs", - "area": { - "value": 4.634866891556, - "unit": "m2", - "available": true - }, - "volume": { - "value": 28.16450759620351, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 39.29050142981706, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 774, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "name": "GF_12 EntranceHall", - "area": { - "value": 6.955039759539971, - "unit": "m2", - "available": true - }, - "volume": { - "value": 20.363019689898447, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 133.44180277588939, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 774, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "name": "GF_07 LivingRoom", - "area": { - "value": 38.98503142608865, - "unit": "m2", - "available": true - }, - "volume": { - "value": 121.59536233412693, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 1789.9830426085152, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 775, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "name": "GF_11 Bedroom", - "area": { - "value": 18.080018143643976, - "unit": "m2", - "available": true - }, - "volume": { - "value": 51.52805170938531, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 936.5975380258114, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 727, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 28.70645886203079, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 7991, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "name": "01_14 Bedroom", - "area": { - "value": 21.280022127733165, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.3391691981517, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 989.0652526225581, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - } - ] -} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-TAS.json b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-TAS.json deleted file mode 100644 index eda7055..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/benchmark-TAS.json +++ /dev/null @@ -1,890 +0,0 @@ -{ - "schemaVersion": "1.0.0", - "provenance": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", - "engine": { - "kind": "TAS", - "name": "Tas", - "version": "9.5.7.0", - "sdkVersion": null - }, - "route": "Native-TAS", - "weather": { - "identity": "Budapest", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-25T08:08:24.9884006Z", - "durationSeconds": 58.4307425, - "state": "Success", - "resultSources": [ - "SAM.Analytical.Tas" - ], - "warnings": [], - "notes": [ - "Shared gbXML was exported from the source model (no --gbxml supplied)." - ] - }, - "model": { - "consumptionHeating": { - "value": 4331.961588091359, - "unit": "kWh", - "available": true - }, - "consumptionCooling": { - "value": 0, - "unit": "kWh", - "available": true - }, - "peakHeatingLoad": { - "value": 3.93393212890625, - "unit": "kW", - "available": true - }, - "peakHeatingHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "peakCoolingLoad": { - "value": 0, - "unit": "kW", - "available": true - }, - "peakCoolingHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "floorArea": { - "value": 167.95195245742798, - "unit": "m2", - "available": true - }, - "volume": { - "value": 470.1821608543396, - "unit": "m3", - "available": true - } - }, - "spaces": [ - { - "guid": "025eb80f65d745b799511b78ac2f82fb", - "name": "01_18 Void", - "area": { - "value": 6.6920779999999995, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.118059598283878, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "name": "01_13 Bedroom", - "area": { - "value": 21.280010175465385, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.33913699268221, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 574.0481567382812, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 565.50927734375, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 65, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "name": "GF_08 Bathroom", - "area": { - "value": 3.647967799712404, - "unit": "m2", - "available": true - }, - "volume": { - "value": 10.396708229180378, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 199.83349609375, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 199.82528686523438, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 4761, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "name": "01_15 Plantroom", - "area": { - "value": 2.9450000000000016, - "unit": "m2", - "available": true - }, - "volume": { - "value": 5.746544632499992, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 28.741456985473633, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "name": "GF_09 WC", - "area": { - "value": 2.355387206192353, - "unit": "m2", - "available": true - }, - "volume": { - "value": 6.712853537648204, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 69.60518646240234, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 52.024513244628906, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "354b8786b4714f3c9941cec051a93361", - "name": "GF_10 Closet", - "area": { - "value": 3.0986077487352315, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.007679927026205, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 137.82980346679688, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 117.870849609375, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 67, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "name": "01_11 Void", - "area": { - "value": 5.03787628653, - "unit": "m2", - "available": true - }, - "volume": { - "value": 7.564547888993382, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "6e7ef8487b714da3b730e0f226af927e", - "name": "01_17 Gallery", - "area": { - "value": 23.55004650825489, - "unit": "m2", - "available": true - }, - "volume": { - "value": 58.340316505330264, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 379.9020080566406, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 132.86868286132812, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1442, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "name": "01_16 Bathroom", - "area": { - "value": 9.409999999999991, - "unit": "m2", - "available": true - }, - "volume": { - "value": 26.966204214472434, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 366.3569641113281, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 366.33624267578125, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1444, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "name": "GF_05 Stairs", - "area": { - "value": 4.634866891556, - "unit": "m2", - "available": true - }, - "volume": { - "value": 28.16450759620351, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 69.23214721679688, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 11.025825500488281, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1452, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "name": "GF_12 EntranceHall", - "area": { - "value": 6.955039759539971, - "unit": "m2", - "available": true - }, - "volume": { - "value": 20.363019689898447, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 204.9483642578125, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 117.45973205566406, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1447, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "name": "GF_07 LivingRoom", - "area": { - "value": 38.98503142608865, - "unit": "m2", - "available": true - }, - "volume": { - "value": 121.59536233412693, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 1065.076171875, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 1025.7105712890625, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1443, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 114, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "name": "GF_11 Bedroom", - "area": { - "value": 18.080018143643976, - "unit": "m2", - "available": true - }, - "volume": { - "value": 51.52805170938531, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 564.768798828125, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 559.67822265625, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 69, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "name": "01_14 Bedroom", - "area": { - "value": 21.280022127733165, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.3391691981517, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 549.6607055664062, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 529.828857421875, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1457, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 29, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - } - ] -} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison-summary.json b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison-summary.json deleted file mode 100644 index 5c4980e..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison-summary.json +++ /dev/null @@ -1,3734 +0,0 @@ -{ - "summarySchemaVersion": "1.0.0", - "toleranceProfile": "default", - "toleranceProfileDetail": { - "name": "default", - "warnRelative": 0.05, - "failRelative": 0.15, - "hourWarnAbsolute": 1, - "hourFailAbsolute": 24, - "defaultNearZeroFloor": 0, - "nearZeroFloors": { - "kWh": 1, - "Wh": 1000, - "kW": 0.01, - "W": 10, - "m2": 0.01, - "m3": 0.01, - "h": 0.5 - } - }, - "gate": "Fail", - "statuses": { - "overall": "Fail", - "numerical": "Fail", - "coverage": "Warn", - "provenance": "Fail", - "reconciliation": "Pass" - }, - "provenanceCompatibility": { - "compatible": false, - "mismatches": [ - { - "field": "designDaySource", - "tas": "EmbeddedModel", - "openStudio": "None" - } - ] - }, - "counts": { - "match": 48, - "warn": 1, - "fail": 33, - "notApplicable": 66 - }, - "coverage": { - "status": "Warn", - "requiredMetricCount": 148, - "comparableMetricCount": 82, - "unavailableRequiredMetricCount": 66, - "unmatchedSpaceCount": 0, - "ambiguousSpaceCount": 0, - "duplicateSpaceIdentityCount": 0 - }, - "schema": { - "tas": "1.0.0", - "openStudio": "1.0.0", - "driftNote": null - }, - "provenance": { - "tas": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", - "engine": { - "kind": "TAS", - "name": "Tas", - "version": "9.5.7.0", - "sdkVersion": null - }, - "route": "Native-TAS", - "weather": { - "identity": "Budapest", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-25T08:08:24.9884006Z", - "durationSeconds": 58.4307425, - "state": "Success", - "resultSources": [ - "SAM.Analytical.Tas" - ], - "warnings": [], - "notes": [ - "Shared gbXML was exported from the source model (no --gbxml supplied)." - ] - }, - "openStudio": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "3de8a4c7793f96d2118513bfb4fb42349a89ca1f", - "engine": { - "kind": "OpenStudio", - "name": "EnergyPlus", - "version": "25.1.0", - "sdkVersion": "3.10.0" - }, - "route": "Native-OpenStudio", - "weather": { - "identity": "Budapest", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "None", - "runTimestampUtc": "2026-07-25T08:10:07.0886114Z", - "durationSeconds": 21.1734065, - "state": "Success", - "resultSources": [ - "SAM.Analytical.OpenStudio" - ], - "warnings": [], - "notes": [] - } - }, - "modelMetrics": [ - { - "scope": "model", - "key": "consumptionHeating", - "unit": "kWh", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4331.961588091359, - "available": true, - "unit": "kWh" - }, - "openStudio": { - "value": 1751.1764825992748, - "available": true, - "unit": "kWh" - }, - "absoluteDifference": 2580.785105492084, - "relativeDifference": 0.5957543835537944, - "signedDifference": -2580.785105492084 - }, - { - "scope": "model", - "key": "consumptionCooling", - "unit": "kWh", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "kWh" - }, - "openStudio": { - "value": 5.2499930426550705, - "available": true, - "unit": "kWh" - }, - "absoluteDifference": 5.2499930426550705, - "relativeDifference": 1, - "signedDifference": 5.2499930426550705 - }, - { - "scope": "model", - "key": "peakHeatingLoad", - "unit": "kW", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.93393212890625, - "available": true, - "unit": "kW" - }, - "openStudio": { - "value": 5.720615732872154, - "available": true, - "unit": "kW" - }, - "absoluteDifference": 1.7866836039659035, - "relativeDifference": 0.3123236531513824, - "signedDifference": 1.7866836039659035 - }, - { - "scope": "model", - "key": "peakHeatingHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 775, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 144, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "model", - "key": "peakCoolingLoad", - "unit": "kW", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "kW" - }, - "openStudio": { - "value": 0.0402250938503556, - "available": true, - "unit": "kW" - }, - "absoluteDifference": 0.0402250938503556, - "relativeDifference": 1, - "signedDifference": 0.0402250938503556 - }, - { - "scope": "model", - "key": "peakCoolingHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7974, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 786, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "model", - "key": "floorArea", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 167.95195245742798, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 167.95195207345205, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 3.839759301627055E-07, - "relativeDifference": 2.2862248669602973E-09, - "signedDifference": -3.839759301627055E-07 - }, - { - "scope": "model", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 470.1821608543396, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 470.1821620538829, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 1.1995433055744797E-06, - "relativeDifference": 2.5512309959496336E-09, - "signedDifference": 1.1995433055744797E-06 - } - ], - "spaces": [ - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "guid": "025eb80f65d745b799511b78ac2f82fb", - "tasName": "01_18 Void", - "openStudioName": "01_18 Void", - "matchKind": "Guid", - "metrics": [ - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.6920779999999995, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 6.6920779999999995, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.118059598283878, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 9.118059598283878, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - } - ] - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "tasName": "01_13 Bedroom", - "openStudioName": "01_13 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 21.280010175465385, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 21.280010175465385, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 57.33913699268221, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 57.33913699268221, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 574.0481567382812, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 565.50927734375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 1185.0374713731348, - "available": true, - "unit": "W" - }, - "absoluteDifference": 619.5281940293848, - "relativeDifference": 0.5227920711330089, - "signedDifference": 619.5281940293848 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 522, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 65, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 65, - "relativeDifference": 1, - "signedDifference": -65 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 15.588918884437554, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7974, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "tasName": "GF_08 Bathroom", - "openStudioName": "GF_08 Bathroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.647967799712404, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 3.647967799712404, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 10.396708229180378, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 10.396708229180378, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 199.83349609375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 199.82528686523438, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 141.7703965184828, - "available": true, - "unit": "W" - }, - "absoluteDifference": 58.05489034675156, - "relativeDifference": 0.2905282472378222, - "signedDifference": -58.05489034675156 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 918, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 523, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4761, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 4761, - "relativeDifference": 1, - "signedDifference": -4761 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "tasName": "01_15 Plantroom", - "openStudioName": "01_15 Plantroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 2.9450000000000016, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 2.9450000000000016, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 5.746544632499992, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 5.746544632499992, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 28.741456985473633, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "tasName": "GF_09 WC", - "openStudioName": "GF_09 WC", - "matchKind": "Guid", - "metrics": [ - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 2.355387206192353, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 2.355387206192353, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.712853537648204, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 6.712853537648204, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 69.60518646240234, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 52.024513244628906, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": 52.024513244628906, - "relativeDifference": 1, - "signedDifference": -52.024513244628906 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 1441, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "guid": "354b8786b4714f3c9941cec051a93361", - "tasName": "GF_10 Closet", - "openStudioName": "GF_10 Closet", - "matchKind": "Guid", - "metrics": [ - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.0986077487352315, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 3.0986077487352315, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.007679927026205, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 9.007679927026205, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 137.82980346679688, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 117.870849609375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 262.9986126690514, - "available": true, - "unit": "W" - }, - "absoluteDifference": 145.1277630596764, - "relativeDifference": 0.5518195004408647, - "signedDifference": 145.1277630596764 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 522, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 67, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 67, - "relativeDifference": 1, - "signedDifference": -67 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "tasName": "01_11 Void", - "openStudioName": "01_11 Void", - "matchKind": "Guid", - "metrics": [ - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 5.03787628653, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 5.03787628653, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 7.564547888993382, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 7.564547888993382, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - } - ] - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "guid": "6e7ef8487b714da3b730e0f226af927e", - "tasName": "01_17 Gallery", - "openStudioName": "01_17 Gallery", - "matchKind": "Guid", - "metrics": [ - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 23.55004650825489, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 23.55004650825489, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 58.340316505330264, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 58.340316505330264, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 379.9020080566406, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 132.86868286132812, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 255.3860902131586, - "available": true, - "unit": "W" - }, - "absoluteDifference": 122.51740735183049, - "relativeDifference": 0.4797340655850561, - "signedDifference": 122.51740735183049 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1442, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 523, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "tasName": "01_16 Bathroom", - "openStudioName": "01_16 Bathroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.409999999999991, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 9.409999999999991, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 26.966204214472434, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 26.966204214472434, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 366.3569641113281, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 366.33624267578125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 190.23285633880042, - "available": true, - "unit": "W" - }, - "absoluteDifference": 176.10338633698083, - "relativeDifference": 0.4807151622528315, - "signedDifference": -176.10338633698083 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1444, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 525, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "tasName": "GF_05 Stairs", - "openStudioName": "GF_05 Stairs", - "matchKind": "Guid", - "metrics": [ - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4.634866891556, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 4.634866891556, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 28.16450759620351, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 28.16450759620351, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 69.23214721679688, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 11.025825500488281, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 39.29050142981706, - "available": true, - "unit": "W" - }, - "absoluteDifference": 28.26467592932878, - "relativeDifference": 0.7193768188430163, - "signedDifference": 28.26467592932878 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1452, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 774, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 678, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "tasName": "GF_12 EntranceHall", - "openStudioName": "GF_12 EntranceHall", - "matchKind": "Guid", - "metrics": [ - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.955039759539971, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 6.955039759539971, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 20.363019689898447, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 20.363019689898447, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 204.9483642578125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Warn", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 117.45973205566406, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 133.44180277588939, - "available": true, - "unit": "W" - }, - "absoluteDifference": 15.982070720225323, - "relativeDifference": 0.1197680965616646, - "signedDifference": 15.982070720225323 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1447, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 774, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 673, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "tasName": "GF_07 LivingRoom", - "openStudioName": "GF_07 LivingRoom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 38.98503142608865, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 38.98503142608865, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 121.59536233412693, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 121.59536233412693, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 1065.076171875, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 1025.7105712890625, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 1789.9830426085152, - "available": true, - "unit": "W" - }, - "absoluteDifference": 764.2724713194527, - "relativeDifference": 0.4269719059492821, - "signedDifference": 764.2724713194527 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1443, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 775, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 668, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 114, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 114, - "relativeDifference": 1, - "signedDifference": -114 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "tasName": "GF_11 Bedroom", - "openStudioName": "GF_11 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 18.080018143643976, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 18.080018143643976, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 51.52805170938531, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 51.52805170938531, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 564.768798828125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 559.67822265625, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 936.5975380258114, - "available": true, - "unit": "W" - }, - "absoluteDifference": 376.91931536956145, - "relativeDifference": 0.4024346638408247, - "signedDifference": 376.91931536956145 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 727, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 714, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 69, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 69, - "relativeDifference": 1, - "signedDifference": -69 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 28.70645886203079, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7991, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "tasName": "01_14 Bedroom", - "openStudioName": "01_14 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 21.280022127733165, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 21.280022127733165, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 57.3391691981517, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 57.3391691981517, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 549.6607055664062, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 529.828857421875, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 989.0652526225581, - "available": true, - "unit": "W" - }, - "absoluteDifference": 459.2363952006831, - "relativeDifference": 0.4643135465359781, - "signedDifference": 459.2363952006831 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1457, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 538, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 29, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 29, - "relativeDifference": 1, - "signedDifference": -29 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - } - ], - "spaceDiagnostics": { - "tasCount": 14, - "openStudioCount": 14, - "matchedByGuid": 14, - "matchedByName": 0, - "onlyInTas": [], - "onlyInOpenStudio": [], - "duplicateTasGuids": [], - "duplicateOpenStudioGuids": [], - "duplicateTasNames": [], - "duplicateOpenStudioNames": [], - "ambiguousNameMatches": [] - }, - "reconciliation": [ - { - "engine": "TAS", - "key": "floorArea", - "unit": "m2", - "modelTotal": 167.95195245742798, - "modelTotalAvailable": true, - "sumOfSpaces": 167.95195207345202, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 3.839759585844149E-07, - "relativeDifference": 2.2862250361855374E-09, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "TAS", - "key": "volume", - "unit": "m3", - "modelTotal": 470.1821608543396, - "modelTotalAvailable": true, - "sumOfSpaces": 470.1821620538828, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 1.199543191887642E-06, - "relativeDifference": 2.5512307541564592E-09, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "OpenStudio", - "key": "floorArea", - "unit": "m2", - "modelTotal": 167.95195207345205, - "modelTotalAvailable": true, - "sumOfSpaces": 167.95195207345202, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 2.842170943040401E-14, - "relativeDifference": 1.6922524019235018E-16, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "OpenStudio", - "key": "volume", - "unit": "m3", - "modelTotal": 470.1821620538829, - "modelTotalAvailable": true, - "sumOfSpaces": 470.1821620538828, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 1.1368683772161603E-13, - "relativeDifference": 2.417931748516387E-16, - "band": "Match", - "notApplicableReason": null - } - ] -} diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.csv b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.csv deleted file mode 100644 index 8406556..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.csv +++ /dev/null @@ -1,149 +0,0 @@ -scope,metric,unit,availability,tasValue,openStudioValue,absoluteDifference,relativeDifference,relativePercent,signedDifference,band,note,toleranceProfile,warnRelative,failRelative,hourWarnAbsolute,hourFailAbsolute,nearZeroFloor,informational -model,consumptionHeating,kWh,Both,4331.961588091359,1751.1764825992748,2580.785105492084,0.5957543835537944,59.57543835537944,-2580.785105492084,Fail,,default,0.05,0.15,1,24,1,false -model,consumptionCooling,kWh,Both,0,5.2499930426550705,5.2499930426550705,1,100,5.2499930426550705,Fail,,default,0.05,0.15,1,24,1,false -model,peakHeatingLoad,kW,Both,3.93393212890625,5.720615732872154,1.7866836039659035,0.3123236531513824,31.23236531513824,1.7866836039659035,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true -model,peakHeatingHour,hourOfYear,Both,919,775,144,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -model,peakCoolingLoad,kW,Both,0,0.0402250938503556,0.0402250938503556,1,100,0.0402250938503556,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true -model,peakCoolingHour,hourOfYear,Both,0,7974,786,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -model,floorArea,m2,Both,167.95195245742798,167.95195207345205,3.839759301627055E-07,2.2862248669602973E-09,2.2862248669602975E-07,-3.839759301627055E-07,Match,,default,0.05,0.15,1,24,0.01,false -model,volume,m3,Both,470.1821608543396,470.1821620538829,1.1995433055744797E-06,2.5512309959496336E-09,2.5512309959496334E-07,1.1995433055744797E-06,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,area,m2,Both,6.6920779999999995,6.6920779999999995,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,volume,m3,Both,9.118059598283878,9.118059598283878,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -025eb80f65d745b799511b78ac2f82fb,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -025eb80f65d745b799511b78ac2f82fb,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -025eb80f65d745b799511b78ac2f82fb,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -0872cd39962f43c3b62a6e031a76c24a,area,m2,Both,21.280010175465385,21.280010175465385,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -0872cd39962f43c3b62a6e031a76c24a,volume,m3,Both,57.33913699268221,57.33913699268221,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -0872cd39962f43c3b62a6e031a76c24a,heating.designLoad,W,TasOnly,574.0481567382812,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,heating.peakLoad,W,Both,565.50927734375,1185.0374713731348,619.5281940293848,0.5227920711330089,52.279207113300885,619.5281940293848,Fail,,default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -0872cd39962f43c3b62a6e031a76c24a,heating.unmetHours,h,Both,65,0,65,1,100,-65,Fail,,default,0.05,0.15,1,24,0.5,false -0872cd39962f43c3b62a6e031a76c24a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,cooling.peakLoad,W,OpenStudioOnly,,15.588918884437554,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,cooling.peakHour,hourOfYear,OpenStudioOnly,,7974,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -0872cd39962f43c3b62a6e031a76c24a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -13a0403ab3a543608613b0ce9bf52d92,area,m2,Both,3.647967799712404,3.647967799712404,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -13a0403ab3a543608613b0ce9bf52d92,volume,m3,Both,10.396708229180378,10.396708229180378,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -13a0403ab3a543608613b0ce9bf52d92,heating.designLoad,W,TasOnly,199.83349609375,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,heating.peakLoad,W,Both,199.82528686523438,141.7703965184828,58.05489034675156,0.2905282472378222,29.052824723782223,-58.05489034675156,Fail,,default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,heating.peakHour,hourOfYear,Both,1441,918,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -13a0403ab3a543608613b0ce9bf52d92,heating.unmetHours,h,Both,4761,0,4761,1,100,-4761,Fail,,default,0.05,0.15,1,24,0.5,false -13a0403ab3a543608613b0ce9bf52d92,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -13a0403ab3a543608613b0ce9bf52d92,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -21bdbb806b4f4e2fbad0632b19fdf14b,area,m2,Both,2.9450000000000016,2.9450000000000016,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -21bdbb806b4f4e2fbad0632b19fdf14b,volume,m3,Both,5.746544632499992,5.746544632499992,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.designLoad,W,TasOnly,28.741456985473633,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -21bdbb806b4f4e2fbad0632b19fdf14b,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -312fab2f1ba64756b47ccc3dcb21ac8a,area,m2,Both,2.355387206192353,2.355387206192353,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -312fab2f1ba64756b47ccc3dcb21ac8a,volume,m3,Both,6.712853537648204,6.712853537648204,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.designLoad,W,TasOnly,69.60518646240234,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakLoad,W,Both,52.024513244628906,0,52.024513244628906,1,100,-52.024513244628906,Fail,,default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakHour,hourOfYear,Both,1441,0,1441,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -312fab2f1ba64756b47ccc3dcb21ac8a,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -354b8786b4714f3c9941cec051a93361,area,m2,Both,3.0986077487352315,3.0986077487352315,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -354b8786b4714f3c9941cec051a93361,volume,m3,Both,9.007679927026205,9.007679927026205,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -354b8786b4714f3c9941cec051a93361,heating.designLoad,W,TasOnly,137.82980346679688,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,heating.peakLoad,W,Both,117.870849609375,262.9986126690514,145.1277630596764,0.5518195004408647,55.18195004408647,145.1277630596764,Fail,,default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -354b8786b4714f3c9941cec051a93361,heating.unmetHours,h,Both,67,0,67,1,100,-67,Fail,,default,0.05,0.15,1,24,0.5,false -354b8786b4714f3c9941cec051a93361,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -354b8786b4714f3c9941cec051a93361,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -4377cbd6712349e68fdae8814ff9c5d9,area,m2,Both,5.03787628653,5.03787628653,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -4377cbd6712349e68fdae8814ff9c5d9,volume,m3,Both,7.564547888993382,7.564547888993382,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -4377cbd6712349e68fdae8814ff9c5d9,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -4377cbd6712349e68fdae8814ff9c5d9,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -4377cbd6712349e68fdae8814ff9c5d9,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -6e7ef8487b714da3b730e0f226af927e,area,m2,Both,23.55004650825489,23.55004650825489,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -6e7ef8487b714da3b730e0f226af927e,volume,m3,Both,58.340316505330264,58.340316505330264,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -6e7ef8487b714da3b730e0f226af927e,heating.designLoad,W,TasOnly,379.9020080566406,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,heating.peakLoad,W,Both,132.86868286132812,255.3860902131586,122.51740735183049,0.4797340655850561,47.97340655850561,122.51740735183049,Fail,,default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,heating.peakHour,hourOfYear,Both,1442,919,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -6e7ef8487b714da3b730e0f226af927e,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -6e7ef8487b714da3b730e0f226af927e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -6e7ef8487b714da3b730e0f226af927e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -7ee49aac87b24671bb00ed1b291806a1,area,m2,Both,9.409999999999991,9.409999999999991,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -7ee49aac87b24671bb00ed1b291806a1,volume,m3,Both,26.966204214472434,26.966204214472434,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -7ee49aac87b24671bb00ed1b291806a1,heating.designLoad,W,TasOnly,366.3569641113281,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,heating.peakLoad,W,Both,366.33624267578125,190.23285633880042,176.10338633698083,0.4807151622528315,48.07151622528315,-176.10338633698083,Fail,,default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,heating.peakHour,hourOfYear,Both,1444,919,525,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -7ee49aac87b24671bb00ed1b291806a1,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -7ee49aac87b24671bb00ed1b291806a1,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -7ee49aac87b24671bb00ed1b291806a1,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -8d2d2a0234584fe4b171e5a9373f1b4d,area,m2,Both,4.634866891556,4.634866891556,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -8d2d2a0234584fe4b171e5a9373f1b4d,volume,m3,Both,28.16450759620351,28.16450759620351,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.designLoad,W,TasOnly,69.23214721679688,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakLoad,W,Both,11.025825500488281,39.29050142981706,28.26467592932878,0.7193768188430163,71.93768188430163,28.26467592932878,Fail,,default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakHour,hourOfYear,Both,1452,774,678,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -8d2d2a0234584fe4b171e5a9373f1b4d,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -affc43b0b5b3445f85d5cea8a7b88d4f,area,m2,Both,6.955039759539971,6.955039759539971,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -affc43b0b5b3445f85d5cea8a7b88d4f,volume,m3,Both,20.363019689898447,20.363019689898447,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.designLoad,W,TasOnly,204.9483642578125,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakLoad,W,Both,117.45973205566406,133.44180277588939,15.982070720225323,0.1197680965616646,11.97680965616646,15.982070720225323,Warn,,default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakHour,hourOfYear,Both,1447,774,673,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -affc43b0b5b3445f85d5cea8a7b88d4f,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,area,m2,Both,38.98503142608865,38.98503142608865,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,volume,m3,Both,121.59536233412693,121.59536233412693,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.designLoad,W,TasOnly,1065.076171875,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakLoad,W,Both,1025.7105712890625,1789.9830426085152,764.2724713194527,0.4269719059492821,42.69719059492821,764.2724713194527,Fail,,default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakHour,hourOfYear,Both,1443,775,668,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.unmetHours,h,Both,114,0,114,1,100,-114,Fail,,default,0.05,0.15,1,24,0.5,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -d8294f2a30b445ca9f6ba83c8dcf4903,area,m2,Both,18.080018143643976,18.080018143643976,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d8294f2a30b445ca9f6ba83c8dcf4903,volume,m3,Both,51.52805170938531,51.52805170938531,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.designLoad,W,TasOnly,564.768798828125,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakLoad,W,Both,559.67822265625,936.5975380258114,376.91931536956145,0.4024346638408247,40.243466384082474,376.91931536956145,Fail,,default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakHour,hourOfYear,Both,1441,727,714,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -d8294f2a30b445ca9f6ba83c8dcf4903,heating.unmetHours,h,Both,69,0,69,1,100,-69,Fail,,default,0.05,0.15,1,24,0.5,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakLoad,W,OpenStudioOnly,,28.70645886203079,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakHour,hourOfYear,OpenStudioOnly,,7991,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,area,m2,Both,21.280022127733165,21.280022127733165,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,volume,m3,Both,57.3391691981517,57.3391691981517,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.designLoad,W,TasOnly,549.6607055664062,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakLoad,W,Both,529.828857421875,989.0652526225581,459.2363952006831,0.4643135465359781,46.431354653597815,459.2363952006831,Fail,,default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakHour,hourOfYear,Both,1457,919,538,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.unmetHours,h,Both,29,0,29,1,100,-29,Fail,,default,0.05,0.15,1,24,0.5,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false diff --git a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.md b/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.md deleted file mode 100644 index 2966595..0000000 --- a/docs/benchmark/reports/2026-07-25-cross-engine-smoke/comparison.md +++ /dev/null @@ -1,296 +0,0 @@ -# Benchmark comparison: TAS vs OpenStudio - -Independent comparison of two engine-neutral benchmark documents. Tolerance bands are -**provisional reporting buckets**, not validated thresholds. The overall gate is the worst of -the numerical, coverage, provenance and reconciliation statuses. Peak-hour bands and the two -whole-model peak-load bands are informational and are excluded from the numerical status — they are -still calculated and shown, and rows carrying such a band are marked in the Note column; -reconciliation is a separate within-document diagnostic status that also contributes to the -overall gate. - -## Summary - -| Field | Value | -| --- | --- | -| Tolerance profile | default | -| Gate status (overall) | Fail | -| Numerical status | Fail | -| Coverage status | Warn | -| Provenance status | Fail | -| Reconciliation status | Pass | -| Metrics matched | 48 | -| Metrics warned | 1 | -| Metrics failed | 33 | -| Metrics not applicable | 66 | -| Required metrics compared | 82 of 148 | -| Required metrics unavailable | 66 | -| TAS schema version | 1.0.0 | -| OpenStudio schema version | 1.0.0 | - -## Coverage - -How much of the two runs was actually compared. Required metrics are the whole-model metrics plus -the metrics of uniquely matched spaces; the metrics of one-sided, duplicated or ambiguous spaces are -not counted here because those spaces are reported under space alignment instead. An unavailable -metric is never a numerical failure, so it is recorded here — otherwise missing results could read as -a clean pass. - -| Field | Value | -| --- | --- | -| Coverage status | Warn | -| Required metrics | 148 | -| Compared | 82 | -| Unavailable (not compared) | 66 | -| Unmatched spaces | 0 | -| Ambiguous (split) space names | 0 | -| Duplicated space identities | 0 | - -**Coverage is INCOMPLETE**, so this comparison must not be described as a complete result: - -- 66 of 148 required metrics were unavailable on at least one side (reported as N/A, never as a numerical failure). - -## Provenance compatibility - -**INCOMPATIBLE** — the two documents are not a valid comparison pair, so a numerical pass is not meaningful. - -| Field | TAS | OpenStudio | -| --- | --- | --- | -| designDaySource | EmbeddedModel | None | - -## Tolerance profile - -Provisional reporting bands (not validated thresholds). Recorded so a changed profile is never hidden behind an unchanged name. - -| Setting | Value | -| --- | --- | -| Name | default | -| Warn (relative) | 0.05 | -| Fail (relative) | 0.15 | -| Peak-hour warn (h) | 1 | -| Peak-hour fail (h) | 24 | -| Default near-zero floor | 0 | -| Near-zero floor (kWh) | 1 | -| Near-zero floor (Wh) | 1000 | -| Near-zero floor (kW) | 0.01 | -| Near-zero floor (W) | 10 | -| Near-zero floor (m2) | 0.01 | -| Near-zero floor (m3) | 0.01 | -| Near-zero floor (h) | 0.5 | - -## Provenance - -| Field | TAS | OpenStudio | -| --- | --- | --- | -| Engine kind | TAS | OpenStudio | -| Engine name | Tas | EnergyPlus | -| Engine version | 9.5.7.0 | 25.1.0 | -| SDK version | | 3.10.0 | -| Route | Native-TAS | Native-OpenStudio | -| Weather | Budapest | Budapest | -| Design-day source | EmbeddedModel | None | -| Run state | Success | Success | -| Source model | 000000_SAM_AnalyticalModel | 000000_SAM_AnalyticalModel | -| Source model GUID | a527e890fceb465fbb50f4b6874f3a4c | a527e890fceb465fbb50f4b6874f3a4c | -| SAM commit | abcd617887f6792047023820f3efd2463fc5c6b3 | abcd617887f6792047023820f3efd2463fc5c6b3 | - -## Model metrics - -> **Caveats (apply even when values pass):** whole-model peak-load semantics differ between -> engines — OpenStudio reports a coincident total, TAS a building-profile maximum — so peak-load -> comparisons are informational, not equivalence claims. `peakHeatingLoad` and `peakCoolingLoad` -> are therefore banded and shown below but **excluded from the numerical status**, exactly like the -> peak-hour metrics; promoting them into the gate needs the B4 corpus and energy-modeller review. -> Every other metric — annual energy, per-space peak loads, unmet hours, geometry — gates normally. -> The primary conditioning pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing) -> is a **candidate** pending validation. A tolerance profile cannot remove these limitations. - -| Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | -| --- | --- | --- | --- | --- | --- | --- | --- | -| consumptionHeating | kWh | 4331.961588091359 | 1751.1764825992748 | 2580.785105492084 | 59.57543835537944 | Fail | | -| consumptionCooling | kWh | 0 | 5.2499930426550705 | 5.2499930426550705 | 100 | Fail | | -| peakHeatingLoad | kW | 3.93393212890625 | 5.720615732872154 | 1.7866836039659035 | 31.23236531513824 | Fail | informational (excluded from the numerical status) | -| peakHeatingHour | hourOfYear | 919 | 775 | 144 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| peakCoolingLoad | kW | 0 | 0.0402250938503556 | 0.0402250938503556 | 100 | Fail | informational (excluded from the numerical status) | -| peakCoolingHour | hourOfYear | 0 | 7974 | 786 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| floorArea | m2 | 167.95195245742798 | 167.95195207345205 | 3.839759301627055E-07 | 2.2862248669602975E-07 | Match | | -| volume | m3 | 470.1821608543396 | 470.1821620538829 | 1.1995433055744797E-06 | 2.5512309959496334E-07 | Match | | - -## Model-total vs sum-of-spaces reconciliation - -Additive quantities only; non-additive peak loads are not reconciled. Only uniquely-matched spaces are summed; unmatched or ambiguous spaces make a reconciliation incomplete. - -| Engine | Quantity | Unit | Model total | Sum of spaces | Abs diff | Band | Used/Missing/Excluded | Complete | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | -| TAS | floorArea | m2 | 167.95195245742798 | 167.95195207345202 | 3.839759585844149E-07 | Match | 14/0/0 | yes | -| TAS | volume | m3 | 470.1821608543396 | 470.1821620538828 | 1.199543191887642E-06 | Match | 14/0/0 | yes | -| OpenStudio | floorArea | m2 | 167.95195207345205 | 167.95195207345202 | 2.842170943040401E-14 | Match | 14/0/0 | yes | -| OpenStudio | volume | m3 | 470.1821620538829 | 470.1821620538828 | 1.1368683772161603E-13 | Match | 14/0/0 | yes | - -## Space alignment - -| Field | Value | -| --- | --- | -| TAS spaces | 14 | -| OpenStudio spaces | 14 | -| Matched by GUID | 14 | -| Matched by name | 0 | - -**Only in TAS:** none - -**Only in OpenStudio:** none - -**Duplicate TAS GUIDs:** none - -**Duplicate OpenStudio GUIDs:** none - -**Duplicate TAS names:** none - -**Duplicate OpenStudio names:** none - -**Ambiguous (split) name matches:** none - -## Space metrics - -| Space | Match | Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 025eb80f65d745b799511b78ac2f82fb | Guid | area | m2 | 6.6920779999999995 | 6.6920779999999995 | 0 | 0 | Match | | -| 025eb80f65d745b799511b78ac2f82fb | Guid | volume | m3 | 9.118059598283878 | 9.118059598283878 | 0 | 0 | Match | | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | area | m2 | 21.280010175465385 | 21.280010175465385 | 0 | 0 | Match | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | volume | m3 | 57.33913699268221 | 57.33913699268221 | 0 | 0 | Match | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.designLoad | W | 574.0481567382812 | | | | N/A | unavailable (TasOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakLoad | W | 565.50927734375 | 1185.0374713731348 | 619.5281940293848 | 52.279207113300885 | Fail | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.unmetHours | h | 65 | 0 | 65 | 100 | Fail | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakLoad | W | | 15.588918884437554 | | | N/A | unavailable (OpenStudioOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakHour | hourOfYear | | 7974 | | | N/A | unavailable (OpenStudioOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | area | m2 | 3.647967799712404 | 3.647967799712404 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | volume | m3 | 10.396708229180378 | 10.396708229180378 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.designLoad | W | 199.83349609375 | | | | N/A | unavailable (TasOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakLoad | W | 199.82528686523438 | 141.7703965184828 | 58.05489034675156 | 29.052824723782223 | Fail | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakHour | hourOfYear | 1441 | 918 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.unmetHours | h | 4761 | 0 | 4761 | 100 | Fail | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | area | m2 | 2.9450000000000016 | 2.9450000000000016 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | volume | m3 | 5.746544632499992 | 5.746544632499992 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.designLoad | W | 28.741456985473633 | | | | N/A | unavailable (TasOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | area | m2 | 2.355387206192353 | 2.355387206192353 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | volume | m3 | 6.712853537648204 | 6.712853537648204 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.designLoad | W | 69.60518646240234 | | | | N/A | unavailable (TasOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakLoad | W | 52.024513244628906 | 0 | 52.024513244628906 | 100 | Fail | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakHour | hourOfYear | 1441 | 0 | 1441 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | area | m2 | 3.0986077487352315 | 3.0986077487352315 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | volume | m3 | 9.007679927026205 | 9.007679927026205 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.designLoad | W | 137.82980346679688 | | | | N/A | unavailable (TasOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakLoad | W | 117.870849609375 | 262.9986126690514 | 145.1277630596764 | 55.18195004408647 | Fail | | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.unmetHours | h | 67 | 0 | 67 | 100 | Fail | | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | area | m2 | 5.03787628653 | 5.03787628653 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | volume | m3 | 7.564547888993382 | 7.564547888993382 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | area | m2 | 23.55004650825489 | 23.55004650825489 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | volume | m3 | 58.340316505330264 | 58.340316505330264 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.designLoad | W | 379.9020080566406 | | | | N/A | unavailable (TasOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakLoad | W | 132.86868286132812 | 255.3860902131586 | 122.51740735183049 | 47.97340655850561 | Fail | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakHour | hourOfYear | 1442 | 919 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | area | m2 | 9.409999999999991 | 9.409999999999991 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | volume | m3 | 26.966204214472434 | 26.966204214472434 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.designLoad | W | 366.3569641113281 | | | | N/A | unavailable (TasOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakLoad | W | 366.33624267578125 | 190.23285633880042 | 176.10338633698083 | 48.07151622528315 | Fail | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakHour | hourOfYear | 1444 | 919 | 525 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | area | m2 | 4.634866891556 | 4.634866891556 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | volume | m3 | 28.16450759620351 | 28.16450759620351 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.designLoad | W | 69.23214721679688 | | | | N/A | unavailable (TasOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakLoad | W | 11.025825500488281 | 39.29050142981706 | 28.26467592932878 | 71.93768188430163 | Fail | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakHour | hourOfYear | 1452 | 774 | 678 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | area | m2 | 6.955039759539971 | 6.955039759539971 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | volume | m3 | 20.363019689898447 | 20.363019689898447 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.designLoad | W | 204.9483642578125 | | | | N/A | unavailable (TasOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakLoad | W | 117.45973205566406 | 133.44180277588939 | 15.982070720225323 | 11.97680965616646 | Warn | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakHour | hourOfYear | 1447 | 774 | 673 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | area | m2 | 38.98503142608865 | 38.98503142608865 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | volume | m3 | 121.59536233412693 | 121.59536233412693 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.designLoad | W | 1065.076171875 | | | | N/A | unavailable (TasOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakLoad | W | 1025.7105712890625 | 1789.9830426085152 | 764.2724713194527 | 42.69719059492821 | Fail | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakHour | hourOfYear | 1443 | 775 | 668 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.unmetHours | h | 114 | 0 | 114 | 100 | Fail | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | area | m2 | 18.080018143643976 | 18.080018143643976 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | volume | m3 | 51.52805170938531 | 51.52805170938531 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.designLoad | W | 564.768798828125 | | | | N/A | unavailable (TasOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakLoad | W | 559.67822265625 | 936.5975380258114 | 376.91931536956145 | 40.243466384082474 | Fail | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakHour | hourOfYear | 1441 | 727 | 714 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.unmetHours | h | 69 | 0 | 69 | 100 | Fail | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakLoad | W | | 28.70645886203079 | | | N/A | unavailable (OpenStudioOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakHour | hourOfYear | | 7991 | | | N/A | unavailable (OpenStudioOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | area | m2 | 21.280022127733165 | 21.280022127733165 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | volume | m3 | 57.3391691981517 | 57.3391691981517 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.designLoad | W | 549.6607055664062 | | | | N/A | unavailable (TasOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakLoad | W | 529.828857421875 | 989.0652526225581 | 459.2363952006831 | 46.431354653597815 | Fail | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakHour | hourOfYear | 1457 | 919 | 538 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.unmetHours | h | 29 | 0 | 29 | 100 | Fail | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | - diff --git a/docs/benchmark/reports/2026-07-26-design-loads/README.md b/docs/benchmark/reports/2026-07-26-design-loads/README.md deleted file mode 100644 index 5baafae..0000000 --- a/docs/benchmark/reports/2026-07-26-design-loads/README.md +++ /dev/null @@ -1,223 +0,0 @@ -# B3 — cross-engine comparison with per-space design loads and conversion diagnostics - -Third run in the series, after the -[first real cross-engine run](../2026-07-25-cross-engine-smoke/README.md) and the -[provenance-fixed repeat](../2026-07-25-cross-engine-provenance-fixed/README.md). Same source model, -same weather bytes, same workstation — now with three merged OpenStudio producer changes: - -| PR | Merge | Change | -|---|---|---| -| [SAM_OpenStudio #18](https://github.com/SAM-BIM/SAM_OpenStudio/pull/18) | `67a6688` | design-day barometric pressure (avoids the EnergyPlus 31000 Pa IDD floor) | -| [SAM_OpenStudio #19](https://github.com/SAM-BIM/SAM_OpenStudio/pull/19) | `f86c4c5` | conversion diagnostics propagate on successful runs too | -| [SAM_OpenStudio #20](https://github.com/SAM-BIM/SAM_OpenStudio/pull/20) | `4306625` | per-space design loads from SQL `ZoneSizes` | - -Its purpose is narrow: record what those three changes do to coverage and to the reported numbers. It -remains a **mechanical validation of the comparator and the producers**, not a validated -engine-equivalence claim. - -## Run - -- **Date:** 2026-07-26 (TAS `14:57:18Z`, OpenStudio `14:58:27Z`). -- **Machine:** the same licensed EDSL Tas workstation as the two previous runs. -- **Model:** `HungaryHouse-WeahterDDY.sam` — 14 spaces, carrying embedded design days. -- **Weather:** `HUN_CEN_Budapest.Met.Center.128400_TMYx.epw`. -- **Engines:** Tas `9.5.7.0` (producer duration 56.9 s); EnergyPlus `25.1.0` / OpenStudio SDK - `3.10.0` (producer duration 22.5 s). -- **Producers built from:** `SAM_Tas@ff00497` and `SAM_OpenStudio@4306625`, both on `sow/2026-Q3`. -- **Comparator:** `SAM_Validation@a4065aad3bfaac41837ae2e784a09ec44b849544`. -- Both producers exited `0` with `state = Success`; the comparator exited `0`. - -Artefacts in this folder are the verbatim outputs: the two neutral input documents and the three -generated reports. - -### Two provenance deviations from the previous run, both inert - -Recorded here so neither is mistaken for a result. - -1. **`weather.identity` differs, `weather.hash` does not.** This run records - `HUN_CEN_Budapest.Met.Center.128400_TMYx`; the previous two recorded `Budapest`. The producers set - the identity from the file name only - (`WeatherIdentity = Path.GetFileNameWithoutExtension(weatherPath)`), so the earlier runs were - handed a copy of the same EPW named `Budapest.epw`. The **hash is unchanged on both sides** - (`sha256:472ffb4e…`), so the weather bytes are provably identical. The provenance gate compares - TAS identity against OpenStudio identity within a run, and both sides here got the same path, so - it still passes. `comparison.csv` does not carry the identity, so no metric row is affected. -2. **`samCommit` is an operator claim, not a measured fact.** `--sam-commit` is passed on the command - line and the producers cannot verify it, because local SAM assemblies are unstamped (`1.0.0.0`). - This run records `abcd617887f6792047023820f3efd2463fc5c6b3`, as the previous run did, so the two - are comparable — but the SAM binaries actually loaded were built **2026-07-21 22:03**, whereas - `abcd617` was committed **2026-07-24 10:36**, so they cannot contain it. `abcd617` changed one - file (`Query/TryGetSAMGeometries.cs`, a Grasshopper geometry-conversion path) that is not on the - producers' route, which is why this is a labelling defect and not a result defect. The binaries - are pinned instead by SHA-256, truncated to 16 hex characters: - - | Assembly | SHA-256 (first 16) | - |---|---| - | `SAM.Core.dll` | `e58a912b08d9d1c9` | - | `SAM.Geometry.dll` | `943ab8f28019876e` | - | `SAM.Analytical.dll` | `a9de2c2639efd467` | - | `SAM.Architectural.dll` | `99b8aa84c45a0b02` | - | `SAM.Weather.dll` | `6aa69438f08dc9d3` | - - These are the same binaries the previous run used — nothing in this session rebuilt `SAM\build` — - so SAM is genuinely held constant across the two runs, which is what the comparison below needs. - **Carried forward as a producer defect:** provenance should record a hash of the loaded assemblies - rather than an unverifiable commit string. - -## Result 1 — coverage rises 82 → 92, and nothing else moves - -| Field | Previous run | This run | -|---|---|---| -| Required metrics compared | 82 of 148 | **92 of 148** | -| Required metrics unavailable | 66 | **56** | -| Matched / warned / failed | 48 / 1 / 33 | 48 / 1 / **43** | -| Coverage status | Warn | Warn | -| Provenance status | Pass | Pass | -| Overall gate | Fail | Fail | - -`comparison.csv` differs from the previous run in **exactly 10 rows**, and every one of them is a -`heating.designLoad` row moving from `TasOnly` to `Both`. Every other row — all 138 of them — is -byte-identical, including all annual energy, peak load, peak hour, unmet hour and geometry values. - -So the whole of the change is the newly extracted design loads. The 10 rows land as `Fail`, which is -where the `fail` count goes from 33 to 43; the `match` and `warn` counts are unchanged. **Reading -this as a regression would be wrong** — these 10 metrics were previously not compared at all, and an -uncompared metric was never a pass. - -## Result 2 — the design loads disagree in *both* directions - -This is the substantive finding, and it bears directly on the open sizing-factor question. - -| Space | TAS (W) | OpenStudio (W) | Rel. diff | Direction | -|---|---:|---:|---:|---| -| `0872cd39` | 574.0 | 1132.1 | 49.3 % | OS higher | -| `c7df54b3` | 1065.1 | 1826.3 | 41.7 % | OS higher | -| `d8294f2a` | 564.8 | 917.5 | 38.4 % | OS higher | -| `d94f4dbe` | 549.7 | 909.1 | 39.5 % | OS higher | -| `354b8786` | 137.8 | 241.9 | 43.0 % | OS higher | -| `7ee49aac` | 366.4 | 170.6 | 53.4 % | OS lower | -| `6e7ef848` | 379.9 | 237.9 | 37.4 % | OS lower | -| `affc43b0` | 204.9 | 128.4 | 37.4 % | OS lower | -| `8d2d2a02` | 69.2 | 43.0 | 37.9 % | OS lower | -| `13a0403a` | 199.8 | 132.9 | 33.5 % | OS lower | -| **Sum** | **4111.7** | **5739.8** | — | OS/TAS = **1.396** | - -Five rows up, five rows down, with the aggregate 39.6 % higher on the OpenStudio side. What this -establishes is bounded: **no single multiplicative factor maps the TAS column onto the OpenStudio -column**, so a uniform sizing margin is ruled out as the *sole* explanation of the design-load -disagreement. The producer already reports `CalcDesLoad` rather than `UserDesLoad`, so EnergyPlus's -own 1.25 sizing factor is excluded on that side. - -**It does not settle whether TBD's `maxHeatingLoad` is pre- or post-factor**, and this run should not -be cited as having answered that. Two reasons: - -- That question is about the TAS side alone. Answering it means comparing a TAS design load against - its own unfactored TBD result — not against OpenStudio. -- This report documents that the two engines are **not evaluating comparable physics**: the - design-day translation is lossy (Result 3) and the OpenStudio side drops two apertures. A constant - multiplier applied to the TAS loads is therefore entirely consistent with ratios above 1 in some - spaces and below 1 in others, whenever the underlying per-space loads already differ. The - bidirectional spread cannot separate a sizing margin from that redistribution. - -The sizing-factor question stays **open for Stage B**. - -### An observed correlation, offered as a lead only - -The split is not random with respect to TAS's own unmet hours: - -| Direction | Spaces | TAS `heating.unmetHours` | -|---|---:|---| -| OS higher | 5 | 29, 65, 67, 69, 114 — all non-zero | -| OS lower | 5 | 0, 0, 0, 0 — and one outlier at **4761** | - -OpenStudio sizes *above* TAS in exactly those spaces where TAS reports moderate unmet heating hours, -and *below* TAS where TAS meets the load in every hour. The single exception, `13a0403a`, is -pathological rather than borderline: 4761 unmet hours is over half the year, and its TAS design load -(199.83349609375 W) sits just 0.0082 W above its annual peak load (199.82528686523438 W), i.e. with -no meaningful headroom at all. - -This is a correlation across ten rows on one fixture, and this run was not designed to test it. It is -recorded as a **lead for Stage B**, not a mechanism. Two readings are consistent with it and are not -distinguished by this data: TAS may be sizing to a design day that under-represents the load these -spaces actually see, or the two engines may be attributing load differently between adjacent spaces. - -## Result 3 — 68 conversion diagnostics now reach the artefact, where there were none - -The previous run recorded `warnings: []` and `notes: []` on the OpenStudio side. That was the -[#19](https://github.com/SAM-BIM/SAM_OpenStudio/pull/19) defect: diagnostics were dropped on -*successful* runs, so they only ever surfaced on failure. This run carries 68: - -| Code | Count | What it reports | -|---|---:|---| -| `SAM-OS-GEO-002` | 43 | duplicate/collinear vertices removed from panels (up to 24 → 7) | -| `SAM-OS-IC-001` | 14 | pollutant generation not converted (out of scope) | -| `SAM-OS-RUN-002` | 9 | design-day and weather translation (6 warnings + 3 notes) | -| `SAM-OS-GEO-001` | 2 | **apertures skipped as degenerate** | - -Three of these are material to the investigation, and all three are now self-documenting in the -committed artefact rather than resident in a session transcript: - -1. **The design days are London.** The warnings name them verbatim: - `London_TRY ANN HTG 100% CONDS DB` and `London_TRY ANN CLG 0% CONDS DB=>GRad`, on a model whose - annual weather is Budapest. This confirms in the artefact what was previously only known from - inspecting the `.sam`: **HungaryHouse is a known-mismatch forensic fixture, not a sizing - baseline.** -2. **The design-day translation is lossy, in named ways.** The warnings state that the hourly - humidity profile "was approximated as a constant dew point" (−5.2 °C for heating, 12.6 °C for - cooling), and that the hourly solar profile "was approximated by the ASHRAEClearSky model" with - **clearness 0.0 for the heating day** and 1.0 for cooling. A heating design day with clearness 0.0 - carries zero solar for all 24 hours. The `#18` pressure fix is also visible, reporting that the - elevation-based standard `101025 Pa` is written "instead of leaving the EnergyPlus 31000 Pa - default". This is precisely the hourly → parametric collapse that the design-day audit exists to - quantify, and it is now on the record from the producer's own output. -3. **Two apertures were skipped entirely.** `SAM-OS-GEO-001` reports one source aperture - (`24df7efc-1805-40ef-a7e7-fc8cd33c7225`) collapsing to 2 sides under the 0.01 m proximity rule on - two surfaces, and being dropped. The EnergyPlus model therefore has two fewer openings than the - SAM model. Both are on `SIM_INT_SLD` (internal) surfaces, so the likely effect is on inter-space - heat flow rather than solar gain — which makes it a candidate for the per-space load - redistribution in Result 2. **Not yet tested**, and the gbXML/TAS side has not been checked for - the same drop. - -The 43 `GEO-002` vertex removals are worth a separate look: reducing a 24-vertex panel to 7 is a -large simplification, and it is applied silently to the OpenStudio side only. - -## Carried forward - -The 56 unavailable required metrics (all 14 spaces matched, so none of this is an alignment problem): - -| Count | Metrics | Availability | Meaning | -|---:|---|---|---| -| 14 | `cooling.designLoad`, all 14 spaces | TasOnly | `ZoneSizes` has **no `Cooling` rows at all** for this model | -| 4 | `heating.designLoad`, 4 spaces | TasOnly | zones EnergyPlus did not size | -| 24 | `cooling.peakLoad` + `cooling.peakHour`, 12 spaces | OpenStudioOnly | TAS reports no cooling result where OpenStudio does | -| 8 | `heating`/`cooling` `peakLoad` + `peakHour`, 2 spaces | Neither | neither engine reports these two spaces | -| 4 | `heating`/`cooling` `unmetHours`, 2 spaces | TasOnly | | -| 2 | `heating.peakLoad` + `heating.peakHour`, 1 space | OpenStudioOnly | | - -- **Design loads fell from 28 unavailable to 18.** The remaining 18 are all one-sided from TAS: the - complete absence of `Cooling` rows in `ZoneSizes` is now the single largest coverage gap, and it is - a producer/route question, not a comparator one. -- `ZoneSizes.PeakTemp` remains **unmapped to any SAM parameter** and is carried verbatim on - `OpenStudioZoneSizingResult`. Every row carried `-3.20000004768372`, bit-identical to the design - day's *outdoor* maximum dry bulb, although EnergyPlus documents it as a zone value. Unsettled. -- `UserDesLoad` is preserved on the result set and may become its own metric - (`sizingCapacity` / `userDesignLoad`). -- The `Site coordinates taken from …` `Information` diagnostic in `Convert/ToOpenStudio/Weather.cs` - still carries the `RunCliFailed` code, so it does **not** appear in the 68 above despite being - material — site elevation feeds the design-day standard pressure that `#18` now writes. Re-coding - it changes conversion output, so it is a separate PR. -- The `eplusout.err` elevation warning (`Elevation difference=[416.00] percent, [104.00] meters`) is - **not fixed**, deliberately: which site is authoritative for each engine has to be established - first. -- TAS still reports `peakCoolingHour = 0` with `available: true` for a measured-zero cooling load. - -## Status - -- **Design-load coverage: improved and verified.** 10 of 28 recovered, with proof that no other - metric moved. -- **Diagnostic visibility: closed.** The producer's own warnings now name the London design days, the - humidity/solar approximations and the skipped apertures, inside the committed artefact. -- **Engine equivalence: open.** No single multiplicative margin reconciles the ten design-load rows, - but the TAS pre/post-factor question is untouched by this run and stays open — it needs a TAS design - load compared against its own unfactored TBD result. Next: the `design-day-audit-v1` neutral - contract, then the TAS (TBD + TSD) and OpenStudio (`.eio`) design-day producers. diff --git a/docs/benchmark/reports/2026-07-26-design-loads/benchmark-OpenStudio.json b/docs/benchmark/reports/2026-07-26-design-loads/benchmark-OpenStudio.json deleted file mode 100644 index 01c4f78..0000000 --- a/docs/benchmark/reports/2026-07-26-design-loads/benchmark-OpenStudio.json +++ /dev/null @@ -1,958 +0,0 @@ -{ - "schemaVersion": "1.0.0", - "provenance": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "43066253b20e56d14367bb9a525b40bb6bbfe52f", - "engine": { - "kind": "OpenStudio", - "name": "EnergyPlus", - "version": "25.1.0", - "sdkVersion": "3.10.0" - }, - "route": "Native-OpenStudio", - "weather": { - "identity": "HUN_CEN_Budapest.Met.Center.128400_TMYx", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-26T14:58:27.9258228Z", - "durationSeconds": 22.497965, - "state": "Success", - "resultSources": [ - "SAM.Analytical.OpenStudio" - ], - "warnings": [ - "[SAM-OS-GEO-001] Warning: Aperture would be degenerate in EnergyPlus: its vertices collapse to 2 side(s) under the 0.01 m proximity rule (sliver geometry); skipped \u2014 repair the source aperture (SAM Aperture 24df7efc-1805-40ef-a7e7-fc8cd33c7225) \u2192 SAM_SubSurface_SIM_INT_SLD_11_24df7efc", - "[SAM-OS-GEO-001] Warning: Aperture would be degenerate in EnergyPlus: its vertices collapse to 2 side(s) under the 0.01 m proximity rule (sliver geometry); skipped \u2014 repair the source aperture (SAM Aperture 24df7efc-1805-40ef-a7e7-fc8cd33c7225) \u2192 SAM_SubSurface_SIM_INT_SLD_8_24df7efc", - "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel 9525acbb-71d0-4e09-beb4-20825202fa1f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_11_9525acbb", - "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel 9525acbb-71d0-4e09-beb4-20825202fa1f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_4_9525acbb", - "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel d112c262-362c-4e80-9265-55d8385289a7) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_4_d112c262", - "[SAM-OS-GEO-002] Warning: Removed 10 duplicate/collinear vertices (15 \u2192 5) (SAM Panel 9605c081-960c-4204-9ea7-b9d0ce5ecf82) \u2192 SAM_Surface_SIM_INT_SLD_FLR_13_9605c081", - "[SAM-OS-GEO-002] Warning: Removed 10 duplicate/collinear vertices (15 \u2192 5) (SAM Panel 9605c081-960c-4204-9ea7-b9d0ce5ecf82) \u2192 SAM_Surface_SIM_INT_SLD_FLR_6_9605c081", - "[SAM-OS-GEO-002] Warning: Removed 11 duplicate/collinear vertices (15 \u2192 4) (SAM Panel eb313b0a-6b48-4279-a2d4-7f88cfac3c97) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_6_eb313b0a", - "[SAM-OS-GEO-002] Warning: Removed 12 duplicate/collinear vertices (17 \u2192 5) (SAM Panel 4dd3f4f2-9c41-4aff-8a74-87e55ecbf381) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_8_4dd3f4f2", - "[SAM-OS-GEO-002] Warning: Removed 12 duplicate/collinear vertices (18 \u2192 6) (SAM Panel ab1bdb45-5dce-4fdc-889c-622d35a54ee7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_ab1bdb45", - "[SAM-OS-GEO-002] Warning: Removed 13 duplicate/collinear vertices (20 \u2192 7) (SAM Panel 2fdf4184-838c-454f-acd1-b3efb35e08ea) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_7_2fdf4184", - "[SAM-OS-GEO-002] Warning: Removed 14 duplicate/collinear vertices (18 \u2192 4) (SAM Panel ab1bdb45-5dce-4fdc-889c-622d35a54ee7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_7_ab1bdb45", - "[SAM-OS-GEO-002] Warning: Removed 14 duplicate/collinear vertices (20 \u2192 6) (SAM Panel a10a7a36-f696-455c-87fc-6cf3c4af093a) \u2192 SAM_Surface_SIM_EXT_SLD_0_a10a7a36", - "[SAM-OS-GEO-002] Warning: Removed 17 duplicate/collinear vertices (24 \u2192 7) (SAM Panel 7da3b4ec-d56e-4308-9470-03840f81c246) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_0_7da3b4ec", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 304137db-7ca7-406f-8fd2-2a40a90a4d91) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_304137db", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 304137db-7ca7-406f-8fd2-2a40a90a4d91) \u2192 SAM_Surface_SIM_INT_SLD_FLR_9_304137db", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 46748422-6946-403f-8b92-e541bdfa6806) \u2192 SAM_Surface_SIM_EXT_SLD_8_46748422", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 5ff33694-7651-4fc9-99d2-1df97c72fcf7) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_5_5ff33694", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel b9425bc5-bc74-44b1-ab57-038579a614d3) \u2192 SAM_Surface_SIM_EXT_SLD_6_b9425bc5", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel c668837d-34ef-465c-8a2b-1d96faa11fb8) \u2192 SAM_Surface_SIM_EXT_SLD_10_c668837d", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel ddc53dfb-89ea-4907-a720-40a05ba9eb2f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_12_ddc53dfb", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel ddc53dfb-89ea-4907-a720-40a05ba9eb2f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_5_ddc53dfb", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel e982e233-c3c7-443f-8e72-e1414cfd7a11) \u2192 SAM_Surface_SIM_INT_SLD_Partition_11_e982e233", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel e982e233-c3c7-443f-8e72-e1414cfd7a11) \u2192 SAM_Surface_SIM_INT_SLD_Partition_8_e982e233", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (11 \u2192 8) (SAM Panel 2373dee4-5fae-4552-bc6a-bf6a3b926b3b) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_1_2373dee4", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel 6ef72c4a-6cfe-4839-862b-3b5884026cb3) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_3_6ef72c4a", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel 7dbfb73e-24b7-4ce7-b380-26651faf2b02) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_13_7dbfb73e", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel bdfe1887-f40d-44cf-a108-f903de8d69ea) \u2192 SAM_Surface_SIM_INT_SLD_FLR_10_bdfe1887", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel bdfe1887-f40d-44cf-a108-f903de8d69ea) \u2192 SAM_Surface_SIM_INT_SLD_FLR_3_bdfe1887", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel e79ed3b6-8a14-4c1b-bbef-0ac5ad4cc8b7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_e79ed3b6", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel e79ed3b6-8a14-4c1b-bbef-0ac5ad4cc8b7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_10_e79ed3b6", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel 90e290fb-acd7-4869-a5a6-a88ab8d4a4b5) \u2192 SAM_Surface_SIM_EXT_SLD_7_90e290fb", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel 976a502c-fd38-4b41-9c8f-6df45eb318c9) \u2192 SAM_Surface_SIM_EXT_SLD_5_976a502c", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel b5cfef9d-d61a-4a33-9fa3-c181ba5447d6) \u2192 SAM_Surface_SIM_EXT_SLD_3_b5cfef9d", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel d5360b9a-cce9-4583-a7e7-985b0161ae4c) \u2192 SAM_Surface_SIM_EXT_SLD_7_d5360b9a", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel dc004ac8-e855-46b9-ad73-dd26c8fca0a1) \u2192 SAM_Surface_SIM_EXT_SLD_1_dc004ac8", - "[SAM-OS-GEO-002] Warning: Removed 5 duplicate/collinear vertices (11 \u2192 6) (SAM Panel eac60a33-bf37-4255-a9a9-86b88486013c) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_eac60a33", - "[SAM-OS-GEO-002] Warning: Removed 5 duplicate/collinear vertices (11 \u2192 6) (SAM Panel eac60a33-bf37-4255-a9a9-86b88486013c) \u2192 SAM_Surface_SIM_INT_SLD_FLR_8_eac60a33", - "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel b2ceb2f0-16b9-4711-afe8-84c5d5073f4d) \u2192 SAM_Surface_SIM_INT_SLD_FLR_1_b2ceb2f0", - "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel b2ceb2f0-16b9-4711-afe8-84c5d5073f4d) \u2192 SAM_Surface_SIM_INT_SLD_FLR_8_b2ceb2f0", - "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel e651d6ae-0dee-4f8a-bf92-3c63732b821d) \u2192 SAM_Surface_SIM_EXT_SLD_6_e651d6ae", - "[SAM-OS-GEO-002] Warning: Removed 7 duplicate/collinear vertices (12 \u2192 5) (SAM Panel fffe6b7c-6554-4388-8b48-2a1af9321321) \u2192 SAM_Surface_SIM_EXT_SLD_8_fffe6b7c", - "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 55a8c3b8-94de-439b-94d2-2331f5b78bce) \u2192 SAM_Surface_SIM_EXT_SLD_0_55a8c3b8", - "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 6d8d3d15-314a-4dde-bc66-35ba0e30dc36) \u2192 SAM_Surface_SIM_EXT_SLD_0_6d8d3d15", - "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 9516906c-832b-49c0-8960-ad019a33ff78) \u2192 SAM_Surface_SIM_EXT_SLD_6_9516906c", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 0944207e-e492-45ac-a962-06891228f02f) \u2192 SAM_InternalCondition_S21_DomStore_dc8aa269", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 0fce4eca-718f-441e-9262-586b3a86f827) \u2192 SAM_InternalCondition_S15_DomBath_fed079cd", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 4c0a6313-bc80-4388-afe6-c4f43d97f450) \u2192 SAM_InternalCondition_S00_Unconditioned_924d7fd2", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 6be45072-bce2-4765-b85f-2f32862ca20b) \u2192 SAM_InternalCondition_S22_DomToilet_1b0c5a3e", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 7a96ac57-e5e6-4697-b433-52fce9d6e6b5) \u2192 SAM_InternalCondition_S16_DomBed_baece118", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 817fc162-7c50-48a5-be38-2f4b195281be) \u2192 SAM_InternalCondition_S20_DomLiving_28619b20", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition a4fa3b9e-3a47-4b65-9f87-7d5f5a1a7283) \u2192 SAM_InternalCondition_S16_DomBed_8521df7f", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition d27581a5-c45e-4ea8-a7a1-6322e509bf64) \u2192 SAM_InternalCondition_S15_DomBath_8f261a30", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition db2bc3b9-53f7-4c1f-83dd-ba3525e51567) \u2192 SAM_InternalCondition_S17_DomCirculation_3d3b8e9e", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition dd085a1e-ac39-425b-9f37-29506b53f612) \u2192 SAM_InternalCondition_S17_DomCirculation_08f6e1dc", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition de8a8726-58a9-41ad-934e-7ab759aff4f7) \u2192 SAM_InternalCondition_S16_DomBed_4bdd0777", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition ea700b3c-a049-4e12-8acf-da82fd8ac80f) \u2192 SAM_InternalCondition_S35_MechPlant_69a598cf", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition f050b569-0bb0-4582-86ff-fce5e0c1dbdf) \u2192 SAM_InternalCondition_S00_Unconditioned_ab9965d6", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition f71a8c74-a5aa-4970-8f9b-5be7be923f41) \u2192 SAM_InternalCondition_S17_DomCirculation_21eac1e6", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027 carries no usable barometric-pressure profile; the elevation-based standard (101025 Pa) is written instead of leaving the EnergyPlus 31000 Pa default", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027: the hourly humidity profile was approximated as a constant dew point (12.6 \u00B0C) from 33% RH at the maximum-dry-bulb hour", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027: the hourly solar profile was approximated by the ASHRAEClearSky model with clearness 1.0; daily dry-bulb range, mean wind/pressure and the SummerDesignDay day type are derived from the SAM hourly profile", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027 carries no usable barometric-pressure profile; the elevation-based standard (101025 Pa) is written instead of leaving the EnergyPlus 31000 Pa default", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027: the hourly humidity profile was approximated as a constant dew point (-5.2 \u00B0C) from 86% RH at the maximum-dry-bulb hour", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027: the hourly solar profile was approximated by the ASHRAEClearSky model with clearness 0.0; daily dry-bulb range, mean wind/pressure and the WinterDesignDay day type are derived from the SAM hourly profile" - ], - "notes": [ - "[SAM-OS-RUN-002] Information: Annual weather source: explicit EPW (HUN_CEN_Budapest.Met.Center.128400_TMYx.epw)", - "[SAM-OS-RUN-002] Information: Design-day source: AnalyticalModel heating/cooling design days (2 imported)", - "[SAM-OS-RUN-002] Information: Ground temperatures taken from the SAM model WeatherData (depth 0 m set, 12 monthly values)" - ] - }, - "model": { - "consumptionHeating": { - "value": 1751.1764825992748, - "unit": "kWh", - "available": true - }, - "consumptionCooling": { - "value": 5.2499930426550705, - "unit": "kWh", - "available": true - }, - "peakHeatingLoad": { - "value": 5.720615732872154, - "unit": "kW", - "available": true - }, - "peakHeatingHour": { - "value": 775, - "unit": "hourOfYear", - "available": true - }, - "peakCoolingLoad": { - "value": 0.0402250938503556, - "unit": "kW", - "available": true - }, - "peakCoolingHour": { - "value": 7974, - "unit": "hourOfYear", - "available": true - }, - "floorArea": { - "value": 167.95195207345205, - "unit": "m2", - "available": true - }, - "volume": { - "value": 470.1821620538829, - "unit": "m3", - "available": true - } - }, - "spaces": [ - { - "guid": "025eb80f65d745b799511b78ac2f82fb", - "name": "01_18 Void", - "area": { - "value": 6.6920779999999995, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.118059598283878, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - } - }, - { - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "name": "01_13 Bedroom", - "area": { - "value": 21.280010175465385, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.33913699268221, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 1132.105378358097, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 1185.0374713731348, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 15.588918884437554, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 7974, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "name": "GF_08 Bathroom", - "area": { - "value": 3.647967799712404, - "unit": "m2", - "available": true - }, - "volume": { - "value": 10.396708229180378, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 132.9146643279224, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 141.7703965184828, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 918, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "name": "01_15 Plantroom", - "area": { - "value": 2.9450000000000016, - "unit": "m2", - "available": true - }, - "volume": { - "value": 5.746544632499992, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "name": "GF_09 WC", - "area": { - "value": 2.355387206192353, - "unit": "m2", - "available": true - }, - "volume": { - "value": 6.712853537648204, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "354b8786b4714f3c9941cec051a93361", - "name": "GF_10 Closet", - "area": { - "value": 3.0986077487352315, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.007679927026205, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 241.94985081273356, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 262.9986126690514, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "name": "01_11 Void", - "area": { - "value": 5.03787628653, - "unit": "m2", - "available": true - }, - "volume": { - "value": 7.564547888993382, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": null, - "unit": "h", - "available": false - } - } - }, - { - "guid": "6e7ef8487b714da3b730e0f226af927e", - "name": "01_17 Gallery", - "area": { - "value": 23.55004650825489, - "unit": "m2", - "available": true - }, - "volume": { - "value": 58.340316505330264, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 237.9464743064166, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 255.3860902131586, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "name": "01_16 Bathroom", - "area": { - "value": 9.409999999999991, - "unit": "m2", - "available": true - }, - "volume": { - "value": 26.966204214472434, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 170.60334873223655, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 190.23285633880042, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "name": "GF_05 Stairs", - "area": { - "value": 4.634866891556, - "unit": "m2", - "available": true - }, - "volume": { - "value": 28.16450759620351, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 42.95932098398786, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 39.29050142981706, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 774, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "name": "GF_12 EntranceHall", - "area": { - "value": 6.955039759539971, - "unit": "m2", - "available": true - }, - "volume": { - "value": 20.363019689898447, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 128.39770298313388, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 133.44180277588939, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 774, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "name": "GF_07 LivingRoom", - "area": { - "value": 38.98503142608865, - "unit": "m2", - "available": true - }, - "volume": { - "value": 121.59536233412693, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 1826.2977051410624, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 1789.9830426085152, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 775, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "name": "GF_11 Bedroom", - "area": { - "value": 18.080018143643976, - "unit": "m2", - "available": true - }, - "volume": { - "value": 51.52805170938531, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 917.4634857295723, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 936.5975380258114, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 727, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 28.70645886203079, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 7991, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "name": "01_14 Bedroom", - "area": { - "value": 21.280022127733165, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.3391691981517, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 909.1363260313252, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 989.0652526225581, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - } - ] -} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-26-design-loads/benchmark-TAS.json b/docs/benchmark/reports/2026-07-26-design-loads/benchmark-TAS.json deleted file mode 100644 index 2319ca6..0000000 --- a/docs/benchmark/reports/2026-07-26-design-loads/benchmark-TAS.json +++ /dev/null @@ -1,890 +0,0 @@ -{ - "schemaVersion": "1.0.0", - "provenance": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", - "engine": { - "kind": "TAS", - "name": "Tas", - "version": "9.5.7.0", - "sdkVersion": null - }, - "route": "Native-TAS", - "weather": { - "identity": "HUN_CEN_Budapest.Met.Center.128400_TMYx", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-26T14:57:18.7687622Z", - "durationSeconds": 56.9406901, - "state": "Success", - "resultSources": [ - "SAM.Analytical.Tas" - ], - "warnings": [], - "notes": [ - "Shared gbXML was exported from the source model (no --gbxml supplied)." - ] - }, - "model": { - "consumptionHeating": { - "value": 4331.961588091359, - "unit": "kWh", - "available": true - }, - "consumptionCooling": { - "value": 0, - "unit": "kWh", - "available": true - }, - "peakHeatingLoad": { - "value": 3.93393212890625, - "unit": "kW", - "available": true - }, - "peakHeatingHour": { - "value": 919, - "unit": "hourOfYear", - "available": true - }, - "peakCoolingLoad": { - "value": 0, - "unit": "kW", - "available": true - }, - "peakCoolingHour": { - "value": 0, - "unit": "hourOfYear", - "available": true - }, - "floorArea": { - "value": 167.95195245742798, - "unit": "m2", - "available": true - }, - "volume": { - "value": 470.1821608543396, - "unit": "m3", - "available": true - } - }, - "spaces": [ - { - "guid": "025eb80f65d745b799511b78ac2f82fb", - "name": "01_18 Void", - "area": { - "value": 6.6920779999999995, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.118059598283878, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "name": "01_13 Bedroom", - "area": { - "value": 21.280010175465385, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.33913699268221, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 574.0481567382812, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 565.50927734375, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 65, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "name": "GF_08 Bathroom", - "area": { - "value": 3.647967799712404, - "unit": "m2", - "available": true - }, - "volume": { - "value": 10.396708229180378, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 199.83349609375, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 199.82528686523438, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 4761, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "name": "01_15 Plantroom", - "area": { - "value": 2.9450000000000016, - "unit": "m2", - "available": true - }, - "volume": { - "value": 5.746544632499992, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 28.741456985473633, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "name": "GF_09 WC", - "area": { - "value": 2.355387206192353, - "unit": "m2", - "available": true - }, - "volume": { - "value": 6.712853537648204, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 69.60518646240234, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 52.024513244628906, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "354b8786b4714f3c9941cec051a93361", - "name": "GF_10 Closet", - "area": { - "value": 3.0986077487352315, - "unit": "m2", - "available": true - }, - "volume": { - "value": 9.007679927026205, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 137.82980346679688, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 117.870849609375, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 67, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "name": "01_11 Void", - "area": { - "value": 5.03787628653, - "unit": "m2", - "available": true - }, - "volume": { - "value": 7.564547888993382, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "6e7ef8487b714da3b730e0f226af927e", - "name": "01_17 Gallery", - "area": { - "value": 23.55004650825489, - "unit": "m2", - "available": true - }, - "volume": { - "value": 58.340316505330264, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 379.9020080566406, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 132.86868286132812, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1442, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "name": "01_16 Bathroom", - "area": { - "value": 9.409999999999991, - "unit": "m2", - "available": true - }, - "volume": { - "value": 26.966204214472434, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 366.3569641113281, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 366.33624267578125, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1444, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "name": "GF_05 Stairs", - "area": { - "value": 4.634866891556, - "unit": "m2", - "available": true - }, - "volume": { - "value": 28.16450759620351, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 69.23214721679688, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 11.025825500488281, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1452, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "name": "GF_12 EntranceHall", - "area": { - "value": 6.955039759539971, - "unit": "m2", - "available": true - }, - "volume": { - "value": 20.363019689898447, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 204.9483642578125, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 117.45973205566406, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1447, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "name": "GF_07 LivingRoom", - "area": { - "value": 38.98503142608865, - "unit": "m2", - "available": true - }, - "volume": { - "value": 121.59536233412693, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 1065.076171875, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 1025.7105712890625, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1443, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 114, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "name": "GF_11 Bedroom", - "area": { - "value": 18.080018143643976, - "unit": "m2", - "available": true - }, - "volume": { - "value": 51.52805170938531, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 564.768798828125, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 559.67822265625, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1441, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 69, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - }, - { - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "name": "01_14 Bedroom", - "area": { - "value": 21.280022127733165, - "unit": "m2", - "available": true - }, - "volume": { - "value": 57.3391691981517, - "unit": "m3", - "available": true - }, - "heating": { - "designLoad": { - "value": 549.6607055664062, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": 529.828857421875, - "unit": "W", - "available": true - }, - "peakHour": { - "value": 1457, - "unit": "hourOfYear", - "available": true - }, - "unmetHours": { - "value": 29, - "unit": "h", - "available": true - } - }, - "cooling": { - "designLoad": { - "value": 0, - "unit": "W", - "available": true - }, - "peakLoad": { - "value": null, - "unit": "W", - "available": false - }, - "peakHour": { - "value": null, - "unit": "hourOfYear", - "available": false - }, - "unmetHours": { - "value": 0, - "unit": "h", - "available": true - } - } - } - ] -} \ No newline at end of file diff --git a/docs/benchmark/reports/2026-07-26-design-loads/comparison-summary.json b/docs/benchmark/reports/2026-07-26-design-loads/comparison-summary.json deleted file mode 100644 index 7d9cbe4..0000000 --- a/docs/benchmark/reports/2026-07-26-design-loads/comparison-summary.json +++ /dev/null @@ -1,3798 +0,0 @@ -{ - "summarySchemaVersion": "1.0.0", - "toleranceProfile": "default", - "toleranceProfileDetail": { - "name": "default", - "warnRelative": 0.05, - "failRelative": 0.15, - "hourWarnAbsolute": 1, - "hourFailAbsolute": 24, - "defaultNearZeroFloor": 0, - "nearZeroFloors": { - "kWh": 1, - "Wh": 1000, - "kW": 0.01, - "W": 10, - "m2": 0.01, - "m3": 0.01, - "h": 0.5 - } - }, - "gate": "Fail", - "statuses": { - "overall": "Fail", - "numerical": "Fail", - "coverage": "Warn", - "provenance": "Pass", - "reconciliation": "Pass" - }, - "provenanceCompatibility": { - "compatible": true, - "mismatches": [] - }, - "counts": { - "match": 48, - "warn": 1, - "fail": 43, - "notApplicable": 56 - }, - "coverage": { - "status": "Warn", - "requiredMetricCount": 148, - "comparableMetricCount": 92, - "unavailableRequiredMetricCount": 56, - "unmatchedSpaceCount": 0, - "ambiguousSpaceCount": 0, - "duplicateSpaceIdentityCount": 0 - }, - "schema": { - "tas": "1.0.0", - "openStudio": "1.0.0", - "driftNote": null - }, - "provenance": { - "tas": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "ff004975684482ec6d6530c75809478e9bb97a4c", - "engine": { - "kind": "TAS", - "name": "Tas", - "version": "9.5.7.0", - "sdkVersion": null - }, - "route": "Native-TAS", - "weather": { - "identity": "HUN_CEN_Budapest.Met.Center.128400_TMYx", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-26T14:57:18.7687622Z", - "durationSeconds": 56.9406901, - "state": "Success", - "resultSources": [ - "SAM.Analytical.Tas" - ], - "warnings": [], - "notes": [ - "Shared gbXML was exported from the source model (no --gbxml supplied)." - ] - }, - "openStudio": { - "sourceModelName": "000000_SAM_AnalyticalModel", - "sourceModelGuid": "a527e890fceb465fbb50f4b6874f3a4c", - "sourceFileHash": "sha256:c71343a9adac8703f7c7924687357114503d84ecb474ba3ff5f7f6064b1fc262", - "canonicalModelHash": "sha256:07a3ef154f1595a97528ff55bd667a255c0c7320d0760b643e4a2eed01629081", - "canonicalizationVersion": "1.0.0", - "samCommit": "abcd617887f6792047023820f3efd2463fc5c6b3", - "runnerCommit": "43066253b20e56d14367bb9a525b40bb6bbfe52f", - "engine": { - "kind": "OpenStudio", - "name": "EnergyPlus", - "version": "25.1.0", - "sdkVersion": "3.10.0" - }, - "route": "Native-OpenStudio", - "weather": { - "identity": "HUN_CEN_Budapest.Met.Center.128400_TMYx", - "hash": "sha256:472ffb4e2f10867600949a392ce46891fc1bd05f2fc8d8917a99bca7d26209c3" - }, - "designDaySource": "EmbeddedModel", - "runTimestampUtc": "2026-07-26T14:58:27.9258228Z", - "durationSeconds": 22.497965, - "state": "Success", - "resultSources": [ - "SAM.Analytical.OpenStudio" - ], - "warnings": [ - "[SAM-OS-GEO-001] Warning: Aperture would be degenerate in EnergyPlus: its vertices collapse to 2 side(s) under the 0.01 m proximity rule (sliver geometry); skipped \u2014 repair the source aperture (SAM Aperture 24df7efc-1805-40ef-a7e7-fc8cd33c7225) \u2192 SAM_SubSurface_SIM_INT_SLD_11_24df7efc", - "[SAM-OS-GEO-001] Warning: Aperture would be degenerate in EnergyPlus: its vertices collapse to 2 side(s) under the 0.01 m proximity rule (sliver geometry); skipped \u2014 repair the source aperture (SAM Aperture 24df7efc-1805-40ef-a7e7-fc8cd33c7225) \u2192 SAM_SubSurface_SIM_INT_SLD_8_24df7efc", - "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel 9525acbb-71d0-4e09-beb4-20825202fa1f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_11_9525acbb", - "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel 9525acbb-71d0-4e09-beb4-20825202fa1f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_4_9525acbb", - "[SAM-OS-GEO-002] Warning: Removed 1 duplicate/collinear vertices (7 \u2192 6) (SAM Panel d112c262-362c-4e80-9265-55d8385289a7) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_4_d112c262", - "[SAM-OS-GEO-002] Warning: Removed 10 duplicate/collinear vertices (15 \u2192 5) (SAM Panel 9605c081-960c-4204-9ea7-b9d0ce5ecf82) \u2192 SAM_Surface_SIM_INT_SLD_FLR_13_9605c081", - "[SAM-OS-GEO-002] Warning: Removed 10 duplicate/collinear vertices (15 \u2192 5) (SAM Panel 9605c081-960c-4204-9ea7-b9d0ce5ecf82) \u2192 SAM_Surface_SIM_INT_SLD_FLR_6_9605c081", - "[SAM-OS-GEO-002] Warning: Removed 11 duplicate/collinear vertices (15 \u2192 4) (SAM Panel eb313b0a-6b48-4279-a2d4-7f88cfac3c97) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_6_eb313b0a", - "[SAM-OS-GEO-002] Warning: Removed 12 duplicate/collinear vertices (17 \u2192 5) (SAM Panel 4dd3f4f2-9c41-4aff-8a74-87e55ecbf381) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_8_4dd3f4f2", - "[SAM-OS-GEO-002] Warning: Removed 12 duplicate/collinear vertices (18 \u2192 6) (SAM Panel ab1bdb45-5dce-4fdc-889c-622d35a54ee7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_ab1bdb45", - "[SAM-OS-GEO-002] Warning: Removed 13 duplicate/collinear vertices (20 \u2192 7) (SAM Panel 2fdf4184-838c-454f-acd1-b3efb35e08ea) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_7_2fdf4184", - "[SAM-OS-GEO-002] Warning: Removed 14 duplicate/collinear vertices (18 \u2192 4) (SAM Panel ab1bdb45-5dce-4fdc-889c-622d35a54ee7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_7_ab1bdb45", - "[SAM-OS-GEO-002] Warning: Removed 14 duplicate/collinear vertices (20 \u2192 6) (SAM Panel a10a7a36-f696-455c-87fc-6cf3c4af093a) \u2192 SAM_Surface_SIM_EXT_SLD_0_a10a7a36", - "[SAM-OS-GEO-002] Warning: Removed 17 duplicate/collinear vertices (24 \u2192 7) (SAM Panel 7da3b4ec-d56e-4308-9470-03840f81c246) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_0_7da3b4ec", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 304137db-7ca7-406f-8fd2-2a40a90a4d91) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_304137db", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 304137db-7ca7-406f-8fd2-2a40a90a4d91) \u2192 SAM_Surface_SIM_INT_SLD_FLR_9_304137db", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 46748422-6946-403f-8b92-e541bdfa6806) \u2192 SAM_Surface_SIM_EXT_SLD_8_46748422", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel 5ff33694-7651-4fc9-99d2-1df97c72fcf7) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_5_5ff33694", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel b9425bc5-bc74-44b1-ab57-038579a614d3) \u2192 SAM_Surface_SIM_EXT_SLD_6_b9425bc5", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel c668837d-34ef-465c-8a2b-1d96faa11fb8) \u2192 SAM_Surface_SIM_EXT_SLD_10_c668837d", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel ddc53dfb-89ea-4907-a720-40a05ba9eb2f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_12_ddc53dfb", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel ddc53dfb-89ea-4907-a720-40a05ba9eb2f) \u2192 SAM_Surface_SIM_INT_SLD_FLR_5_ddc53dfb", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel e982e233-c3c7-443f-8e72-e1414cfd7a11) \u2192 SAM_Surface_SIM_INT_SLD_Partition_11_e982e233", - "[SAM-OS-GEO-002] Warning: Removed 2 duplicate/collinear vertices (6 \u2192 4) (SAM Panel e982e233-c3c7-443f-8e72-e1414cfd7a11) \u2192 SAM_Surface_SIM_INT_SLD_Partition_8_e982e233", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (11 \u2192 8) (SAM Panel 2373dee4-5fae-4552-bc6a-bf6a3b926b3b) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_1_2373dee4", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel 6ef72c4a-6cfe-4839-862b-3b5884026cb3) \u2192 SAM_Surface_SIM_EXT_GRD_FLR_FLR01_3_6ef72c4a", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel 7dbfb73e-24b7-4ce7-b380-26651faf2b02) \u2192 SAM_Surface_SIM_EXT_SLD_FLR_Exposed_13_7dbfb73e", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel bdfe1887-f40d-44cf-a108-f903de8d69ea) \u2192 SAM_Surface_SIM_INT_SLD_FLR_10_bdfe1887", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel bdfe1887-f40d-44cf-a108-f903de8d69ea) \u2192 SAM_Surface_SIM_INT_SLD_FLR_3_bdfe1887", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel e79ed3b6-8a14-4c1b-bbef-0ac5ad4cc8b7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_e79ed3b6", - "[SAM-OS-GEO-002] Warning: Removed 3 duplicate/collinear vertices (7 \u2192 4) (SAM Panel e79ed3b6-8a14-4c1b-bbef-0ac5ad4cc8b7) \u2192 SAM_Surface_SIM_INT_SLD_FLR_10_e79ed3b6", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel 90e290fb-acd7-4869-a5a6-a88ab8d4a4b5) \u2192 SAM_Surface_SIM_EXT_SLD_7_90e290fb", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel 976a502c-fd38-4b41-9c8f-6df45eb318c9) \u2192 SAM_Surface_SIM_EXT_SLD_5_976a502c", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel b5cfef9d-d61a-4a33-9fa3-c181ba5447d6) \u2192 SAM_Surface_SIM_EXT_SLD_3_b5cfef9d", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel d5360b9a-cce9-4583-a7e7-985b0161ae4c) \u2192 SAM_Surface_SIM_EXT_SLD_7_d5360b9a", - "[SAM-OS-GEO-002] Warning: Removed 4 duplicate/collinear vertices (8 \u2192 4) (SAM Panel dc004ac8-e855-46b9-ad73-dd26c8fca0a1) \u2192 SAM_Surface_SIM_EXT_SLD_1_dc004ac8", - "[SAM-OS-GEO-002] Warning: Removed 5 duplicate/collinear vertices (11 \u2192 6) (SAM Panel eac60a33-bf37-4255-a9a9-86b88486013c) \u2192 SAM_Surface_SIM_INT_SLD_FLR_0_eac60a33", - "[SAM-OS-GEO-002] Warning: Removed 5 duplicate/collinear vertices (11 \u2192 6) (SAM Panel eac60a33-bf37-4255-a9a9-86b88486013c) \u2192 SAM_Surface_SIM_INT_SLD_FLR_8_eac60a33", - "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel b2ceb2f0-16b9-4711-afe8-84c5d5073f4d) \u2192 SAM_Surface_SIM_INT_SLD_FLR_1_b2ceb2f0", - "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel b2ceb2f0-16b9-4711-afe8-84c5d5073f4d) \u2192 SAM_Surface_SIM_INT_SLD_FLR_8_b2ceb2f0", - "[SAM-OS-GEO-002] Warning: Removed 6 duplicate/collinear vertices (10 \u2192 4) (SAM Panel e651d6ae-0dee-4f8a-bf92-3c63732b821d) \u2192 SAM_Surface_SIM_EXT_SLD_6_e651d6ae", - "[SAM-OS-GEO-002] Warning: Removed 7 duplicate/collinear vertices (12 \u2192 5) (SAM Panel fffe6b7c-6554-4388-8b48-2a1af9321321) \u2192 SAM_Surface_SIM_EXT_SLD_8_fffe6b7c", - "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 55a8c3b8-94de-439b-94d2-2331f5b78bce) \u2192 SAM_Surface_SIM_EXT_SLD_0_55a8c3b8", - "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 6d8d3d15-314a-4dde-bc66-35ba0e30dc36) \u2192 SAM_Surface_SIM_EXT_SLD_0_6d8d3d15", - "[SAM-OS-GEO-002] Warning: Removed 9 duplicate/collinear vertices (14 \u2192 5) (SAM Panel 9516906c-832b-49c0-8960-ad019a33ff78) \u2192 SAM_Surface_SIM_EXT_SLD_6_9516906c", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 0944207e-e492-45ac-a962-06891228f02f) \u2192 SAM_InternalCondition_S21_DomStore_dc8aa269", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 0fce4eca-718f-441e-9262-586b3a86f827) \u2192 SAM_InternalCondition_S15_DomBath_fed079cd", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 4c0a6313-bc80-4388-afe6-c4f43d97f450) \u2192 SAM_InternalCondition_S00_Unconditioned_924d7fd2", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 6be45072-bce2-4765-b85f-2f32862ca20b) \u2192 SAM_InternalCondition_S22_DomToilet_1b0c5a3e", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 7a96ac57-e5e6-4697-b433-52fce9d6e6b5) \u2192 SAM_InternalCondition_S16_DomBed_baece118", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition 817fc162-7c50-48a5-be38-2f4b195281be) \u2192 SAM_InternalCondition_S20_DomLiving_28619b20", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition a4fa3b9e-3a47-4b65-9f87-7d5f5a1a7283) \u2192 SAM_InternalCondition_S16_DomBed_8521df7f", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition d27581a5-c45e-4ea8-a7a1-6322e509bf64) \u2192 SAM_InternalCondition_S15_DomBath_8f261a30", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition db2bc3b9-53f7-4c1f-83dd-ba3525e51567) \u2192 SAM_InternalCondition_S17_DomCirculation_3d3b8e9e", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition dd085a1e-ac39-425b-9f37-29506b53f612) \u2192 SAM_InternalCondition_S17_DomCirculation_08f6e1dc", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition de8a8726-58a9-41ad-934e-7ab759aff4f7) \u2192 SAM_InternalCondition_S16_DomBed_4bdd0777", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition ea700b3c-a049-4e12-8acf-da82fd8ac80f) \u2192 SAM_InternalCondition_S35_MechPlant_69a598cf", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition f050b569-0bb0-4582-86ff-fce5e0c1dbdf) \u2192 SAM_InternalCondition_S00_Unconditioned_ab9965d6", - "[SAM-OS-IC-001] Warning: Pollutant generation is not converted (EnergyPlus generic contaminant modelling is out of scope) (SAM InternalCondition f71a8c74-a5aa-4970-8f9b-5be7be923f41) \u2192 SAM_InternalCondition_S17_DomCirculation_21eac1e6", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027 carries no usable barometric-pressure profile; the elevation-based standard (101025 Pa) is written instead of leaving the EnergyPlus 31000 Pa default", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027: the hourly humidity profile was approximated as a constant dew point (12.6 \u00B0C) from 33% RH at the maximum-dry-bulb hour", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN CLG 0% CONDS DB=\u003EGRad\u0027: the hourly solar profile was approximated by the ASHRAEClearSky model with clearness 1.0; daily dry-bulb range, mean wind/pressure and the SummerDesignDay day type are derived from the SAM hourly profile", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027 carries no usable barometric-pressure profile; the elevation-based standard (101025 Pa) is written instead of leaving the EnergyPlus 31000 Pa default", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027: the hourly humidity profile was approximated as a constant dew point (-5.2 \u00B0C) from 86% RH at the maximum-dry-bulb hour", - "[SAM-OS-RUN-002] Warning: Embedded design day \u0027London_TRY ANN HTG 100% CONDS DB\u0027: the hourly solar profile was approximated by the ASHRAEClearSky model with clearness 0.0; daily dry-bulb range, mean wind/pressure and the WinterDesignDay day type are derived from the SAM hourly profile" - ], - "notes": [ - "[SAM-OS-RUN-002] Information: Annual weather source: explicit EPW (HUN_CEN_Budapest.Met.Center.128400_TMYx.epw)", - "[SAM-OS-RUN-002] Information: Design-day source: AnalyticalModel heating/cooling design days (2 imported)", - "[SAM-OS-RUN-002] Information: Ground temperatures taken from the SAM model WeatherData (depth 0 m set, 12 monthly values)" - ] - } - }, - "modelMetrics": [ - { - "scope": "model", - "key": "consumptionHeating", - "unit": "kWh", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4331.961588091359, - "available": true, - "unit": "kWh" - }, - "openStudio": { - "value": 1751.1764825992748, - "available": true, - "unit": "kWh" - }, - "absoluteDifference": 2580.785105492084, - "relativeDifference": 0.5957543835537944, - "signedDifference": -2580.785105492084 - }, - { - "scope": "model", - "key": "consumptionCooling", - "unit": "kWh", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "kWh" - }, - "openStudio": { - "value": 5.2499930426550705, - "available": true, - "unit": "kWh" - }, - "absoluteDifference": 5.2499930426550705, - "relativeDifference": 1, - "signedDifference": 5.2499930426550705 - }, - { - "scope": "model", - "key": "peakHeatingLoad", - "unit": "kW", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.93393212890625, - "available": true, - "unit": "kW" - }, - "openStudio": { - "value": 5.720615732872154, - "available": true, - "unit": "kW" - }, - "absoluteDifference": 1.7866836039659035, - "relativeDifference": 0.3123236531513824, - "signedDifference": 1.7866836039659035 - }, - { - "scope": "model", - "key": "peakHeatingHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 775, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 144, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "model", - "key": "peakCoolingLoad", - "unit": "kW", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "kW" - }, - "openStudio": { - "value": 0.0402250938503556, - "available": true, - "unit": "kW" - }, - "absoluteDifference": 0.0402250938503556, - "relativeDifference": 1, - "signedDifference": 0.0402250938503556 - }, - { - "scope": "model", - "key": "peakCoolingHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7974, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 786, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "model", - "key": "floorArea", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 167.95195245742798, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 167.95195207345205, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 3.839759301627055E-07, - "relativeDifference": 2.2862248669602973E-09, - "signedDifference": -3.839759301627055E-07 - }, - { - "scope": "model", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 470.1821608543396, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 470.1821620538829, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 1.1995433055744797E-06, - "relativeDifference": 2.5512309959496336E-09, - "signedDifference": 1.1995433055744797E-06 - } - ], - "spaces": [ - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "guid": "025eb80f65d745b799511b78ac2f82fb", - "tasName": "01_18 Void", - "openStudioName": "01_18 Void", - "matchKind": "Guid", - "metrics": [ - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.6920779999999995, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 6.6920779999999995, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.118059598283878, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 9.118059598283878, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "heating.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "025eb80f65d745b799511b78ac2f82fb", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - } - ] - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "guid": "0872cd39962f43c3b62a6e031a76c24a", - "tasName": "01_13 Bedroom", - "openStudioName": "01_13 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 21.280010175465385, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 21.280010175465385, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 57.33913699268221, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 57.33913699268221, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 574.0481567382812, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 1132.105378358097, - "available": true, - "unit": "W" - }, - "absoluteDifference": 558.0572216198157, - "relativeDifference": 0.4929375235626663, - "signedDifference": 558.0572216198157 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 565.50927734375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 1185.0374713731348, - "available": true, - "unit": "W" - }, - "absoluteDifference": 619.5281940293848, - "relativeDifference": 0.5227920711330089, - "signedDifference": 619.5281940293848 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 522, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 65, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 65, - "relativeDifference": 1, - "signedDifference": -65 - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 15.588918884437554, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7974, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "0872cd39962f43c3b62a6e031a76c24a", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "guid": "13a0403ab3a543608613b0ce9bf52d92", - "tasName": "GF_08 Bathroom", - "openStudioName": "GF_08 Bathroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.647967799712404, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 3.647967799712404, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 10.396708229180378, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 10.396708229180378, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 199.83349609375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 132.9146643279224, - "available": true, - "unit": "W" - }, - "absoluteDifference": 66.9188317658276, - "relativeDifference": 0.3348729470980844, - "signedDifference": -66.9188317658276 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 199.82528686523438, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 141.7703965184828, - "available": true, - "unit": "W" - }, - "absoluteDifference": 58.05489034675156, - "relativeDifference": 0.2905282472378222, - "signedDifference": -58.05489034675156 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 918, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 523, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4761, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 4761, - "relativeDifference": 1, - "signedDifference": -4761 - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "13a0403ab3a543608613b0ce9bf52d92", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "guid": "21bdbb806b4f4e2fbad0632b19fdf14b", - "tasName": "01_15 Plantroom", - "openStudioName": "01_15 Plantroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 2.9450000000000016, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 2.9450000000000016, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 5.746544632499992, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 5.746544632499992, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 28.741456985473633, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "21bdbb806b4f4e2fbad0632b19fdf14b", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "guid": "312fab2f1ba64756b47ccc3dcb21ac8a", - "tasName": "GF_09 WC", - "openStudioName": "GF_09 WC", - "matchKind": "Guid", - "metrics": [ - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 2.355387206192353, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 2.355387206192353, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.712853537648204, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 6.712853537648204, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 69.60518646240234, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 52.024513244628906, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": 52.024513244628906, - "relativeDifference": 1, - "signedDifference": -52.024513244628906 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 1441, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "312fab2f1ba64756b47ccc3dcb21ac8a", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "guid": "354b8786b4714f3c9941cec051a93361", - "tasName": "GF_10 Closet", - "openStudioName": "GF_10 Closet", - "matchKind": "Guid", - "metrics": [ - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 3.0986077487352315, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 3.0986077487352315, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.007679927026205, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 9.007679927026205, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 137.82980346679688, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 241.94985081273356, - "available": true, - "unit": "W" - }, - "absoluteDifference": 104.12004734593668, - "relativeDifference": 0.4303373074882547, - "signedDifference": 104.12004734593668 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 117.870849609375, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 262.9986126690514, - "available": true, - "unit": "W" - }, - "absoluteDifference": 145.1277630596764, - "relativeDifference": 0.5518195004408647, - "signedDifference": 145.1277630596764 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 522, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 67, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 67, - "relativeDifference": 1, - "signedDifference": -67 - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "354b8786b4714f3c9941cec051a93361", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "guid": "4377cbd6712349e68fdae8814ff9c5d9", - "tasName": "01_11 Void", - "openStudioName": "01_11 Void", - "matchKind": "Guid", - "metrics": [ - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 5.03787628653, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 5.03787628653, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 7.564547888993382, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 7.564547888993382, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "heating.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "Neither", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "Neither", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "4377cbd6712349e68fdae8814ff9c5d9", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "h" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - } - ] - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "guid": "6e7ef8487b714da3b730e0f226af927e", - "tasName": "01_17 Gallery", - "openStudioName": "01_17 Gallery", - "matchKind": "Guid", - "metrics": [ - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 23.55004650825489, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 23.55004650825489, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 58.340316505330264, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 58.340316505330264, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 379.9020080566406, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 237.9464743064166, - "available": true, - "unit": "W" - }, - "absoluteDifference": 141.95553375022402, - "relativeDifference": 0.3736635520206555, - "signedDifference": -141.95553375022402 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 132.86868286132812, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 255.3860902131586, - "available": true, - "unit": "W" - }, - "absoluteDifference": 122.51740735183049, - "relativeDifference": 0.4797340655850561, - "signedDifference": 122.51740735183049 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1442, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 523, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "6e7ef8487b714da3b730e0f226af927e", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "guid": "7ee49aac87b24671bb00ed1b291806a1", - "tasName": "01_16 Bathroom", - "openStudioName": "01_16 Bathroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 9.409999999999991, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 9.409999999999991, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 26.966204214472434, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 26.966204214472434, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 366.3569641113281, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 170.60334873223655, - "available": true, - "unit": "W" - }, - "absoluteDifference": 195.75361537909157, - "relativeDifference": 0.5343248103770348, - "signedDifference": -195.75361537909157 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 366.33624267578125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 190.23285633880042, - "available": true, - "unit": "W" - }, - "absoluteDifference": 176.10338633698083, - "relativeDifference": 0.4807151622528315, - "signedDifference": -176.10338633698083 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1444, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 525, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "7ee49aac87b24671bb00ed1b291806a1", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "guid": "8d2d2a0234584fe4b171e5a9373f1b4d", - "tasName": "GF_05 Stairs", - "openStudioName": "GF_05 Stairs", - "matchKind": "Guid", - "metrics": [ - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 4.634866891556, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 4.634866891556, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 28.16450759620351, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 28.16450759620351, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 69.23214721679688, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 42.95932098398786, - "available": true, - "unit": "W" - }, - "absoluteDifference": 26.272826232809017, - "relativeDifference": 0.37948882548069796, - "signedDifference": -26.272826232809017 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 11.025825500488281, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 39.29050142981706, - "available": true, - "unit": "W" - }, - "absoluteDifference": 28.26467592932878, - "relativeDifference": 0.7193768188430163, - "signedDifference": 28.26467592932878 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1452, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 774, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 678, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "8d2d2a0234584fe4b171e5a9373f1b4d", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "guid": "affc43b0b5b3445f85d5cea8a7b88d4f", - "tasName": "GF_12 EntranceHall", - "openStudioName": "GF_12 EntranceHall", - "matchKind": "Guid", - "metrics": [ - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 6.955039759539971, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 6.955039759539971, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 20.363019689898447, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 20.363019689898447, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 204.9483642578125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 128.39770298313388, - "available": true, - "unit": "W" - }, - "absoluteDifference": 76.55066127467862, - "relativeDifference": 0.3735119406875703, - "signedDifference": -76.55066127467862 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Warn", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 117.45973205566406, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 133.44180277588939, - "available": true, - "unit": "W" - }, - "absoluteDifference": 15.982070720225323, - "relativeDifference": 0.1197680965616646, - "signedDifference": 15.982070720225323 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1447, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 774, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 673, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "affc43b0b5b3445f85d5cea8a7b88d4f", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "guid": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "tasName": "GF_07 LivingRoom", - "openStudioName": "GF_07 LivingRoom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 38.98503142608865, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 38.98503142608865, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 121.59536233412693, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 121.59536233412693, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 1065.076171875, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 1826.2977051410624, - "available": true, - "unit": "W" - }, - "absoluteDifference": 761.2215332660624, - "relativeDifference": 0.41681130689876544, - "signedDifference": 761.2215332660624 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 1025.7105712890625, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 1789.9830426085152, - "available": true, - "unit": "W" - }, - "absoluteDifference": 764.2724713194527, - "relativeDifference": 0.4269719059492821, - "signedDifference": 764.2724713194527 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1443, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 775, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 668, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 114, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 114, - "relativeDifference": 1, - "signedDifference": -114 - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "c7df54b3c0064fc5a7c6e6012dbb9b8e", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "guid": "d8294f2a30b445ca9f6ba83c8dcf4903", - "tasName": "GF_11 Bedroom", - "openStudioName": "GF_11 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 18.080018143643976, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 18.080018143643976, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 51.52805170938531, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 51.52805170938531, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 564.768798828125, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 917.4634857295723, - "available": true, - "unit": "W" - }, - "absoluteDifference": 352.6946869014473, - "relativeDifference": 0.3844236772224046, - "signedDifference": 352.6946869014473 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 559.67822265625, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 936.5975380258114, - "available": true, - "unit": "W" - }, - "absoluteDifference": 376.91931536956145, - "relativeDifference": 0.4024346638408247, - "signedDifference": 376.91931536956145 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1441, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 727, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 714, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 69, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 69, - "relativeDifference": 1, - "signedDifference": -69 - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 28.70645886203079, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 7991, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d8294f2a30b445ca9f6ba83c8dcf4903", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "guid": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "tasName": "01_14 Bedroom", - "openStudioName": "01_14 Bedroom", - "matchKind": "Guid", - "metrics": [ - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "area", - "unit": "m2", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 21.280022127733165, - "available": true, - "unit": "m2" - }, - "openStudio": { - "value": 21.280022127733165, - "available": true, - "unit": "m2" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "volume", - "unit": "m3", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 57.3391691981517, - "available": true, - "unit": "m3" - }, - "openStudio": { - "value": 57.3391691981517, - "available": true, - "unit": "m3" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.designLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 549.6607055664062, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 909.1363260313252, - "available": true, - "unit": "W" - }, - "absoluteDifference": 359.47562046491896, - "relativeDifference": 0.39540342869605327, - "signedDifference": 359.47562046491896 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.peakLoad", - "unit": "W", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 529.828857421875, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": 989.0652526225581, - "available": true, - "unit": "W" - }, - "absoluteDifference": 459.2363952006831, - "relativeDifference": 0.4643135465359781, - "signedDifference": 459.2363952006831 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.peakHour", - "unit": "hourOfYear", - "availability": "Both", - "band": "Fail", - "informational": true, - "notApplicableReason": null, - "circular": true, - "tas": { - "value": 1457, - "available": true, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 919, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": 538, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "heating.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Fail", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 29, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 29, - "relativeDifference": 1, - "signedDifference": -29 - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.designLoad", - "unit": "W", - "availability": "TasOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "W" - }, - "openStudio": { - "value": null, - "available": false, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.peakLoad", - "unit": "W", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": false, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "W" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "W" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.peakHour", - "unit": "hourOfYear", - "availability": "OpenStudioOnly", - "band": "N/A", - "informational": true, - "notApplicableReason": "unavailable", - "circular": false, - "tas": { - "value": null, - "available": false, - "unit": "hourOfYear" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "hourOfYear" - }, - "absoluteDifference": null, - "relativeDifference": null, - "signedDifference": null - }, - { - "scope": "d94f4dbee3d24ff1bcb341e2c5c6ec6d", - "key": "cooling.unmetHours", - "unit": "h", - "availability": "Both", - "band": "Match", - "informational": false, - "notApplicableReason": null, - "circular": false, - "tas": { - "value": 0, - "available": true, - "unit": "h" - }, - "openStudio": { - "value": 0, - "available": true, - "unit": "h" - }, - "absoluteDifference": 0, - "relativeDifference": 0, - "signedDifference": 0 - } - ] - } - ], - "spaceDiagnostics": { - "tasCount": 14, - "openStudioCount": 14, - "matchedByGuid": 14, - "matchedByName": 0, - "onlyInTas": [], - "onlyInOpenStudio": [], - "duplicateTasGuids": [], - "duplicateOpenStudioGuids": [], - "duplicateTasNames": [], - "duplicateOpenStudioNames": [], - "ambiguousNameMatches": [] - }, - "reconciliation": [ - { - "engine": "TAS", - "key": "floorArea", - "unit": "m2", - "modelTotal": 167.95195245742798, - "modelTotalAvailable": true, - "sumOfSpaces": 167.95195207345202, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 3.839759585844149E-07, - "relativeDifference": 2.2862250361855374E-09, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "TAS", - "key": "volume", - "unit": "m3", - "modelTotal": 470.1821608543396, - "modelTotalAvailable": true, - "sumOfSpaces": 470.1821620538828, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 1.199543191887642E-06, - "relativeDifference": 2.5512307541564592E-09, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "OpenStudio", - "key": "floorArea", - "unit": "m2", - "modelTotal": 167.95195207345205, - "modelTotalAvailable": true, - "sumOfSpaces": 167.95195207345202, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 2.842170943040401E-14, - "relativeDifference": 1.6922524019235018E-16, - "band": "Match", - "notApplicableReason": null - }, - { - "engine": "OpenStudio", - "key": "volume", - "unit": "m3", - "modelTotal": 470.1821620538829, - "modelTotalAvailable": true, - "sumOfSpaces": 470.1821620538828, - "contributingSpaces": 14, - "spacesMissingValue": 0, - "excludedSpaces": 0, - "complete": true, - "absoluteDifference": 1.1368683772161603E-13, - "relativeDifference": 2.417931748516387E-16, - "band": "Match", - "notApplicableReason": null - } - ] -} diff --git a/docs/benchmark/reports/2026-07-26-design-loads/comparison.csv b/docs/benchmark/reports/2026-07-26-design-loads/comparison.csv deleted file mode 100644 index 061a354..0000000 --- a/docs/benchmark/reports/2026-07-26-design-loads/comparison.csv +++ /dev/null @@ -1,149 +0,0 @@ -scope,metric,unit,availability,tasValue,openStudioValue,absoluteDifference,relativeDifference,relativePercent,signedDifference,band,note,toleranceProfile,warnRelative,failRelative,hourWarnAbsolute,hourFailAbsolute,nearZeroFloor,informational -model,consumptionHeating,kWh,Both,4331.961588091359,1751.1764825992748,2580.785105492084,0.5957543835537944,59.57543835537944,-2580.785105492084,Fail,,default,0.05,0.15,1,24,1,false -model,consumptionCooling,kWh,Both,0,5.2499930426550705,5.2499930426550705,1,100,5.2499930426550705,Fail,,default,0.05,0.15,1,24,1,false -model,peakHeatingLoad,kW,Both,3.93393212890625,5.720615732872154,1.7866836039659035,0.3123236531513824,31.23236531513824,1.7866836039659035,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true -model,peakHeatingHour,hourOfYear,Both,919,775,144,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -model,peakCoolingLoad,kW,Both,0,0.0402250938503556,0.0402250938503556,1,100,0.0402250938503556,Fail,informational (excluded from the numerical status),default,0.05,0.15,1,24,0.01,true -model,peakCoolingHour,hourOfYear,Both,0,7974,786,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -model,floorArea,m2,Both,167.95195245742798,167.95195207345205,3.839759301627055E-07,2.2862248669602973E-09,2.2862248669602975E-07,-3.839759301627055E-07,Match,,default,0.05,0.15,1,24,0.01,false -model,volume,m3,Both,470.1821608543396,470.1821620538829,1.1995433055744797E-06,2.5512309959496336E-09,2.5512309959496334E-07,1.1995433055744797E-06,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,area,m2,Both,6.6920779999999995,6.6920779999999995,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,volume,m3,Both,9.118059598283878,9.118059598283878,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -025eb80f65d745b799511b78ac2f82fb,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -025eb80f65d745b799511b78ac2f82fb,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -025eb80f65d745b799511b78ac2f82fb,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -025eb80f65d745b799511b78ac2f82fb,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -025eb80f65d745b799511b78ac2f82fb,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -0872cd39962f43c3b62a6e031a76c24a,area,m2,Both,21.280010175465385,21.280010175465385,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -0872cd39962f43c3b62a6e031a76c24a,volume,m3,Both,57.33913699268221,57.33913699268221,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -0872cd39962f43c3b62a6e031a76c24a,heating.designLoad,W,Both,574.0481567382812,1132.105378358097,558.0572216198157,0.4929375235626663,49.29375235626663,558.0572216198157,Fail,,default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,heating.peakLoad,W,Both,565.50927734375,1185.0374713731348,619.5281940293848,0.5227920711330089,52.279207113300885,619.5281940293848,Fail,,default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -0872cd39962f43c3b62a6e031a76c24a,heating.unmetHours,h,Both,65,0,65,1,100,-65,Fail,,default,0.05,0.15,1,24,0.5,false -0872cd39962f43c3b62a6e031a76c24a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,cooling.peakLoad,W,OpenStudioOnly,,15.588918884437554,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -0872cd39962f43c3b62a6e031a76c24a,cooling.peakHour,hourOfYear,OpenStudioOnly,,7974,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -0872cd39962f43c3b62a6e031a76c24a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -13a0403ab3a543608613b0ce9bf52d92,area,m2,Both,3.647967799712404,3.647967799712404,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -13a0403ab3a543608613b0ce9bf52d92,volume,m3,Both,10.396708229180378,10.396708229180378,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -13a0403ab3a543608613b0ce9bf52d92,heating.designLoad,W,Both,199.83349609375,132.9146643279224,66.9188317658276,0.3348729470980844,33.48729470980844,-66.9188317658276,Fail,,default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,heating.peakLoad,W,Both,199.82528686523438,141.7703965184828,58.05489034675156,0.2905282472378222,29.052824723782223,-58.05489034675156,Fail,,default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,heating.peakHour,hourOfYear,Both,1441,918,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -13a0403ab3a543608613b0ce9bf52d92,heating.unmetHours,h,Both,4761,0,4761,1,100,-4761,Fail,,default,0.05,0.15,1,24,0.5,false -13a0403ab3a543608613b0ce9bf52d92,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -13a0403ab3a543608613b0ce9bf52d92,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -13a0403ab3a543608613b0ce9bf52d92,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -21bdbb806b4f4e2fbad0632b19fdf14b,area,m2,Both,2.9450000000000016,2.9450000000000016,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -21bdbb806b4f4e2fbad0632b19fdf14b,volume,m3,Both,5.746544632499992,5.746544632499992,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.designLoad,W,TasOnly,28.741456985473633,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,heating.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -21bdbb806b4f4e2fbad0632b19fdf14b,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -21bdbb806b4f4e2fbad0632b19fdf14b,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -312fab2f1ba64756b47ccc3dcb21ac8a,area,m2,Both,2.355387206192353,2.355387206192353,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -312fab2f1ba64756b47ccc3dcb21ac8a,volume,m3,Both,6.712853537648204,6.712853537648204,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.designLoad,W,TasOnly,69.60518646240234,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakLoad,W,Both,52.024513244628906,0,52.024513244628906,1,100,-52.024513244628906,Fail,,default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,heating.peakHour,hourOfYear,Both,1441,0,1441,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -312fab2f1ba64756b47ccc3dcb21ac8a,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -312fab2f1ba64756b47ccc3dcb21ac8a,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -354b8786b4714f3c9941cec051a93361,area,m2,Both,3.0986077487352315,3.0986077487352315,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -354b8786b4714f3c9941cec051a93361,volume,m3,Both,9.007679927026205,9.007679927026205,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -354b8786b4714f3c9941cec051a93361,heating.designLoad,W,Both,137.82980346679688,241.94985081273356,104.12004734593668,0.4303373074882547,43.03373074882547,104.12004734593668,Fail,,default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,heating.peakLoad,W,Both,117.870849609375,262.9986126690514,145.1277630596764,0.5518195004408647,55.18195004408647,145.1277630596764,Fail,,default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,heating.peakHour,hourOfYear,Both,1441,919,522,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -354b8786b4714f3c9941cec051a93361,heating.unmetHours,h,Both,67,0,67,1,100,-67,Fail,,default,0.05,0.15,1,24,0.5,false -354b8786b4714f3c9941cec051a93361,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -354b8786b4714f3c9941cec051a93361,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -354b8786b4714f3c9941cec051a93361,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -4377cbd6712349e68fdae8814ff9c5d9,area,m2,Both,5.03787628653,5.03787628653,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -4377cbd6712349e68fdae8814ff9c5d9,volume,m3,Both,7.564547888993382,7.564547888993382,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -4377cbd6712349e68fdae8814ff9c5d9,heating.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,heating.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,heating.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -4377cbd6712349e68fdae8814ff9c5d9,heating.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.peakLoad,W,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,10,false -4377cbd6712349e68fdae8814ff9c5d9,cooling.peakHour,hourOfYear,Neither,,,,,,,N/A,unavailable (Neither),default,0.05,0.15,1,24,0,true -4377cbd6712349e68fdae8814ff9c5d9,cooling.unmetHours,h,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,0.5,false -6e7ef8487b714da3b730e0f226af927e,area,m2,Both,23.55004650825489,23.55004650825489,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -6e7ef8487b714da3b730e0f226af927e,volume,m3,Both,58.340316505330264,58.340316505330264,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -6e7ef8487b714da3b730e0f226af927e,heating.designLoad,W,Both,379.9020080566406,237.9464743064166,141.95553375022402,0.3736635520206555,37.36635520206555,-141.95553375022402,Fail,,default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,heating.peakLoad,W,Both,132.86868286132812,255.3860902131586,122.51740735183049,0.4797340655850561,47.97340655850561,122.51740735183049,Fail,,default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,heating.peakHour,hourOfYear,Both,1442,919,523,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -6e7ef8487b714da3b730e0f226af927e,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -6e7ef8487b714da3b730e0f226af927e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -6e7ef8487b714da3b730e0f226af927e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -6e7ef8487b714da3b730e0f226af927e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -7ee49aac87b24671bb00ed1b291806a1,area,m2,Both,9.409999999999991,9.409999999999991,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -7ee49aac87b24671bb00ed1b291806a1,volume,m3,Both,26.966204214472434,26.966204214472434,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -7ee49aac87b24671bb00ed1b291806a1,heating.designLoad,W,Both,366.3569641113281,170.60334873223655,195.75361537909157,0.5343248103770348,53.43248103770348,-195.75361537909157,Fail,,default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,heating.peakLoad,W,Both,366.33624267578125,190.23285633880042,176.10338633698083,0.4807151622528315,48.07151622528315,-176.10338633698083,Fail,,default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,heating.peakHour,hourOfYear,Both,1444,919,525,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -7ee49aac87b24671bb00ed1b291806a1,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -7ee49aac87b24671bb00ed1b291806a1,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -7ee49aac87b24671bb00ed1b291806a1,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -7ee49aac87b24671bb00ed1b291806a1,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -8d2d2a0234584fe4b171e5a9373f1b4d,area,m2,Both,4.634866891556,4.634866891556,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -8d2d2a0234584fe4b171e5a9373f1b4d,volume,m3,Both,28.16450759620351,28.16450759620351,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.designLoad,W,Both,69.23214721679688,42.95932098398786,26.272826232809017,0.37948882548069796,37.9488825480698,-26.272826232809017,Fail,,default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakLoad,W,Both,11.025825500488281,39.29050142981706,28.26467592932878,0.7193768188430163,71.93768188430163,28.26467592932878,Fail,,default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,heating.peakHour,hourOfYear,Both,1452,774,678,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -8d2d2a0234584fe4b171e5a9373f1b4d,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -8d2d2a0234584fe4b171e5a9373f1b4d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -affc43b0b5b3445f85d5cea8a7b88d4f,area,m2,Both,6.955039759539971,6.955039759539971,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -affc43b0b5b3445f85d5cea8a7b88d4f,volume,m3,Both,20.363019689898447,20.363019689898447,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.designLoad,W,Both,204.9483642578125,128.39770298313388,76.55066127467862,0.3735119406875703,37.35119406875703,-76.55066127467862,Fail,,default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakLoad,W,Both,117.45973205566406,133.44180277588939,15.982070720225323,0.1197680965616646,11.97680965616646,15.982070720225323,Warn,,default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,heating.peakHour,hourOfYear,Both,1447,774,673,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -affc43b0b5b3445f85d5cea8a7b88d4f,heating.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -affc43b0b5b3445f85d5cea8a7b88d4f,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,area,m2,Both,38.98503142608865,38.98503142608865,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,volume,m3,Both,121.59536233412693,121.59536233412693,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.designLoad,W,Both,1065.076171875,1826.2977051410624,761.2215332660624,0.41681130689876544,41.68113068987655,761.2215332660624,Fail,,default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakLoad,W,Both,1025.7105712890625,1789.9830426085152,764.2724713194527,0.4269719059492821,42.69719059492821,764.2724713194527,Fail,,default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.peakHour,hourOfYear,Both,1443,775,668,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -c7df54b3c0064fc5a7c6e6012dbb9b8e,heating.unmetHours,h,Both,114,0,114,1,100,-114,Fail,,default,0.05,0.15,1,24,0.5,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -c7df54b3c0064fc5a7c6e6012dbb9b8e,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -d8294f2a30b445ca9f6ba83c8dcf4903,area,m2,Both,18.080018143643976,18.080018143643976,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d8294f2a30b445ca9f6ba83c8dcf4903,volume,m3,Both,51.52805170938531,51.52805170938531,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.designLoad,W,Both,564.768798828125,917.4634857295723,352.6946869014473,0.3844236772224046,38.44236772224046,352.6946869014473,Fail,,default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakLoad,W,Both,559.67822265625,936.5975380258114,376.91931536956145,0.4024346638408247,40.243466384082474,376.91931536956145,Fail,,default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,heating.peakHour,hourOfYear,Both,1441,727,714,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -d8294f2a30b445ca9f6ba83c8dcf4903,heating.unmetHours,h,Both,69,0,69,1,100,-69,Fail,,default,0.05,0.15,1,24,0.5,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakLoad,W,OpenStudioOnly,,28.70645886203079,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.peakHour,hourOfYear,OpenStudioOnly,,7991,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -d8294f2a30b445ca9f6ba83c8dcf4903,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,area,m2,Both,21.280022127733165,21.280022127733165,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,volume,m3,Both,57.3391691981517,57.3391691981517,0,0,0,0,Match,,default,0.05,0.15,1,24,0.01,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.designLoad,W,Both,549.6607055664062,909.1363260313252,359.47562046491896,0.39540342869605327,39.540342869605325,359.47562046491896,Fail,,default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakLoad,W,Both,529.828857421875,989.0652526225581,459.2363952006831,0.4643135465359781,46.431354653597815,459.2363952006831,Fail,,default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.peakHour,hourOfYear,Both,1457,919,538,,,,Fail,circular hour difference; informational (excluded from the numerical status),default,0.05,0.15,1,24,0,true -d94f4dbee3d24ff1bcb341e2c5c6ec6d,heating.unmetHours,h,Both,29,0,29,1,100,-29,Fail,,default,0.05,0.15,1,24,0.5,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.designLoad,W,TasOnly,0,,,,,,N/A,unavailable (TasOnly),default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakLoad,W,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,10,false -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.peakHour,hourOfYear,OpenStudioOnly,,0,,,,,N/A,unavailable (OpenStudioOnly),default,0.05,0.15,1,24,0,true -d94f4dbee3d24ff1bcb341e2c5c6ec6d,cooling.unmetHours,h,Both,0,0,0,0,0,0,Match,,default,0.05,0.15,1,24,0.5,false diff --git a/docs/benchmark/reports/2026-07-26-design-loads/comparison.md b/docs/benchmark/reports/2026-07-26-design-loads/comparison.md deleted file mode 100644 index d562007..0000000 --- a/docs/benchmark/reports/2026-07-26-design-loads/comparison.md +++ /dev/null @@ -1,292 +0,0 @@ -# Benchmark comparison: TAS vs OpenStudio - -Independent comparison of two engine-neutral benchmark documents. Tolerance bands are -**provisional reporting buckets**, not validated thresholds. The overall gate is the worst of -the numerical, coverage, provenance and reconciliation statuses. Peak-hour bands and the two -whole-model peak-load bands are informational and are excluded from the numerical status — they are -still calculated and shown, and rows carrying such a band are marked in the Note column; -reconciliation is a separate within-document diagnostic status that also contributes to the -overall gate. - -## Summary - -| Field | Value | -| --- | --- | -| Tolerance profile | default | -| Gate status (overall) | Fail | -| Numerical status | Fail | -| Coverage status | Warn | -| Provenance status | Pass | -| Reconciliation status | Pass | -| Metrics matched | 48 | -| Metrics warned | 1 | -| Metrics failed | 43 | -| Metrics not applicable | 56 | -| Required metrics compared | 92 of 148 | -| Required metrics unavailable | 56 | -| TAS schema version | 1.0.0 | -| OpenStudio schema version | 1.0.0 | - -## Coverage - -How much of the two runs was actually compared. Required metrics are the whole-model metrics plus -the metrics of uniquely matched spaces; the metrics of one-sided, duplicated or ambiguous spaces are -not counted here because those spaces are reported under space alignment instead. An unavailable -metric is never a numerical failure, so it is recorded here — otherwise missing results could read as -a clean pass. - -| Field | Value | -| --- | --- | -| Coverage status | Warn | -| Required metrics | 148 | -| Compared | 92 | -| Unavailable (not compared) | 56 | -| Unmatched spaces | 0 | -| Ambiguous (split) space names | 0 | -| Duplicated space identities | 0 | - -**Coverage is INCOMPLETE**, so this comparison must not be described as a complete result: - -- 56 of 148 required metrics were unavailable on at least one side (reported as N/A, never as a numerical failure). - -## Provenance compatibility - -Compatible: both runs share the same canonical model, weather and design-day basis, with the expected engine and route on each side. - -## Tolerance profile - -Provisional reporting bands (not validated thresholds). Recorded so a changed profile is never hidden behind an unchanged name. - -| Setting | Value | -| --- | --- | -| Name | default | -| Warn (relative) | 0.05 | -| Fail (relative) | 0.15 | -| Peak-hour warn (h) | 1 | -| Peak-hour fail (h) | 24 | -| Default near-zero floor | 0 | -| Near-zero floor (kWh) | 1 | -| Near-zero floor (Wh) | 1000 | -| Near-zero floor (kW) | 0.01 | -| Near-zero floor (W) | 10 | -| Near-zero floor (m2) | 0.01 | -| Near-zero floor (m3) | 0.01 | -| Near-zero floor (h) | 0.5 | - -## Provenance - -| Field | TAS | OpenStudio | -| --- | --- | --- | -| Engine kind | TAS | OpenStudio | -| Engine name | Tas | EnergyPlus | -| Engine version | 9.5.7.0 | 25.1.0 | -| SDK version | | 3.10.0 | -| Route | Native-TAS | Native-OpenStudio | -| Weather | HUN_CEN_Budapest.Met.Center.128400_TMYx | HUN_CEN_Budapest.Met.Center.128400_TMYx | -| Design-day source | EmbeddedModel | EmbeddedModel | -| Run state | Success | Success | -| Source model | 000000_SAM_AnalyticalModel | 000000_SAM_AnalyticalModel | -| Source model GUID | a527e890fceb465fbb50f4b6874f3a4c | a527e890fceb465fbb50f4b6874f3a4c | -| SAM commit | abcd617887f6792047023820f3efd2463fc5c6b3 | abcd617887f6792047023820f3efd2463fc5c6b3 | - -## Model metrics - -> **Caveats (apply even when values pass):** whole-model peak-load semantics differ between -> engines — OpenStudio reports a coincident total, TAS a building-profile maximum — so peak-load -> comparisons are informational, not equivalence claims. `peakHeatingLoad` and `peakCoolingLoad` -> are therefore banded and shown below but **excluded from the numerical status**, exactly like the -> peak-hour metrics; promoting them into the gate needs the B4 corpus and energy-modeller review. -> Every other metric — annual energy, per-space peak loads, unmet hours, geometry — gates normally. -> The primary conditioning pairing (OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing) -> is a **candidate** pending validation. A tolerance profile cannot remove these limitations. - -| Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | -| --- | --- | --- | --- | --- | --- | --- | --- | -| consumptionHeating | kWh | 4331.961588091359 | 1751.1764825992748 | 2580.785105492084 | 59.57543835537944 | Fail | | -| consumptionCooling | kWh | 0 | 5.2499930426550705 | 5.2499930426550705 | 100 | Fail | | -| peakHeatingLoad | kW | 3.93393212890625 | 5.720615732872154 | 1.7866836039659035 | 31.23236531513824 | Fail | informational (excluded from the numerical status) | -| peakHeatingHour | hourOfYear | 919 | 775 | 144 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| peakCoolingLoad | kW | 0 | 0.0402250938503556 | 0.0402250938503556 | 100 | Fail | informational (excluded from the numerical status) | -| peakCoolingHour | hourOfYear | 0 | 7974 | 786 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| floorArea | m2 | 167.95195245742798 | 167.95195207345205 | 3.839759301627055E-07 | 2.2862248669602975E-07 | Match | | -| volume | m3 | 470.1821608543396 | 470.1821620538829 | 1.1995433055744797E-06 | 2.5512309959496334E-07 | Match | | - -## Model-total vs sum-of-spaces reconciliation - -Additive quantities only; non-additive peak loads are not reconciled. Only uniquely-matched spaces are summed; unmatched or ambiguous spaces make a reconciliation incomplete. - -| Engine | Quantity | Unit | Model total | Sum of spaces | Abs diff | Band | Used/Missing/Excluded | Complete | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | -| TAS | floorArea | m2 | 167.95195245742798 | 167.95195207345202 | 3.839759585844149E-07 | Match | 14/0/0 | yes | -| TAS | volume | m3 | 470.1821608543396 | 470.1821620538828 | 1.199543191887642E-06 | Match | 14/0/0 | yes | -| OpenStudio | floorArea | m2 | 167.95195207345205 | 167.95195207345202 | 2.842170943040401E-14 | Match | 14/0/0 | yes | -| OpenStudio | volume | m3 | 470.1821620538829 | 470.1821620538828 | 1.1368683772161603E-13 | Match | 14/0/0 | yes | - -## Space alignment - -| Field | Value | -| --- | --- | -| TAS spaces | 14 | -| OpenStudio spaces | 14 | -| Matched by GUID | 14 | -| Matched by name | 0 | - -**Only in TAS:** none - -**Only in OpenStudio:** none - -**Duplicate TAS GUIDs:** none - -**Duplicate OpenStudio GUIDs:** none - -**Duplicate TAS names:** none - -**Duplicate OpenStudio names:** none - -**Ambiguous (split) name matches:** none - -## Space metrics - -| Space | Match | Metric | Unit | TAS | OpenStudio | Abs diff | Rel % | Band | Note | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| 025eb80f65d745b799511b78ac2f82fb | Guid | area | m2 | 6.6920779999999995 | 6.6920779999999995 | 0 | 0 | Match | | -| 025eb80f65d745b799511b78ac2f82fb | Guid | volume | m3 | 9.118059598283878 | 9.118059598283878 | 0 | 0 | Match | | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 025eb80f65d745b799511b78ac2f82fb | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | area | m2 | 21.280010175465385 | 21.280010175465385 | 0 | 0 | Match | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | volume | m3 | 57.33913699268221 | 57.33913699268221 | 0 | 0 | Match | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.designLoad | W | 574.0481567382812 | 1132.105378358097 | 558.0572216198157 | 49.29375235626663 | Fail | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakLoad | W | 565.50927734375 | 1185.0374713731348 | 619.5281940293848 | 52.279207113300885 | Fail | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | heating.unmetHours | h | 65 | 0 | 65 | 100 | Fail | | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakLoad | W | | 15.588918884437554 | | | N/A | unavailable (OpenStudioOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.peakHour | hourOfYear | | 7974 | | | N/A | unavailable (OpenStudioOnly) | -| 0872cd39962f43c3b62a6e031a76c24a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | area | m2 | 3.647967799712404 | 3.647967799712404 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | volume | m3 | 10.396708229180378 | 10.396708229180378 | 0 | 0 | Match | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.designLoad | W | 199.83349609375 | 132.9146643279224 | 66.9188317658276 | 33.48729470980844 | Fail | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakLoad | W | 199.82528686523438 | 141.7703965184828 | 58.05489034675156 | 29.052824723782223 | Fail | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.peakHour | hourOfYear | 1441 | 918 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | heating.unmetHours | h | 4761 | 0 | 4761 | 100 | Fail | | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 13a0403ab3a543608613b0ce9bf52d92 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | area | m2 | 2.9450000000000016 | 2.9450000000000016 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | volume | m3 | 5.746544632499992 | 5.746544632499992 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.designLoad | W | 28.741456985473633 | | | | N/A | unavailable (TasOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 21bdbb806b4f4e2fbad0632b19fdf14b | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | area | m2 | 2.355387206192353 | 2.355387206192353 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | volume | m3 | 6.712853537648204 | 6.712853537648204 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.designLoad | W | 69.60518646240234 | | | | N/A | unavailable (TasOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakLoad | W | 52.024513244628906 | 0 | 52.024513244628906 | 100 | Fail | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.peakHour | hourOfYear | 1441 | 0 | 1441 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 312fab2f1ba64756b47ccc3dcb21ac8a | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | area | m2 | 3.0986077487352315 | 3.0986077487352315 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | volume | m3 | 9.007679927026205 | 9.007679927026205 | 0 | 0 | Match | | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.designLoad | W | 137.82980346679688 | 241.94985081273356 | 104.12004734593668 | 43.03373074882547 | Fail | | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakLoad | W | 117.870849609375 | 262.9986126690514 | 145.1277630596764 | 55.18195004408647 | Fail | | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.peakHour | hourOfYear | 1441 | 919 | 522 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 354b8786b4714f3c9941cec051a93361 | Guid | heating.unmetHours | h | 67 | 0 | 67 | 100 | Fail | | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 354b8786b4714f3c9941cec051a93361 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | area | m2 | 5.03787628653 | 5.03787628653 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | volume | m3 | 7.564547888993382 | 7.564547888993382 | 0 | 0 | Match | | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | heating.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakLoad | W | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.peakHour | hourOfYear | | | | | N/A | unavailable (Neither) | -| 4377cbd6712349e68fdae8814ff9c5d9 | Guid | cooling.unmetHours | h | 0 | | | | N/A | unavailable (TasOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | area | m2 | 23.55004650825489 | 23.55004650825489 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | volume | m3 | 58.340316505330264 | 58.340316505330264 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.designLoad | W | 379.9020080566406 | 237.9464743064166 | 141.95553375022402 | 37.36635520206555 | Fail | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakLoad | W | 132.86868286132812 | 255.3860902131586 | 122.51740735183049 | 47.97340655850561 | Fail | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.peakHour | hourOfYear | 1442 | 919 | 523 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 6e7ef8487b714da3b730e0f226af927e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | area | m2 | 9.409999999999991 | 9.409999999999991 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | volume | m3 | 26.966204214472434 | 26.966204214472434 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.designLoad | W | 366.3569641113281 | 170.60334873223655 | 195.75361537909157 | 53.43248103770348 | Fail | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakLoad | W | 366.33624267578125 | 190.23285633880042 | 176.10338633698083 | 48.07151622528315 | Fail | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.peakHour | hourOfYear | 1444 | 919 | 525 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 7ee49aac87b24671bb00ed1b291806a1 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | area | m2 | 4.634866891556 | 4.634866891556 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | volume | m3 | 28.16450759620351 | 28.16450759620351 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.designLoad | W | 69.23214721679688 | 42.95932098398786 | 26.272826232809017 | 37.9488825480698 | Fail | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakLoad | W | 11.025825500488281 | 39.29050142981706 | 28.26467592932878 | 71.93768188430163 | Fail | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.peakHour | hourOfYear | 1452 | 774 | 678 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| 8d2d2a0234584fe4b171e5a9373f1b4d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | area | m2 | 6.955039759539971 | 6.955039759539971 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | volume | m3 | 20.363019689898447 | 20.363019689898447 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.designLoad | W | 204.9483642578125 | 128.39770298313388 | 76.55066127467862 | 37.35119406875703 | Fail | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakLoad | W | 117.45973205566406 | 133.44180277588939 | 15.982070720225323 | 11.97680965616646 | Warn | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.peakHour | hourOfYear | 1447 | 774 | 673 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | heating.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| affc43b0b5b3445f85d5cea8a7b88d4f | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | area | m2 | 38.98503142608865 | 38.98503142608865 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | volume | m3 | 121.59536233412693 | 121.59536233412693 | 0 | 0 | Match | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.designLoad | W | 1065.076171875 | 1826.2977051410624 | 761.2215332660624 | 41.68113068987655 | Fail | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakLoad | W | 1025.7105712890625 | 1789.9830426085152 | 764.2724713194527 | 42.69719059492821 | Fail | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.peakHour | hourOfYear | 1443 | 775 | 668 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | heating.unmetHours | h | 114 | 0 | 114 | 100 | Fail | | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| c7df54b3c0064fc5a7c6e6012dbb9b8e | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | area | m2 | 18.080018143643976 | 18.080018143643976 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | volume | m3 | 51.52805170938531 | 51.52805170938531 | 0 | 0 | Match | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.designLoad | W | 564.768798828125 | 917.4634857295723 | 352.6946869014473 | 38.44236772224046 | Fail | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakLoad | W | 559.67822265625 | 936.5975380258114 | 376.91931536956145 | 40.243466384082474 | Fail | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.peakHour | hourOfYear | 1441 | 727 | 714 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | heating.unmetHours | h | 69 | 0 | 69 | 100 | Fail | | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakLoad | W | | 28.70645886203079 | | | N/A | unavailable (OpenStudioOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.peakHour | hourOfYear | | 7991 | | | N/A | unavailable (OpenStudioOnly) | -| d8294f2a30b445ca9f6ba83c8dcf4903 | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | area | m2 | 21.280022127733165 | 21.280022127733165 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | volume | m3 | 57.3391691981517 | 57.3391691981517 | 0 | 0 | Match | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.designLoad | W | 549.6607055664062 | 909.1363260313252 | 359.47562046491896 | 39.540342869605325 | Fail | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakLoad | W | 529.828857421875 | 989.0652526225581 | 459.2363952006831 | 46.431354653597815 | Fail | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.peakHour | hourOfYear | 1457 | 919 | 538 | | Fail | circular hour difference; informational (excluded from the numerical status) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | heating.unmetHours | h | 29 | 0 | 29 | 100 | Fail | | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.designLoad | W | 0 | | | | N/A | unavailable (TasOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakLoad | W | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.peakHour | hourOfYear | | 0 | | | N/A | unavailable (OpenStudioOnly) | -| d94f4dbee3d24ff1bcb341e2c5c6ec6d | Guid | cooling.unmetHours | h | 0 | 0 | 0 | 0 | Match | | - From 31492d6388d33b92d030fb580a18eaac11698ad4 Mon Sep 17 00:00:00 2001 From: Michal Dengusiak Date: Mon, 5 Oct 2026 22:07:52 +0200 Subject: [PATCH 11/11] docs: remove internal AI/process provenance wording Benchmark implementation plan: drop the model-allocation table, the paste-ready first-implementation prompt and the mandatory-git/session-continuity block (replaced by one sentence on branch hygiene); neutralise stakeholder wording. Co-Authored-By: Claude Sonnet 5.5 --- docs/benchmark/IMPLEMENTATION-PLAN.md | 83 +++------------------------ 1 file changed, 7 insertions(+), 76 deletions(-) diff --git a/docs/benchmark/IMPLEMENTATION-PLAN.md b/docs/benchmark/IMPLEMENTATION-PLAN.md index 5d1d5ef..3028e3f 100644 --- a/docs/benchmark/IMPLEMENTATION-PLAN.md +++ b/docs/benchmark/IMPLEMENTATION-PLAN.md @@ -1,6 +1,6 @@ # SAM TAS ↔ OpenStudio Benchmark Programme — Implementation Plan (B0–B6) -> **Revision 3** — incorporates stakeholder corrections: (1) `feature/benchmark-*` branch names; (2) unavailable metrics carry `value: null`; (3) dual model hashes; (4) build-order verified to live in per-repo CI, not a SAM PR; (5) conditioning equivalence is a **candidate** in B0, frozen only after the first TAS SingleBox validation; (6) B5 GH host deferred to B5 kickoff; (7) **commit and push the feature branch after every stage** for multi-laptop continuity; plus a per-milestone **model-allocation** plan and a **mandatory Git rules** block for every implementation prompt. +> **Revision 3:** (1) `feature/benchmark-*` branch names; (2) unavailable metrics carry `value: null`; (3) dual model hashes; (4) build-order verified to live in per-repo CI, not a SAM PR; (5) conditioning equivalence is a **candidate** in B0, frozen only after the first TAS SingleBox validation; (6) B5 GH host deferred to B5 kickoff. ## Context @@ -8,7 +8,7 @@ We need a **defensible, repeatable benchmark** comparing two independent buildin The **primary gate** harmonises *conditioning semantics* — OpenStudio **Ideal Loads** vs the equivalent idealised TAS route (thermostats + IZAMs + TBD sizing). This pairing is a **candidate** until the first TAS SingleBox run confirms meaningful conditioning, annual energy and loads (see D10). Production HVAC (TAS **TPD** plant) is deferred to a later, separately-labelled *informational* comparison. All work branches off **`sow/2026-Q3`** in each repo and merges back into `sow/2026-Q3`. -This plan is grounded in direct repository inspection (findings in §2). Three architecture forks were resolved with the stakeholder (§3). +This plan is grounded in direct repository inspection (findings in §2). Three architecture forks are resolved in §3. --- @@ -128,7 +128,7 @@ This plan is grounded in direct repository inspection (findings in §2). Three a | # | Decision | Resolution | Rationale | |---|----------|------------|-----------| | D1 | Schema shape | **Independent, versioned DTOs** (`SAM.Analytical.Benchmark`, netstandard2.0, STJ-only, zero deps). | Result types are enum-keyed graph bags with misleading unit labels; comparator must not drag in `SAM.Analytical`/engines. Independent DTOs = clean isolation + explicit versioned wire contract. | -| D2 | Schema location | **Standalone lib in the `SAM_Validation` repo** (stakeholder-confirmed), output to `SAM_Validation\build\`. | Honours programme decision #1; versioned alongside its comparator. Build-order handled in per-repo CI (see D3-build below), **not** a SAM PR. | +| D2 | Schema location | **Standalone lib in the `SAM_Validation` repo**, output to `SAM_Validation\build\`. | Honours programme decision #1; versioned alongside its comparator. Build-order handled in per-repo CI (see D3-build below), **not** a SAM PR. | | D3 | Invocation | **Three thin console CLIs** (`benchmark-openstudio`, `benchmark-tas`, `benchmark-compare`) over **one shared CLI host** in the schema lib; extensible. | A single cross-engine exe is impossible under isolation rules. Shared host = arg parsing, invariant-culture JSON I/O, stable exit codes, written once. | | D3-build | Build order | B1a: schema project outputs to `SAM_Validation\build\` + is added to SAM_Validation's solution (its CI already builds the solution). B1b/B2: each engine repo's **own** `.github/workflows/build.yml` gains a step to clone `SAM_Validation` @ matching branch and build **only** `SAM.Analytical.Benchmark.csproj` → `SAM_Validation\build\`. Root `BuildAlls*` (untracked) updated locally only. | Verified: build order is tracked per-repo in CI, not in SAM nor the untracked root. Keeps each change inside the repo that needs it; no implicit cross-repo edits. | | D4 | Canonical units | Schema stores **explicit unit tokens**; producers convert at emit; **pin consumption magnitude empirically in B1**. | Enum labels contradict the legacy consumer; requirement forbids name-based inference. | @@ -164,7 +164,7 @@ If B1a's build-order verification finds the change must touch a repo not listed ## 5. Detailed implementation steps per milestone -> Every implementation session must begin with the **Mandatory Git & repository rules** (§13) and verify that named files live in the stated repo before editing. +> Before editing, verify that the files named in each milestone live in the stated repo. ### B0 — Methodology & contracts (docs only) - **Objective:** freeze the engine-neutral contract: metric definitions, canonical units, sign, tolerance bands, provenance fields (incl. dual hashes §7), missing-data semantics (`available`/`null` invariant), space-alignment rules, the four routes. Record the **candidate** conditioning pairing (D10) — explicitly *not yet frozen*. @@ -337,7 +337,7 @@ All branches created from `sow/2026-Q3`; all PRs target `sow/2026-Q3`. | 8 | B5 | *selected GH repo* | `feature/benchmark-b5-gh-viewer` | | 9 | B6 | SAM_Validation | `feature/benchmark-b6-docs-report` | -Ordering rule: **B1a first**; B1b/B2/B3 parallelisable after B1a (B2 also gates the D10 conditioning freeze); B4 after B1b; B5/B6 after B3 (B6 also after B2+B4). **One milestone per session.** +Ordering rule: **B1a first**; B1b/B2/B3 parallelisable after B1a (B2 also gates the D10 conditioning freeze); B4 after B1b; B5/B6 after B3 (B6 also after B2+B4). One milestone per PR. --- @@ -452,75 +452,6 @@ Ordering rule: **B1a first**; B1b/B2/B3 parallelisable after B1a (B2 also gates --- -## 11. Recommended first implementation prompt (B0 only) - -> Prepend the **Mandatory Git & repository rules** (§13) to this prompt. -> -> **Implement milestone B0 (methodology & contracts) for the SAM TAS↔OpenStudio benchmark.** Repo `SAM_Validation`, branch `feature/benchmark-b0-methodology` created from `sow/2026-Q3`; PR targets `sow/2026-Q3`. **Docs only — no code.** -> -> Create under `SAM_Validation/docs/benchmark/`: -> - `METHODOLOGY.md` — the two-stage file-mediated workflow; the four routes (Native SAM→TAS, Native SAM→OpenStudio, shared-gbXML→both [optional], later TPD production [informational]); the **candidate** primary conditioning pairing **OpenStudio Ideal Loads ↔ TAS thermostats + IZAMs + TBD sizing** (state explicitly it is *not frozen* until the first TAS SingleBox run in B2 confirms meaningful conditioning, annual energy and loads); production HVAC is a separately-labelled later comparison. -> - `METRICS.md` — each metric's definition, canonical unit, sign convention, exact both-route SAM result-parameter source; include the §2.10 availability matrix; **explicitly list excluded metrics** (per-space annual energy; monthly profiles) with evidence; flag caveats: (a) peak-load definition differs (OpenStudio coincident-total vs TAS building-profile max); (b) model consumption stored-magnitude (Wh vs kWh) **unresolved, pinned empirically in B1b**. -> - `SCHEMA.md` — prose spec of the v1.0.0 schema in §7: provenance (**dual hashes `sourceFileHash` + `canonicalModelHash` + `canonicalizationVersion`**), model, spaces; every metric a `MetricValue` with explicit unit + `available` flag and the **availability invariant** (`available=false ⇒ value null`; real 0 ⇒ 0+available; **never 0 for unavailable**); `schemaVersion` semver policy + comparator compatibility (equal major required; minor drift warns). -> - `TOLERANCES.md` — provisional bands **±5% warn / ±15% fail** as **configurable reporting bands, NOT validated thresholds**; near-zero absolute-floor rule; unavailable⇒"N/A" (never fail); peak-hour circular/year-boundary comparison; model-total vs sum-of-spaces reconciliation. -> - `GLOSSARY.md` — engine, route, provenance, canonical unit, tolerance band, gate status, canonicalization. -> -> Constraints: no absolute/machine-specific paths; align with verified findings (System.Text.Json, Space Guid identity, `Query.Source()` provenance, `IntervalHourOfYear` peak hours). Keep each doc scannable. Commit in three boundaries: (1) methodology+glossary; (2) metrics+units; (3) tolerances+schema-spec. Open a PR to `sow/2026-Q3` titled "B0 — benchmark methodology & contracts". Do not modify any code or existing tests. Mark the energy-modeller review as a required human check before merge. +## 11. Branch hygiene ---- - -## 12. Model allocation & session discipline - -Run **one milestone per session**; do not hand B1–B6 to a single model in one continuous run. - -| Milestone | Recommended model | -|---|---| -| B0 documentation | **Codex 5.6-SOL High** | -| B1a schema, serializer, deterministic tests | **Codex 5.6-SOL High** | -| B1b OpenStudio producer | **Codex 5.6-SOL High** | -| B2 TAS producer | **Kimi**, followed by **Codex review** | -| B3 comparator & reports | **Codex 5.6-SOL High** | -| B4 corpus preparation | **Kimi** | -| B2b OpenStudio SDK spike | **Codex 5.6-SOL High** | -| B5 Grasshopper components | **Codex 5.6-SOL High** | -| B6 documentation & report assembly | **Kimi** | - -Kimi fits B2 (well-specified orchestration around existing APIs) and B4/B6 (mechanical corpus/doc assembly). Deterministic-serialization, comparator, and SDK-spike work go to Codex 5.6-SOL High. **Immediate next step: B0 with Codex 5.6-SOL High; after B0 is reviewed and merged, start B1a in a fresh session.** - -Because sessions run on different laptops, **each session ends by committing and pushing its feature branch(es)** so the next session resumes from remote — see the *Multi-laptop continuity* rule in §13. - ---- - -## 13. Mandatory Git & repository rules (include in EVERY implementation prompt) - -All implementation work starts from the current `sow/2026-Q3` branch in each affected repository. For every repository modified: - -1. Confirm the working tree is clean. -2. Fetch current remote branches. -3. Check out `sow/2026-Q3`. -4. Fast-forward from `origin/sow/2026-Q3`. -5. Create the milestone-specific `feature/benchmark-*` branch. -6. Commit only files belonging to that repository and milestone. -7. Push the feature branch. -8. Open the PR against `sow/2026-Q3` — never `master` or another feature branch. - -```bash -git status -git fetch origin -git checkout sow/2026-Q3 -git pull --ff-only origin sow/2026-Q3 -git checkout -b -``` - -- If a repo lacks `sow/2026-Q3`, or it cannot fast-forward cleanly, **stop before modifying files** and report the exact repo and Git state. -- **Do not reuse one branch across repositories.** Each repo gets its own feature branch, commits and PR. -- **Before implementation, verify the files named in the plan actually live in the stated repository.** In particular, confirm the build-order change lives in each engine repo's own `.github/workflows/build.yml` (verified: the root `BuildAlls*` files are untracked and belong to no repo; do **not** rely on or commit them). - -### Multi-laptop continuity (REQUIRED — Michal works across several laptops) - -Michal moves between several laptops with different Windows user profiles (see [[two-laptop-workspace-paths]]). To avoid stranding work on one machine: - -- **Commit and push the feature branch at the end of every B stage/session** — a stage is only "done for this session" once `git push` has succeeded. Never leave a milestone's work committed-but-unpushed, or uncommitted, on a single laptop. -- **Push work-in-progress commits too**, not only at PR time, so any laptop always has the latest state. Keep messages conventional (`docs:`/`feat:`/`fix:`/…) and end them with the `Co-Authored-By` trailer. -- **When resuming a stage on another laptop**, do NOT recreate the branch: `git fetch origin` → `git checkout feature/benchmark-` → `git pull --ff-only origin feature/benchmark-`, then continue. If the local branch is behind and cannot fast-forward, stop and report before editing. -- Because each repo has its own feature branch, **push every affected repo's branch** at stage end (a milestone that touches two repos must push both). +Each milestone branches from `sow/2026-Q3` (as a `feature/benchmark-*` branch) in every affected repository and opens its PR back into `sow/2026-Q3`.