Decision Log¶
Chronological record of project decisions made in conversations, meetings, or reviews, so collaborators and agents share the same context.
- One entry per decision, newest at the bottom, numbered
D-NNN. - Record: date, who decided, the decision, and why (if given). Link the ADR when a decision needs a full rationale.
- Architectural decisions with trade-offs get an ADR in this folder (
ADR-NNN-<topic>.md); the log entry links to it. - Never rewrite a past entry. To change a decision, add a new entry that supersedes it (
Supersedes D-NNN) and mark the old oneSuperseded by D-NNN.
| Status | Meaning |
|---|---|
| Accepted | In force |
| Provisional | Working assumption; revisit at the noted point |
| Superseded | Replaced by a later entry |
D-001 — Commit message convention¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- Commit messages use
type(scope): summary, withfeature(notfeat) for new behavior. Full type list in AGENTS.md. - Supersedes the plain-sentence examples in the repository spec §25.
D-002 — No tool attribution in commits¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- Commits are authored by the configured human git user. No agent, model, or provider names, attribution footers, or
Co-Authored-Bytrailers for tools. Matches repository spec §22 and §25.
D-003 — Target Rhino 8 only¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- The Grasshopper plugin targets Rhino 8. Rhino 7 is not supported. Exact target frameworks are fixed in the foundation ADR (Stage 0).
D-004 — Branching and merging¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- Trunk-based (GitHub Flow):
mainplus short-lived branches named<type>/<scope>-<short-description>, mirroring commit types. - Branches are integrated by rebase merge (linear history, individual commits preserved). No merge commits, no squash.
- No PR review is required; authors may self-merge.
- No force pushes to
main.
D-005 — No CI/CD¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- No CI/CD pipelines are configured. Build and test verification runs locally before merging to
main.
D-006 — Downstream energy model: ClimateStudio in Grasshopper¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Provisional — revisit after the ClimateStudio capability spike (Stage 0)
- The downstream EnergyPlus model generator was not yet chosen; assume ClimateStudio running in Grasshopper. This replaces the spec's assumption of an external IDF/REST API for now. The
IEnergyModelExporterboundary and the versioned JSON intermediate model are kept.
D-007 — Record chat decisions in docs¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- The project expects collaboration across several users and agents. Important decisions made in chat conversations are written to this log (or an ADR) in the same working session.
D-008 — Staged roadmap with a precedent study¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted (roadmap details pending review)
- Work follows a staged roadmap derived from the spec's milestones, with a foundation stage first and an early minimal JSON export plus Grasshopper preview.
- A precedent study stage indexes reference books on housing plan design and synthesizes the parametric floor plan generation mechanism before generators are built.
- Roadmap: docs/plans/2026-09-30-implementation-roadmap.md.
D-009 — Ground-truth zoning stops at the dwelling unit¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- The detailed reference model (Z0) has at most one thermal zone per dwelling unit. Rooms inside a dwelling unit are never modelled. Non-dwelling spaces (corridors, stairs, cores, service) remain their own zones.
D-010 — Setpoint aggregation method is a parameter¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Superseded by D-027
- When zones with different setpoint schedules are merged, the aggregation method is a parameter of the transformation and is recorded in the model metadata.
- Initial methods: floor-area weighted and exposed-surface-area weighted, each
T*(t) = Σ wᵢTᵢ(t) / Σ wᵢwithwᵢthe source zone's floor area or exposed envelope area. - Open details (definition of "exposed", default vs required parameter, zero-weight handling) are settled in ADR-005 during Stage 1, together with an update to the research brief §7.
D-011 — Current precedent source: Floor Plan Manual Housing¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- The precedent study (Stage 2) currently looks at Heckmann & Schneider, Floor Plan Manual Housing. The other reference books will be decided later.
- Noted only: no precedent work is started until Stage 2 is scheduled.
D-012 — No PI sign-off for research-rule ADRs¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- ADRs that change research rules do not require sign-off from Timur Dogan. They still require the research documentation update and a decision-log entry (GLOBAL.md, scientific rule 7).
D-013 — No GitHub milestones or issues for now¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- Progress is tracked through the roadmap in
docs/plans/and the per-stage plans. GitHub milestones and issues are not used for now.
D-014 — Setpoint aggregation details¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Superseded by D-027
- Settles the open details of D-010 (to be written up in ADR-005):
- The setpoint aggregation method is a required input with no default.
- Exposed surface area counts only surfaces with an outdoor boundary condition: exterior walls (including their windows) and roofs. Ground-contact, adiabatic, and inter-zone surfaces are excluded.
- When the exposed-area weights of the merged zones sum to zero (only interior zones, e.g. corridor + core), the weights fall back to floor area. The fallback emits a warning diagnostic, and the method actually applied is recorded in the model metadata, so it is never silent.
- Consequence: when the weights do not sum to zero, a source zone with no exposed area gets weight 0, so its setpoint does not affect the merged setpoint.
D-015 — Pipeline first, typologies and precedents later¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- The entire pipeline must work end to end with a simple linear plan generator before any reference-book typology work or additional typologies are attempted.
- The precedent study (D-011) moves after the working pipeline. Supersedes the ordering in D-008 ("precedent study before generators are built").
D-016 — Pipeline ends at Grasshopper-ready inputs; no simulation in the pipeline¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted; refined by D-130 (the repository may run EnergyPlus outside the pipeline) and D-132 (batches and their results in the repository)
- The pipeline's deliverable is Grasshopper output usable for (1) visualization and (2) wiring into a ClimateStudio definition. Simulation happens only afterwards, outside the pipeline.
- ClimateStudio does not need to be run for now; the Stage 0 ClimateStudio capability spike is dropped. Refines D-006.
D-017 — Output is Grasshopper objects, not JSON¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- BEMGen is a Grasshopper plugin. Stages exchange custom typed Grasshopper instances, not a JSON intermediate schema.
- Supersedes the JSON intermediate-model export in the repository spec §14 and research brief §16, and the JSON stages in the draft roadmap. GLOBAL.md, AGENTS.md, README, and the roadmap are updated to match.
D-018 — Pipeline architecture and vocabulary¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted (open details in the roadmap revision)
- Reference flow given by the decision owner:
program presets (x, y, ...) → LinearPlanGenerator : PlanGenerator → IGeneratedPlan (previewable, custom typed instance)
IGeneratedPlan → IPlanSimplifier { NoSimplification, PerimeterCore, ... } → IFloor
IFloor[] → IFloorAggregator { Stack, FloorAreaMultiplier, SingleZoneMerged } → IGeneratedBuilding
IGeneratedBuilding → Convert2BEM → ClimateStudio-ready input(s)
IGeneratedBuilding → Convert2IDF → EnergyPlus simulation-ready IDF
- Horizontal zoning simplification lives in
IPlanSimplifier; vertical simplification lives inIFloorAggregator.
D-019 — Air-change-based loads are volume weighted¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- Loads expressed in air changes per hour (infiltration, ventilation, and similar) aggregate by zone volume:
ACH* = Σ Vᵢ·ACHᵢ / Σ Vᵢ, which conserves total air volume flowΣ Vᵢ·ACHᵢ. - Written up in ADR-007 (load-basis aggregation) together with an update to the research brief §9.
D-020 — Convert2IDF comes after Convert2BEM¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
Convert2IDFis implemented in a later stage, after the full pipeline works throughConvert2BEM.
D-021 — SingleZoneMerged means the whole building as one zone¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- The
SingleZoneMergedfloor aggregator merges the entire building into one thermal zone. One zone per floor is a plan simplifier, not a floor aggregator.
D-022 — Windows: one parameter per program preset, centered on every exterior wall¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Superseded by D-039
- Windows are specified initially by a single parameter on the program preset (taken to be window-to-wall ratio).
- The plan generator distributes glazing over each zone's exterior walls from that parameter.
- Every window is centered on its wall, at all levels of detail including the most detailed one. There is no explicit window positioning.
D-023 — Convert2BEM targets ClimateStudio; no reference definition yet¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Provisional — revisit when a ClimateStudio reference definition is available
Convert2BEMmust produce inputs that wire into ClimateStudio components. No ClimateStudio definition is available yet, so its output starts in a neutral, documented form and is adapted later.
D-024 — Program presets¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- What the reference flow called "room presets" are named program presets: one per space type (dwelling unit, corridor, stair, ...), carrying loads, schedules, setpoints, and the window parameter. Rooms are never modelled (D-009).
D-025 — Window conservation rule for plan simplifiers¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Superseded by D-039
- When a plan simplifier creates target zones, each target exterior wall receives the glazed area of the source windows on the façade portion it covers, as one centered window (D-022). Its WWR is derived from that area.
- This conserves glazed area per façade portion, per orientation, and per building. One wide window on a long merged wall is an accepted consequence.
- Recorded in the research brief §10 during Stage 0.
D-026 — The window parameter is WWR¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Accepted
- Confirms the assumption in D-022: the single window parameter on a program preset is the window-to-wall ratio.
D-027 — Setpoints are floor-area weighted only¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Superseded by D-038
- The base pipeline aggregates setpoint schedules by floor-area weighting only:
T*(t) = Σ Aᵢ·Tᵢ(t) / Σ Aᵢ. There is no method input, no exposed-surface-area definition, and no fallback. - Why (from a review of the roadmap): exposed-surface-area weighting makes the control assumption depend on geometry, which confounds the experiment. Split source zones carry floor area by overlap but not exposure, the zero-exposure fallback is discontinuous, and exposure varies across typologies with the same program. Simplification should change geometry, not the thermostat assumption.
- Exposed-area or other weightings may return later only as explicitly labelled sensitivity tests, never as part of the base pipeline.
- Setpoint aggregation is a prescribed control rule, not a conservation invariant.
D-028 — All spaces are conditioned for now¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li · Status: Superseded by D-038
- Every space type is treated as conditioned, so setpoint weights use total floor area. Program presets get no conditioned/unconditioned flag for now.
D-029 — Conditioning is intentionally homogenized¶
- Date: 2026-09-30 · Decided by: Cheng Xuan Li (framing from roadmap review; keeps D-028) · Status: Superseded by D-038
- D-028 (every space conditioned) is an experimental simplification, not a property of the DOE prototype that supplies the program values. Corridors, stairs, cores, and service spaces may be unconditioned or differently conditioned in the prototype.
- Conditioning status is held identical at every LoD, so it is outside the LoD variables under study. As a consequence, Z0 does not reproduce the prototype's conditioning, and results must not be presented as if it did.
docs/research/program-presets.mdand the research brief state this explicitly.- Alternative not taken: keeping prototype-specific conditioned/unconditioned status. Choosing it later would supersede D-028 and this entry, and would need a conditioned flag on program presets.
D-030 — Roadmap approved; first checkpoint is S0–S4¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- The implementation roadmap (v2 with review fixes) is approved.
- The first checkpoint is the completion of stages S0 to S4: the full pipeline for the linear plan, from program presets through plan simplifiers and floor aggregators to
Convert2BEM. - Detailed test-first plans are written for S0 to S4 before execution starts.
D-031 — SingleZoneMerged slabs become internal mass¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- In
SingleZoneMerged, slabs between storeys are no longer heat-transfer boundaries between two zones. They become internal mass of the single zone, keeping the slab construction and area, so their thermal storage and exchange area are retained. Written up in ADR-009.
D-032 — FloorAreaMultiplier uses floor types with individual multipliers¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
FloorAreaMultipliertakes a configurable list of floor types, each anIFloorwith its own multiplierNᵢ("one floor × Nᵢ for i floor types"). One typical floor × N, or bottom / middle × (N−2) / top, are special cases of this.- Boundary conditions of each floor type's floor and ceiling are fixed in ADR-009.
D-033 — FloorAreaMultiplier boundary conditions follow floor-type order¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Superseded by D-040
- Floor types are listed bottom to top. The first type's floor is ground, the last type's ceiling is roof (outdoors), and every other floor and ceiling is adiabatic.
- With a single floor type, that type gets both ground and roof, so ground and roof exposure are multiplied by N; a warning reports this.
- Ground and roof areas are compared with the fully stacked building and reported, not enforced: vertical exposure is part of the simplification under study.
D-034 — Partitions between merged zones are discarded¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- When zones merge (plan simplifiers Z1–Z3 and
SingleZoneMerged), walls with the same zone on both sides are discarded, as the research brief describes for rezoning. They do not become internal mass. - Only inter-storey slabs in
SingleZoneMergedbecome internal mass (D-031). Walls and slabs are therefore treated differently on purpose; ADR-009 records this asymmetry.
D-038 — Conditioning comes from program presets¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (raised in plan review) · Status: Accepted · Supersedes: D-027, D-028, D-029
- Why: with every space conditioned, every source has a thermostat and the setpoint rule is never exercised; Z0 would also lose the distinction between dwelling units and corridors, stairs, or other unconditioned spaces.
- Program presets carry conditioned or unconditioned status. Unconditioned zones have no setpoints.
- A simplified zone is conditioned if any source contributing floor area to it is conditioned ("any conditioned wins"). Its setpoints are floor-area weighted over the conditioned sources only:
T*(t) = Σ wᵢTᵢ(t) / Σ wᵢ,wᵢ= transferred floor area of conditioned source i. Setpoint aggregation remains a prescribed control rule, not a conservation invariant; exposed-area weighting stays rejected (reasons in D-027). - Merging conditioned and unconditioned sources enlarges the conditioned floor area. That change is reported by validation, not enforced: it is a consequence of the zoning simplification under study.
D-039 — Explicit windows in the model; window LoD becomes its own stage¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (raised in plan review) · Status: Accepted · Supersedes: D-022, D-025
- Why: a global "one centered window per wall at every LoD" rule removes the window-LoD axis (W0–W5 in the research brief).
- Walls carry explicit windows: each window has a position along the wall, a sill height, a width, and a height; a wall may have any number of windows.
- The first plan generator (S2) still uses one simple deterministic rule: one centered window per outdoor wall, sized from the preset's window-to-wall ratio (D-026 stays).
- Plan simplifiers and
SingleZoneMergedkeep the source windows unchanged and re-host them on the target walls that cover them (W0 at every zoning level). A window not fully covered by one target wall is an error, never silently moved. - Window transformations (W1–W5, including the former D-025 rule "one centered window per target wall from the conserved façade glazing") become stage S5 of the roadmap, after Checkpoint 1. Later stages are renumbered: Convert2IDF S6, precedent study S7, typologies S8, Grasshopper UX and release S9.
D-040 — Floor multiplier must not multiply ground or roof exposure¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (raised in plan review) · Status: Superseded by D-046 · Supersedes: D-033
- Why: multiplying ground or roof heat transfer by N creates a boundary-condition artifact that can dominate the error attributed to the vertical simplification.
FloorAreaMultiplieruses the bottom / typical / top convention: floor types are listed bottom to top; the first and the last type must stand for exactly one storey (multiplier 1), otherwise the aggregation fails. The first type's floor is ground, the last type's ceiling is roof, and every other floor and ceiling is adiabatic.- Ground and roof area conservation against the fully stacked reference are enforced validation checks for every floor aggregator. There is no typical-floor-only mode.
D-041 — Façade coverage is an enforced invariant¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (raised in plan review) · Status: Accepted
- Why: attribution fractions are normalised per source wall for exact conservation; without a coverage check, a target façade covering only part of a source wall would silently receive all of its glazing and wall-based loads.
- For every source outdoor wall i, the target outdoor walls on the same façade line must cover its full length:
Σⱼ Lᵢⱼ ≈ Lᵢwithin tolerance. Simplifiers andSingleZoneMergedfail when it does not hold, and floor validation enforces it as a separate check, evaluated before any normalisation.
D-045 — Representative storeys sit at their true elevations¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (raised in plan review) · Status: Accepted
- Why: placing one storey per floor type stacked the representative storeys directly on each other, so a 1 / 8 / 1 building was three storeys tall. Vertical position matters for shading, wind exposure, façade position, and any bottom / middle / top differentiation.
- In
FloorAreaMultiplier, a floor type standing for N storeys occupies its N storeys of the fully stacked building. Its representative storey is placed at the whole storey nearest the middle of those storeys: storeyfirst + ⌊N/2⌋(for 1 / 8 / 1 with 3 m storeys: storeys 0, 5, and 9 at 0, 15, and 27 m). Storeys in between are left empty. Zone IDs use the storey index (L5/…), so they match the zones of the fully stacked building. - Building height is an enforced validation check for every floor aggregator, against the fully stacked reference.
D-046 — Exposed storeys of a multiplied floor type are split off automatically¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (raised in plan review) · Status: Accepted · Supersedes: D-040
- Why: multiplying a storey multiplies every surface of it. Exposure (ground, roof, setback roofs, overhanging floors) must never be multiplied, and floor types with different footprints must be allowed, because the study includes buildings whose plan varies with height.
- Floors and ceilings of the representative storeys are resolved against the representative storey below and above: overlap is adiabatic, uncovered parts face outdoors, the lowest storey's floors face the ground, and the highest storey's ceilings are roofs.
- A storey of a floor type with N > 1 whose floor or ceiling is not fully covered by the neighbouring type (always the case for the bottom and the top type) is split off as its own ×1 storey at its true position, e.g. a single type ×10 becomes ×1 / ×8 / ×1. The split is reported as an info diagnostic (
FloorTypeSplit) and recorded in the building's provenance. Exposure is therefore never multiplied, and no user input is rejected for it. - Ground area, roof area, exposed floor area, and building height are enforced validation checks for every floor aggregator, against the fully stacked reference. There is no mode that multiplies exposure.
D-047 — Loads are aggregated per load type and basis¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (raised in plan review) · Status: Accepted · Superseded in part by D-126 (one load per load type, end use, and basis)
- Why: simplified zones merge different programs, and programs may express the same load type in different bases (e.g. ventilation per person in one, per floor area in another; ASHRAE 62.1 ventilation is per person plus per area within one program). Rejecting mixed bases would block legitimate combinations.
- A program may hold one load per load type and basis; components of the same type add up. Each (type, basis) component is aggregated on its own with the ADR-007 magnitude rule, so its design magnitude and its hourly scheduled magnitude are conserved exactly; sources without the component contribute nothing to it. Mixed bases are no longer an error (
MixedLoadBasisis removed). - Occupancy may also have several components; a zone's design occupants are their sum, and per-person components use that sum.
- Amends ADR-007 (written in S0) and the research brief §9 accordingly. Whether the eventual DOE or prototype presets fit these bases stays an open research input (roadmap §7).
D-048 — S0–S4 are executed subagent-driven with two model tiers¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Superseded by D-111
- The stage plans are executed with subagent-driven development: one fresh subagent per plan task, a review after each task, and a controller session that dispatches and verifies but does not write production code.
- Complex tasks (geometry, numerical invariants, toolchain setup, the aggregation engine, simplifiers, floor aggregators, validation) and their reviews use Opus 5.5 with medium reasoning effort. Bounded tasks fully determined by the plan (documents, ADR text, model types, thin Grasshopper components, close-out bookkeeping) use Sonnet 5.5. A bounded task escalates to Opus 5.5 after two failures or any deviation from the plan's expected output.
- Task-by-task assignment and rules: the execution handoff plan §6 (retired and deleted, D-111).
D-049 — The verified reference code travels as an untracked archive¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Superseded by D-111
- Why: the code verified while writing the S0–S4 plans, its build logs, and the plan-checking tools are useful to whoever executes the plans, possibly on another machine, but must not change the repository's history.
- They are packed into a zip archive with a README and a handoff message, moved between machines outside git, and unpacked into
<repo>/.handoff/checkpoint-1/, which.gitignoreexcludes. No branch, tag, or commit holds them. The conversation transcript is not part of the archive; durable context belongs in the decision log and the plans. - The plans stay the source of truth; the reference is used to check results, never copied to make a test pass. Details: the execution handoff plan (retired and deleted, D-111).
D-050 — Commit author identity¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- Why: a machine whose global git configuration uses another address would split the history between two author identities for the same person.
- Commits by Cheng Xuan Li use
Cheng Xuan Li <(e-mail address withheld)>, the identity of the existing history. A machine with a different global address sets it in the repository's local git configuration (git config --local user.email). Unpushed commits made with another address are re-authored before they are pushed. Refines D-002.
D-051 — Stage-plan checkboxes are ticked at stage close-out¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- Why: each plan task specifies exactly which files its commit touches; ticking checkboxes in those commits, or in extra commits between tasks, would change the expected
git statusand diff output. - During execution the controller tracks task progress in its session. The stage close-out task ticks the stage plan's completed checkboxes in its own commit, together with the plan's status change. Refines the execution model in D-048.
D-052 — Launch profiles for the Grasshopper plugin¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (raised during S0 execution) · Status: Accepted; its .NET Framework profile superseded by D-053
- Why: developing and smoke-testing the plugin needs Rhino started with Grasshopper and the fresh build loaded; the S0 plan only described loading it through Grasshopper's library folders.
src/Lod.Grasshopper/Properties/launchSettings.jsonis committed with two profiles, Rhino 8 (.NET Core) and Rhino 8 (.NET Framework). Each starts Rhino 8 from its default install location in that runtime (/netcoreor/netfx), runsGrasshopper, and setsRHINO_PACKAGE_DIRStobin\<Configuration>\net7.0\orbin\<Configuration>\net48\of the project. A machine with Rhino elsewhere changesexecutablePathlocally without committing it. The smoke-test guide describes the profiles as a third way to load the plugin (Option C).- Added to the S0 plan (Task 10 Step 6, guide text in Task 11, reviewer note 13) after the handoff; in this execution it lands as follow-up commits on the plugin-shell slice. The untracked reference archive is updated to match, following its
tools/README.md("Changing a plan after the handoff").
D-053 — The plugin targets Rhino's .NET Core runtime only; net48 is dropped¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted · Supersedes: the
net48targets in ADR-001 as first written in S0, and the .NET Framework launch profile in D-052 - Why: Rhino 8 runs plugins on its .NET Core runtime by default, and the S0 smoke test passed there. A second runtime doubles builds, test runs, and manual checks without a current need.
Lod.Grasshoppertargetsnet7.0only: oneBEMGen.gha, which Rhino 8 runs on .NET 8. Test projects targetnet8.0only. Core libraries stay onnetstandard2.0(anet7.0plugin cannot reference anet8.0library), with theIsExternalInitpolyfill. The launch profile keeps only Rhino 8 (.NET Core).- Consequence: a Rhino session switched to .NET Framework (
SetDotNetRuntime) cannot load BEMGen. If a plugin that BEMGen's output is wired into, such as ClimateStudio, turns out to need .NET Framework mode, this decision is revisited. That has not been checked yet. - ADR-001 is revised in place, since it is accepted only at the S0 close-out (D-042). The S0 to S4 plans, the roadmap, the execution handoff plan, and the untracked reference archive are revised together. Every stage of the reference was rebuilt with 0 warnings and the same test counts, now on
net8.0only.
D-042 — Foundation ADRs accepted¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- ADR-001 to ADR-005 and ADR-007 are accepted at the close of S0. Beyond earlier entries, they decide:
- ADR-001, revised before acceptance to drop the .NET Framework 4.8 target (D-053): core libraries on
netstandard2.0with C# 12 (LangVersion12.0, notlatest) and anIsExternalInitpolyfill; no System.Collections.Immutable, because Rhino ships its own copy, so read-only arrays instead; the pluginBEMGen.ghafornet7.0only, for Rhino 8's default .NET Core runtime, against RhinoCommon and Grasshopper 8.19.25132.1001, making Rhino 8.19 the minimum supported version; tests onnet8.0only; central package management with exact versions; component GUIDs fixed once merged; the local gatescripts/verify.ps1on Windows PowerShell 5.1. - ADR-002: 2.5D geometry in metres; zones as one or more prisms; vertical rectangular walls and horizontal floors and ceilings; Clipper2 at 6 decimals for intersection, union, and difference; vertices snapped to the distance grid; sloped roofs and non-vertical walls out of scope. For windows it records D-039 (explicit windows, W0 kept through simplification) and the first generator's centered placement rule (D-026) without a new window decision.
- ADR-003: 8760 hourly values for a non-leap year; fraction or temperature kind; weekday and weekend profiles with an explicit weekday for 1 January; holidays not modelled.
- ADR-004: one
ToleranceSettings; relative area tolerance 1e-6 instead of the 1e-9 proposed in roadmap v1 and v2; relative comparisons with a floor of 1. - ADR-007: one magnitude rule for every load basis, applied to each load component (load type and basis) separately, so mixed bases are aggregated per component instead of rejected (D-047); the exterior-wall-area basis counts walls only; positive loads with a zero target basis quantity are errors. Exterior-wall fractions are used only after façade coverage is proven (D-041).
- ADR-005 writes up D-038, with the reasons from D-027 for rejecting exposed-area weighting, and makes no new decision.
- The tech stack and tolerances proposed in roadmap v1 and v2 are superseded where these ADRs differ; roadmap v3 already reflects them.
D-054 — Checkpoint 1 is completed in an unsupervised run¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- After the S0 close-out, S1 to S4 are executed without human review: the controller drives the subagents of D-048, records findings and minor issues as it goes, and stops only for a blocking issue it cannot resolve. Window transformations (S5) are not part of the run.
- Pushes: the controller pushes
mainand each stage tag (fromv0.0.1) at the stage close-out, only afterscripts/verify.ps1and the fresh-clone check pass; never a force push. - Manual Rhino checks are performed by the controller through screen automation of Rhino 8 and recorded as such; when that is not possible, the check is recorded as not performed (unsupervised run) and left for a person.
- When a plan and the reference archive disagree, the controller investigates and fixes both together with a record, instead of stopping (refines the execution handoff plan §7).
- Decisions the controller makes during the run are logged as "Decided by: controller (unsupervised run)" with status Provisional, for later review.
- A run summary is committed to
docs/plans/at the end of S4, or when the run stops.
D-055 — The execution-handoff note stays in AGENTS.md until Checkpoint 1¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run) · Status: Superseded by D-111
- Why: the S1 to S4 close-outs replace the whole "Project state" section of
AGENTS.md. Their texts were written before the paragraph that points to the execution handoff plan and the git-ignored.handoff/reference was added, so the S1 close-out removed it while S2 to S4 still use the reference. - The paragraph is restored after the S1 text, and the S1 to S3 plans' replacement texts now end with it. The S4 close-out drops it as written, because Checkpoint 1 ends the handoff and retires the reference.
D-056 — Manual Rhino checks run as scripted headless Grasshopper sessions¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run) · Status: Provisional · Superseded in part by D-111
- Why: D-054 lets the controller perform the manual Rhino checks through screen automation, but no one was present to grant screen control. Rhino itself can still be started and observed.
- The controller starts Rhino 8 (
/nosplash /netcore) withRHINO_PACKAGE_DIRSpointing atsrc/Lod.Grasshopper/bin/Release/net7.0/, runs a Python script through-_RunPythonScriptthat loads Grasshopper headless, places the stage's components by GUID, wires and sets inputs, solves, and writes the components' states, runtime messages, outputs, data trees, and bounding boxes to a file, then exits Rhino. The checklist items are evaluated from that file. - Checklist items that need a viewport or the toolbar UI (preview colours, icons, panel layout as drawn) cannot be observed this way and are recorded as not performed; the registered category, panel, and exposure of every component are checked instead. Example
.ghdefinitions are not created (agents never write them) and remain for a person.
D-057 — Floor aggregator components ignore null floors¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run) · Status: Provisional
- Why: the S2 smoke test found that when Linear Plan Generator fails, the null item it leaves in its output reaches Stack Floors through No Simplification, and
FloorAggregatorComponent.SolveInstancethrew a NullReferenceException. FloorAggregatorComponentreturns without output when the floor list contains a null, as No Simplification does for a null plan. The S2 plan (Task 8 block, reviewer note 13) and the reference archive (S2 to S4) are changed together; the S4 aggregator components inherit the fix.
D-058 — Stages close with person-only checklist items pending¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run) · Status: Provisional · Superseded in part by D-115 (example definitions)
- Why: the S2 and S4 close-outs require example
.ghdefinitions saved by a person in Rhino 8 (agents never write them), and some checklist items need a viewport (D-056). In the unsupervised run (D-054) no person is available, and holding the stage tags would block the later stages for no technical reason. - In this run a stage is closed and tagged once every check that can be observed passes. The close-out commit records the checks performed (by whom and how) and lists the items not performed. Those items stay open for a person, who records the result in a later commit; any defect found then is fixed in a later change, and the stage tag is not moved.
D-059 — Rhino geometry uses the central distance tolerance¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run) · Status: Provisional
- Why: the S2 end-of-stage review found
private const double Tolerance = 1e-6insrc/Lod.Grasshopper/Convert/RhinoGeometry.cs, a numerical tolerance outsideToleranceSettings(GLOBAL.md quality rule 4). RhinoGeometrypassesToleranceSettings.Default.Distance(1e-6 m, unchanged) toBrep.CreatePlanarBrepsandBrep.CreateFromCornerPoints; geometry is still created in metres and scaled to model units afterwards. The S2 plan (Task 7 block, reviewer note 14) and the reference archive (S2 to S4) change together.
D-043 — Perimeter/core corners are split on the bisectors¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
PerimeterCore(Z2) gives each footprint edge the trapezoid between the edge and its inward mitred offset by the perimeter depth, so corners are split along the angle bisectors. Trapezoids in the same orientation bin merge into one perimeter zone (suffixes-1,-2when they do not form one polygon); the offset ring is the core. Perimeter and core zones have space typeMixed.- Footprints too narrow for a valid core fail with
PlanTooNarrowand are never repaired; footprints with holes are not supported until S8. The 4.57 m default depth of the Grasshopper component is an input default, not a research rule. - Written up in ADR-008.
D-060 — ADR-009 merged before the owner's acceptance¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run) · Status: Provisional
- Why: S4 Task 1 Step 5 gates Slice B on the decision owner reading ADR-009. In the unsupervised run (D-054) the owner is not available, and ADR-009 only writes up decisions the owner already made (D-031, D-032, D-034, D-045, D-046).
- ADR-009 is merged as written in the S4 plan after a consistency review against those decisions, and S4 continues. The owner's reading of ADR-009 remains open; requested changes are made in a later change, with code changes if the ADR's rules change.
D-044 — Checkpoint 1 reached¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run) · Status: Provisional
- Checkpoint 1 (D-030) is reached: stages S0–S4 are complete and tagged
v0.0.1,v0.1.0,v0.2.0,v0.3.0, andv0.4.0. - The pipeline runs for the linear plan from program presets through every plan simplifier (
NoSimplification,SemanticMerge,PerimeterCore,SingleZonePerFloor) and every floor aggregator (Stack,FloorAreaMultiplier,SingleZoneMerged) toConvert2BEM, with every window kept in place (W0, D-039). All 12 combinations pass building validation in the test suite (EverySimplifierAndAggregatorCombinationValidates) and were validated and converted in Rhino 8 in a scripted headless Grasshopper session (D-056); the example definitionexamples/grasshopper/checkpoint-1-pipeline.ghand the viewport checks are pending a person (D-058). - The pipeline is described in docs/architecture/pipeline.md; vertical aggregation in ADR-009, with representative storeys at their true elevations (D-045) and exposure never multiplied (D-046). Loads are aggregated per load type and basis at every zoning level (D-047).
- S5 (window transformations, gated on ADR-012), S6 (Convert2IDF), and S7 (precedent study) can start (D-015, D-039, roadmap §1).
D-061 — Plan generators take one explicit preset input per space type; default preset components per space type¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of Checkpoint 1) · Status: Accepted
- Why: a single Presets list hides which presets a generator needs and which space type each wire feeds; an explicit input per space type makes the definition self-describing and lets each preset be swapped on its own.
- Generator inputs. A plan generator component has one preset input per space type it places instead of the single Presets list; for the linear plan: Dwelling Unit, Corridor, and Stair. The inputs are required: an unconnected input is a missing-input error, never a silent fallback, so a definition always shows which presets it uses. A preset whose space type does not match its input is an error. In
Lod.Generators,LinearPlanGeneratortakes a typed record with one preset per space type instead of aProgramPresetSet(typed parameter records per typology, AGENTS.md). The Linear Plan Generator keeps its GUID; its inputs change. - Default preset components. One per space type, visible on the toolbar: Dwelling Unit Preset, Corridor Preset, Stair Preset. Every input has a default, so each works with nothing connected, as an output-only component. Inputs: name, window-to-wall ratio, Conditioned, heating and cooling setpoints (°C, constant), the design value of each of the space type's loads, and an optional schedule per load that replaces the built-in schedule when connected. Defaults are today's illustrative example values (not DOE prototype values), and each component shows a remark saying so; sourced values stay a research input.
- Dwelling units are not subdivided. The dwelling unit is the existing
DwellingUnitspace type, one zone per unit; there is no bedroom space type and no splitting of units into rooms. - General Program Preset. Stays, for custom presets of any space type. Its Name (already an input) is descriptive only: it appears in reports, provenance, and messages and is never used for matching or behaviour. The default preset components get the same Name input, defaulting to the component's preset name.
- Example Residential Presets (the component that outputs a list of presets) is deleted.
- Lands in the revision stage S4.1 (D-064).
D-062 — Preview location input on every component with a viewport preview¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of Checkpoint 1) · Status: Accepted
- Why: several plans, floors, and buildings previewed in one model overlap at the origin; moving each preview lets them be compared side by side.
- The Linear Plan Generator, the four plan simplifier components, and the three floor aggregator components get an optional last input Preview Location: a vector in model units, default
0,0,0(a point wired in converts to a vector). It moves only that component's viewport preview. Plan, floor, and building data, the Inspect and Validate reports, and the Convert2BEM outputs stay at their true coordinates, and downstream components do not inherit the move. - Convert2BEM gets no such input; its outputs can be moved with Grasshopper's Move if needed.
- Lands in the revision stage S4.1 (D-064).
D-063 — A bitmap icon for every component¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of Checkpoint 1) · Status: Accepted
- Every component has a bitmap icon that depicts its function, replacing the current
Icon => null. - Icons are 24 × 24 PNG pictograms, embedded as resources and drawn by a script kept in the repository, so they can be regenerated and restyled consistently: a dark glyph with one accent colour per toolbar panel (Program, Generate, Simplify, Aggregate, Inspect, Convert, Info).
- Lands in the revision stage S4.1 (D-064); S9 only polishes them.
D-064 — Revision stage S4.1 before S5; no backward compatibility for now¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of Checkpoint 1) · Status: Accepted
- D-061, D-062, and D-063 are implemented in a revision stage S4.1, with its own plan in
docs/plans/and its own tag, executed like S0–S4 (D-048), before S5 starts and before the pending example definitions (D-058) are built. - Until the owner says otherwise, changes do not preserve backward compatibility: components may be deleted and component inputs and outputs changed without migration or obsolete stand-ins, and saved Grasshopper definitions may break. Component GUIDs still never change (GLOBAL.md); a component that changes keeps its GUID, and a deleted component's GUID is not reused.
D-065 — An unconnected preset input is Grasshopper's missing-input warning¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of the S4.1 plan) · Status: Accepted · Refines: D-061
- Why: every required input of every BEMGen component already behaves this way; an extra error would make the preset inputs the only exception.
- The preset inputs of a plan generator component are required inputs. When one is unconnected, Grasshopper reports its own warning (
Input parameter <nickname> failed to collect data) and the component outputs nothing. There is no fallback to default presets. D-061's "missing-input error" means this behaviour. docs/architecture/domain-model.mdsays that a removed diagnostic code's name is not reused, instead of "later stages only append codes", since S4.1 deletesDuplicateSpaceType(D-064).
D-066 — S4.1 is executed in an unsupervised run¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- The S4.1 plan is approved and executed without human supervision, following the rules of D-054 where they apply: subagent-driven execution (D-048), a review after each task, findings recorded rather than asked about, a stop only on a blocking issue that attempts cannot solve, manual Rhino checks as scripted headless Grasshopper sessions (D-056), and person-only items left open (D-058). New decisions made during the run are logged as "Decided by: controller (unsupervised run)", status Provisional.
- Nothing is pushed in this run: slices are merged and the stage is tagged
v0.4.1locally; pushingmainand the tag waits for the owner.
D-067 — S4.1 complete¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run, D-066) · Status: Provisional
- The revision stage S4.1 (D-064) is complete and tagged
v0.4.1. D-061: plan generators take a typed preset record with one preset per space type (PlanPresets,PresetSlot; for the linear planLinearPlanPresets), and a preset of the wrong space type is the errorPresetSpaceTypeMismatch;ProgramPresetSetand the codeDuplicateSpaceTypeare deleted. The Linear Plan Generator has the inputs Dwelling Unit, Corridor, and Stair; Dwelling Unit Preset, Corridor Preset, and Stair Preset build presets fromPresetValueswith the illustrative example values as defaults; Example Residential Presets is deleted and its GUID0239f4c9-0f19-4cf1-9d90-08db878eac8aretired. D-062: the generator, the four plan simplifiers, and the three floor aggregators have a Preview Location input that moves only their preview. D-063: every component and parameter, and the plugin, has a 24 × 24 icon drawn byscripts/make_icons.py. - Plans, floors, and buildings are unchanged: all seven snapshots are byte-identical. Setpoint schedules of presets built from
PresetValuesare named"<preset name> Heating Setpoint"and"<preset name> Cooling Setpoint". 120 core, 19 generator, and 63 integration tests pass onnet8.0. - The S4.1 smoke test is recorded in the close-out commit; items it did not perform stay open for a person (D-058).
- S5 (window transformations, gated on ADR-012) can start; S6 and S7 depend on S4 only.
D-068 — Ground, roof, and exposure are designated on the assembled building¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of S4.1) · Status: Accepted · Refines: ADR-009 (D-046)
- Why: whether a floor touches the ground, a ceiling is a roof, or a piece is exposed at a setback is a property of the whole building, not of one floor or of the representative storeys alone.
- Plans and floors leave every floor and ceiling unresolved (as since S2). Every floor aggregator first assembles the full stack of storeys and designates every floor and ceiling piece there:
Ground, roof (Outdoors), exposed (Outdoors), or between storeys. A method that models fewer storeys (the zone-multiplier method, D-071) takes the designations of the storeys it models from that assembled stack instead of resolving them against its neighbouring representative storeys. - ADR-009 is superseded where it differs, by a new ADR written in the revision stage S4.2 (D-073).
D-069 — Heat transfer between zones is an export option, off by default¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of S4.1) · Status: Accepted
- Convert2BEM and Convert2IDF (S6) get two Boolean inputs, both default
False: EnableInternalWallHeatTransfer for walls between zones, and EnableFloorHeatTransfer for floors and ceilings between storeys. When an option isFalse, every surface of that kind that lies between two zones is written as adiabatic; whenTrue, it stays paired with the adjacent zone (Interzone:<zone>). - The building model itself keeps the interzone surfaces; the options only change what a converter writes, and the chosen values are recorded in the conversion provenance.
- Convert2BEM gets the options in S4.2 (D-073); Convert2IDF has them from its first version (S6).
D-070 — Transform Plan moves and rotates a plan's geometry¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of S4.1) · Status: Accepted
- Why: unlike Preview Location (D-062), a real move or rotation of a plan changes how floors stack (overhangs, setbacks, rotated storeys) and which way façades face.
- A new component Transform Plan takes a plan and a Grasshopper transform and outputs a new plan. Only two parts of the transform are applied: the translation in the world x-y plane and the rotation about the world z axis. Anything else the transform contains (vertical translation, rotation about another axis, scaling, shear) is not applied, and the component reports that with a warning; geometry is never changed silently (GLOBAL.md quality rule 6). No vertical translation is applied: storey elevations come from the floor aggregator.
- The core service that does it lives in
Lod.Core, records the applied rotation and translation in the plan's provenance, and keeps area, volume, wall area, and window area unchanged. Window and wall orientations follow the rotated geometry; the plan's Orientation (true north) is unchanged. - Only plans are transformed. Floors keep the geometry of the plan they were made from, and buildings keep the geometry of their floors.
D-071 — Floor aggregators: zone multiplier, single zone per floor type, and single zone building¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of S4.1) · Status: Accepted
- Stack Floors stays as it is: every storey modelled, multiplier 1.
- Stacked Floor Zone Multiplier (
StackedFloorZoneMultiplier) is Floor Area Multiplier (FloorAreaMultiplier) renamed. It keeps its GUIDe4b44821-c9ed-49b9-9c4e-7c08355254dd(D-064) and models what it modelled before: representative storeys withZone.Multiplier(D-045), exposed storeys split off as × 1 (D-046). The converters write that multiplier as the engine's zone multiplier. Its preview shows the whole stacked building; storeys that are represented but not modelled are drawn differently from the modelled ones (proposed: transparent grey). - Single Zone per Floor Type (
SingleZonePerFloorType) is what Single Zone Merged becomes, keeping its GUID506d5037-de93-4aee-a6c4-47dcc97509e1(D-064): each floor entry (floor type and number of storeys) becomes one zone spanning its storeys, e.g. A × 1 → one zone of A's storey, B × 3 → one zone spanning B's three storeys. Within an entry, the slabs between its storeys become internal mass (D-031) and partitions are discarded (D-034); floors and ceilings between entries stay between two zones (D-068, D-069). The program of each zone is aggregated from its sources with the S1EquivalentPropertyAggregator, so every invariant is conserved, as in Single Zone Merged today. Each zone is previewed as one merged volume, not as separate storeys. - Single Zone Building (
SingleZoneBuilding) is a new method with a new GUID: the whole building as one zone, which is what Single Zone Merged does today (D-021). It is previewed as one merged volume. - The single-zone proxy discussed in the same review (one zone per floor type with area-based quantities multiplied by the number of storeys) is not adopted.
D-072 — Floor multipliers stay whole numbers¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of S4.1) · Status: Accepted
- Every floor aggregator takes whole-number multipliers from 1, as since S2. Fractional multipliers were considered in the same review, for the zone-multiplier and single-zone methods, and rejected: the EnergyPlus
Zonemultiplier field takes whole numbers only.
D-073 — Revision stage S4.2 before S5¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li (review of S4.1) · Status: Accepted
- D-068 to D-071 are implemented in a revision stage S4.2 with its own plan and tag (
v0.4.2), before S5. The parts of D-069 that concern Convert2IDF are requirements for S6. A new ADR on vertical aggregation, superseding ADR-009 where it differs, is written and accepted before any S4.2 code.
D-074 — An unsupported storey is a warning¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- Why: with Transform Plan (D-070) a storey can be moved or rotated so that the storey below no longer carries all of it.
- When part of a storey's floor is not covered by the storey below (more than the area tolerance), every floor aggregator still builds the building, treats the uncovered part as exposed (D-068), and reports a warning naming the storey and the uncovered floor area. A storey on the ground is never unsupported.
D-075 — S4.2 is planned and executed in an unsupervised run¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- The controller builds and verifies the S4.2 reference, writes ADR-013 and the S4.2 plan, and executes the plan without human supervision, under the rules of D-066. ADR-013 and the plan are accepted by the controller (Provisional, pending the owner's reading) so that execution can start; decisions made during the run are logged as "Decided by: controller (unsupervised run)", status Provisional.
mainandv0.4.1are pushed before the run. Nothing is pushed during the run: S4.2 is merged and taggedv0.4.2locally, and pushing waits for the owner.- Defaults the owner did not object to: in Stacked Floor Zone Multiplier a surface facing a represented but unmodelled storey stays adiabatic whatever EnableFloorHeatTransfer says; unmodelled storeys are previewed transparent grey; Single Zone per Floor Type names its zones
E{i}by floor-entry index and Single Zone Building keepsBUILDING; nicknames Stack, ZMult, 1ZType, 1ZBldg; Transform Plan records the applied rotation about the world origin and the translation; the new ADR is ADR-013.
D-076 — Rotated plans: façade lines matched over their overlap, storey outlines unioned as tiles¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run, D-075) · Status: Provisional
- Why: building the S4.2 reference showed that plans rotated by a non-right angle (D-070) broke the two single-zone aggregators, although every quantity was conserved. Three rules that only held for axis-aligned geometry are generalised; no tolerance changes and axis-aligned results stay identical. Details in ADR-013.
- Façade coverage (
FacadeAttribution, D-041): two walls lie on the same façade line when their separation, measured at both ends of their overlap along the line, is within the distance tolerance. Before, the end points of one wall had to lie within the tolerance of the other wall's infinite line, which failed for a short wall with rounded end points next to a long rebuilt wall (2.4 µm off the extrapolated line, 0.23 µm apart). Walls that do not overlap are not on the same façade line. This changes a validation predicate (FacadeCoverage); it makes it exact for rotated walls and does not relax it. - Storey outlines (single-zone methods): the zones of a storey are unioned as tiles (shared grid vertices, T-junction vertices inserted) before clipping, so rotated zones give one outline without slits; a plain union added spurious wall area.
- Unsupported storeys (D-074): a storey is unsupported when its covered floor area differs from its floor area by more than the area tolerance (
AreaEquals), so rounding slivers of rotated setbacks are not reported. The D-046 split still compares the uncovered area with zero, so not even a sliver is multiplied.
D-077 — S6 depends on S4.2¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run, D-075) · Status: Provisional
- Why: Convert2IDF must offer the heat-transfer options of D-069 from its first version (D-073), and the shared rule (
HeatTransferOptions,ExportedBoundaryinLod.Core.Conversion) is created in S4.2. - S6 (Convert2IDF) depends on S4.2 instead of S4. S7 (precedent study) still depends on S4 only; S5 depends on S4.2 (roadmap, S4.2 plan commit).
D-078 — S4.2 complete¶
- Date: 2026-10-01 · Decided by: controller (unsupervised run, D-075) · Status: Provisional
- The revision stage S4.2 (D-073) is complete and tagged
v0.4.2locally; pushingmainand the tag waits for the owner (D-075). ADR-013, accepted by the controller under D-075 and pending the owner's reading, supersedes ADR-009 where it differs. D-068: every floor aggregator starts fromStoreys.Assemble(AssembledStack), andStoreys.ResolveRepresentativeis deleted. D-071:FloorAreaMultiplieris renamedStackedFloorZoneMultiplier(Stacked Floor Zone Multiplier, ZMult, GUID kept);SingleZoneMergedis renamedSingleZoneBuilding(Single Zone Building, 1ZBldg, new GUID50965154-e31e-4730-b61c-15a6f40bee6f); the newSingleZonePerFloorType(zonesE{i}) is behind Single Zone per Floor Type (1ZType), which keeps the GUID of Single Zone Merged; both single-zone methods shareSingleZoneMerge. D-074: the warningUnsupportedStorey. D-070:PlanMotion,PlanTransform, and Transform Plan (TPlan,57c17b9c-04b4-4595-9150-bb923877d95e). D-069:HeatTransferOptionsinLod.Core.Conversion; Convert2BEM has the inputs EnableInternalWallHeatTransfer and EnableFloorHeatTransfer, bothFalseby default, and records them in Provenance. D-075 defaults: pieces between storeys of the zone multiplier stay adiabatic whatever EnableFloorHeatTransfer says; unmodelled storeys are previewed transparent grey. D-076: façade lines matched over their overlap, storey outlines unioned as tiles, unsupported storeys compared with their floor area. - Every existing building is numerically unchanged: five snapshots are byte-identical,
aggregator-StackedFloorZoneMultiplier.txtandaggregator-SingleZoneBuilding.txtare the S4 snapshots renamed with identical content, andaggregator-SingleZonePerFloorType.txtis new. 136 core, 19 generator, and 163 integration tests pass onnet8.0, including every simplifier with every aggregator on plans rotated and moved by four placements. - The S4.2 smoke test is recorded in the close-out commit; items it did not perform stay open for a person (D-058).
- S5 (window transformations, gated on ADR-012) can start. S6 (Convert2IDF, which depends on S4.2 since D-077) implements the Convert2IDF part of D-069 with
HeatTransferOptions; S7 depends on S4 only.
D-079 — Windows of merged zones: one centred window per wall with the wall's glazed area; W1–W5 dropped¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted · Supersedes: D-039 in part (windows kept in place on rebuilt walls; window LoD as its own stage)
- Why: models are built from the bottom up. The plan generator creates windows at the highest level of detail, and every later step only simplifies or aggregates; window level of detail is not a separate experimental axis.
- Wherever zones are merged and walls are rebuilt (Semantic Merge, Perimeter Core, Single Zone per Floor, Single Zone per Floor Type, Single Zone Building), every outdoor wall of a target zone carries exactly one window, centred on the wall, whose area is the total glazed area of the source windows that wall covers. Its shape follows the generator's rule (D-026): the wall's rectangle scaled by the square root of glazed area over wall area. This is the only behaviour; there is no option to keep the windows in place.
- Steps that do not rebuild walls (No Simplification, Stack Floors, Stacked Floor Zone Multiplier) keep the generator's windows, which are already one centred window per wall.
- Glazed area is conserved per target wall, and therefore per façade, orientation, and building; validation keeps checking glazing in total and per orientation. Individual window positions are no longer conserved.
- Because every target wall covers its source walls (D-041), every source window lies on exactly one target wall and a target wall's glazed area is always smaller than its area. These hold by construction; the code still checks them and fails loudly (never caps or moves silently) if they were ever violated.
- The window levels W1–W5 are dropped from the roadmap and from the research brief (§10, updated with ADR-012). Stage S5 becomes this change, gated on a short ADR-012 that records the rule.
D-080 — S5 is planned and executed in an unsupervised run; push after completion¶
- Date: 2026-10-01 · Decided by: Cheng Xuan Li · Status: Accepted
- S5 (D-079) is run like S4.2 (D-075): the controller builds and verifies the reference, writes ADR-012 and the S5 plan, accepts both (Provisional, pending the owner's reading), and executes the plan without human supervision. Decisions made during the run are logged as "Decided by: controller (unsupervised run)", status Provisional.
- Nothing is pushed during the run. When S5 is complete, its gate passes, and a fresh clone of the tag passes
scripts/verify.ps1, the controller pushesmainand the tagsv0.4.2andv0.5.0; never a force push.
D-081 — S5 complete¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-080) · Status: Provisional
- Stage S5 (D-079) is complete and tagged
v0.5.0. ADR-012, accepted by the controller under D-080 and pending the owner's reading, supersedes in part ADR-002 ("Windows through simplification (W0)"), ADR-009, and ADR-013 where they keep windows in place on rebuilt walls; the research brief §10 drops W1–W5 and describes the rule.CentredWindows.PlaceinLod.Core.Simplificationattributes the source windows withWindowRehosting.Rehostand gives every target outdoor wall one window shaped byWindow.Centered(D-026) with the glazed area attributed to it, none when that area is zero;PlanSimplifier(Semantic Merge, Perimeter Core, Single Zone per Floor) andSingleZoneMerge(Single Zone per Floor Type, Single Zone Building) use it.WindowNotHostedstays as a guard, and the new errorGlazingExceedsWallguards a wall whose glazed area is not smaller than its area; neither caps nor moves a window. No component, input, output, icon, or GUID changed; the descriptions of the five components that rebuild walls and of the Windows output of Convert2BEM state the rule. - Glazing per wall, façade, orientation, and building and every validation report are unchanged. Six snapshots changed in their
window(…)entries only:simplifier-SemanticMerge,simplifier-PerimeterCore,simplifier-SingleZonePerFloor,aggregator-SingleZonePerFloorType,aggregator-SingleZoneBuilding, andaggregator-StackedFloorZoneMultiplier, which stacks a Perimeter Core floor although the zone multiplier itself rebuilds no wall;linear-plan-canonicalandstack-two-storeysare byte-identical. 142 core, 19 generator, and 187 integration tests pass onnet8.0, including every merging simplifier and both single-zone aggregators on the canonical plan and on plans rotated and moved by two placements. - D-079's guards hold by construction for the four current zoning strategies. Façade coverage (D-041) alone does not guarantee that every source window lies on one target wall: a future strategy that cuts a source wall inside a window fails with
WindowNotHosted(roadmap risk table; ADR-011, S8). - The S5 smoke test is recorded in the close-out commit; items it did not perform stay open for a person (D-058). After
scripts/verify.ps1passes on a fresh clone ofv0.5.0,mainand the tagsv0.4.2andv0.5.0are pushed (D-080). - S6 (Convert2IDF) and S7 (precedent study) can start.
D-082 — Precedent source: High-Density Housing (DETAIL), studied in a separate repository¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Superseded by D-093 · Supersedes: D-011
- The precedent study uses High-Density Housing (DETAIL) instead of Floor Plan Manual Housing. The study was carried out in a separate research repository,
typology-synthesizer(local pathD:\typology-synthesizer, commit366ffbb), with knowledge of the BEMGen research brief. It indexes the book's floor plans, catalogues their organisation with cited claims, and specifies nine typology families (HDHM-TYP-001toHDHM-TYP-009) as topological generator rules with an abstract validation set. - BEMGen consumes its derived records as the contract for new plan generators:
data/derived/generators.jsonl(SHA-25629700ded…),typologies.jsonl(5bb31c5c…), ands6_validation.jsonl(1ebd21d0…). The rules are topological only; the book supports no metric or count ranges, so dimensions, counts, and façade choices are BEMGen design inputs. - BEMGen has temporary read-only access to that repository. Nothing in it is modified, and no book content, page renders, or copied drawings enter BEMGen; BEMGen commits only paraphrased family rules with their stable IDs.
- This replaces the research work of roadmap S7 (slices 1–2); ADR-006 (plan generation mechanism) is still written in BEMGen before S8.
D-083 — S8 builds a plan generator for every High-Density Housing family¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Accepted
- S8 implements a plan generator for each of the nine families
HDHM-TYP-001toHDHM-TYP-009(D-082). The existingLinearPlanGeneratoris checked againstHDHM-TYP-009(double-sided internal corridor bar) and kept as that family's generator. - Dimensions, counts, and other choices the source does not fix are design defaults chosen by the controller, recorded with their reasons in ADR-006; the owner does not need to confirm them.
- The continuous external gallery of
HDHM-TYP-002is left out of the energy model: no gallery zone, no gallery geometry; the bar's façade on the gallery side is modelled as an ordinary outdoor façade, and the omission is stated in the generator's documentation. - Generators are not tested against the families' abstract validation records; each generator's documentation describes in text how it realises its family's rules, citing the family and rule IDs.
- A short page
docs/research/precedents/hdhm-typologies.mdparaphrases the nine families with their stable IDs and points to the pinned commit of the source repository (D-082). No book content is committed.
D-084 — S8 is split into three sub-stages and run like S5¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run) · Status: Provisional
- Why: the families differ in what the pipeline already supports. Bars need no pipeline change; non-rectangular outlines produce short façade fragments; the enclosed courtyard needs footprints with holes in every simplifier and aggregator.
- S8.1 (tag
v0.8.0): ADR-006 (generation mechanism, parameter records and defaults for all nine families) and ADR-011 (minimum wall and window geometry), the precedent page, and the bar families 009 (check), 001, 008, 002, and 006. S8.2 (v0.8.1): the non-rectangular families without holes, 007, 003, and 004. S8.3 (v0.8.2): the enclosed courtyard 005, with footprints with holes supported by every plan simplifier and floor aggregator. - Each sub-stage follows the S5 method (D-080): verified reference, headless Rhino check, plan, per-task execution, local merge and tag; the controller accepts its ADRs and plans provisionally, pending the owner's reading. Nothing is pushed during the run; pushing waits for the owner.
D-085 — A dwelling that spans storeys is one zone per storey¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-084) · Status: Provisional · Refines: D-009
- Why: plans are generated one storey at a time and stacked by the floor aggregators, so a terraced house of
HDHM-TYP-006that spans several storeys cannot be one zone of a plan. - Such a dwelling is modelled as one zone per storey of its plot (
U1…Unon every storey); its own internal stair is not a zone. D-009 ("one zone per dwelling unit, never subdivided") applies per storey: a dwelling is never split within a storey. The single-zone floor aggregators can merge the storeys of a floor type into one zone when a whole-house zone is wanted. - ADR-006 records the choice; the owner may revisit it.
D-086 — S8.1 complete¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-084) · Status: Provisional
- Sub-stage S8.1 (D-084) is complete and tagged
v0.8.0locally; pushingmainand the tag waits for the owner (D-084). ADR-006 and ADR-011, accepted by the controller under D-084 and pending the owner's reading, fix the plan generation mechanism (onePlanGeneratorsubclass per High-Density Housing family with typed parameter and preset records; design defaults, zone IDs, and realisation of the rules for all nine families, D-083) and the minimum wall and window geometry of every generator. The precedent pagedocs/research/precedents/hdhm-typologies.mdparaphrases the nine families with their stable IDs and the pinned source of D-082. ADR-002's placement rule is refined by ADR-011, and the research brief §10 states the exception. GeometryLimitsinLod.Core.Common(MinimumWindowWallLength1 m,MinimumWindowWidth0.3 m,MinimumWindowHeight0.3 m) is passed through thePlanGeneratorconstructor, the defaults when omitted. An outdoor wall or window below a limit by more than the distance tolerance gets no window and the new infoWindowOmitted; only non-default limits are recorded in provenance.LayoutMeasures.PlaceWindowsByRatiotakes the limits and tolerances and returns aResult. Rebuilt walls are not subject to the limits (ADR-012).LinearPlanGeneratorrealisesHDHM-TYP-009and is unchanged apart from its documentation. New inLod.Generators:StairBayBarGenerator(001, at least 2 bays),StairPairGenerator(008, one bay of the sharedStairBayLayout),GalleryBarGenerator(002, at least 2 dwellings, the gallery not modelled, D-083), andTerraceRowGenerator(006, at least 2 plots, one dwelling zone per plot and storey, D-085), each with a staticDefaultparameter record. New components in 2 Generate, with icons: Stair Bay Bar (SBar,911c3204-42bb-4023-bdb7-2931ea1caad3), Stair Pair (SPair,5669d39b-45e6-47ea-9250-f2304d005335), Gallery Bar (GBar,a58df462-b3c2-46b7-be3d-7ea9f4629280), and Terrace Row (Terr,f694e595-dba2-4b98-9d1e-f1492e3edf39). Linear Plan Generator's description namesHDHM-TYP-009; its inputs and GUID are unchanged.- The eight existing snapshots are byte-identical; four canonical snapshots are new. 152 core, 91 generator, and 319 integration tests pass on
net8.0, including every bar family with every simplifier and aggregator on its canonical plan and on the plan rotated by 131.4° and moved by (15.08, −153.9) m. - The S8.1 smoke test is recorded in the close-out commit; items it did not perform stay open for a person (D-058).
- S8.2 (families 007, 003, and 004) can start; S6 (Convert2IDF) is unaffected.
D-087 — Perimeter Core requires parts wider than twice the perimeter depth¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-084) · Status: Provisional
- Why: the non-rectangular families of S8.2 (winged band, open court) have wings; the corner rule of ADR-008 works at re-entrant corners, but a wing narrower than twice the perimeter depth leaves no valid core.
- Perimeter Core requires every wing, plate, and other part of a footprint to be wider than twice the perimeter depth. A narrower plan fails with
PlanTooNarrow, whose message states this precondition and suggests a smaller depth or another simplifier. Supporting narrower wings needs another partition (for example a straight skeleton) and a new ADR. ADR-008 records the precondition.
D-088 — Pipeline fixes for rotated plans found in S8.2¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-084) · Status: Provisional
- Why: running the S8.2 families through every simplifier and aggregator at many rotated placements found failures of Semantic Merge that already affected the linear plan and the S8.1 bars at some rotated placements (released up to
v0.8.0). - Semantic Merge unites each group as tiles (like the single-zone methods, D-076), so a merged group may have holes (a dwelling ring around a core). The surface builder tests whether a vertex lies on an edge at the vertices' original positions against the distance tolerance, instead of comparing snapped grid cells; the tolerance value is unchanged, it is measured on unsnapped coordinates. Unions of tiles keep the tiles' own vertices instead of grid-rounded ones. Details in ADR-002 and ADR-013. Axis-aligned results and every existing snapshot are unchanged.
D-089 — S8.2 complete¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-084) · Status: Provisional
- Sub-stage S8.2 (D-084) is complete and tagged
v0.8.1locally; pushingmainand the tagsv0.8.0andv0.8.1waits for the owner (D-084). The three families without holes that ADR-006 specifies for S8.2 are implemented as specified, with no parameter record, default, or rule of ADR-006 changed:PointPlateGenerator(HDHM-TYP-007: a square plate with the coreST, aStairzone without outdoor walls, and the dwellingsU1…U4in a pinwheel),WingedBandGenerator(HDHM-TYP-003: a comb of a band and at least two wings of stair bays, zonesB-…andW{i}-…), andOpenCourtGenerator(HDHM-TYP-004: a U of north, west, and east wings of stair bays around a court open to plan south, zonesN-…,W-…,E-…); the wings shareStairBayLayout(Wing,Fit,FitErrors), and the S8.1 bars are unchanged. New components in 2 Generate, with icons: Point Plate (PPlate,8ac9044a-8fbe-4964-a9de-2ef8be35094a), Winged Band (WBand,e285811d-2fdb-4d31-8b02-62c270b609cf), and Open Court (OCourt,223014ea-3c01-4d2b-b8ba-92ebdc4a7327). ADR-006 records the details settled in S8.2 and the pipeline consequences. - D-088 is implemented test-first: Semantic Merge unites each group as tiles, so a merged zone may have holes; the surface builder and the union of tiles test whether a vertex lies on an edge at the vertices' original positions against the unchanged distance tolerance; and unions of tiles keep the tiles' own vertices (ADR-002, ADR-013). Each fix repairs rotated plans that already failed on
v0.8.0(the linear plan and the S8.1 bars through Semantic Merge). D-087: Perimeter Core'sPlanTooNarrowmessage states that every part of the footprint must be wider than twice the perimeter depth (ADR-008); its checks are unchanged. No tolerance or validation check changed. - The twelve existing snapshots are byte-identical; three canonical snapshots are new. 154 core, 159 generator, and 474 integration tests pass on
net8.0, including every new family with every simplifier and aggregator on its canonical plan and on the plan rotated by 30° and moved by (5.3, −2.1) m and rotated by 131.4° and moved by (15.08, −153.9) m. - The S8.2 smoke test is recorded in the close-out commit; items it did not perform stay open for a person (D-058).
- S8.3 (the enclosed courtyard 005, footprints with holes in every simplifier and aggregator) can start; S6 (Convert2IDF) is unaffected.
D-090 — Very small zones on rotated plans are a known validation limit¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-084) · Status: Provisional
- Why: the S8.2 sweeps found that a zone only about 1 m wide (for example a Point Plate core of 1 m, or the 0.5 m dwellings of an Open Court with a 4 m court depth) can fail
SourceCoverageon a rotated plan after a merging simplifier: clipping on the integer grid (ADR-002) loses more than the relative area tolerance of ADR-004 on such thin zones. All defaults and every tested placement of the default plans pass. - The limit is accepted and documented (roadmap risk table, the S8.2 plan's reviewer notes); the tolerance is not widened (AGENTS.md rule 12). A remedy (exact rational clipping for thin zones, or a size-aware tolerance) needs an ADR-004 revision decided by the owner.
D-091 — Rhino geometry has one orientation rule¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-084) · Status: Provisional
- Why: the S8.3 headless check found that every zone solid from
RhinoGeometrywas inward-oriented (negative volume) and that floors faced up like ceilings, on every plan in released versions up tov0.8.1; simulation tools that read the Convert2BEM Breps may depend on orientation. - Zone solids are outward-oriented (positive volume); walls and windows face the outward side of their wall (court walls into the court), floors face down, and ceilings and roofs face up, interzone surfaces included. Planar faces are built unreversed, so
BrepFace.NormalAtgives the facing directly. Only orientation changes, never geometry. Implemented in S8.3 (RhinoGeometry, documented indocs/architecture/convert2bem.md).
D-092 — S8.3 complete¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-084) · Status: Provisional
- Sub-stage S8.3 (D-084) is complete and tagged
v0.8.2locally, and with it S8: a plan generator exists for each of the nine High-Density Housing families (D-083). Pushingmainand the tagsv0.8.0,v0.8.1, andv0.8.2waits for the owner (D-084).EnclosedCourtGenerator(HDHM-TYP-005) is implemented as ADR-006 specifies it, with no record, default, or rule of ADR-006 changed: four wings of stair bays on the sharedStairBayLayoutaround one court closed on all four sides (zonesS-…andN-…west to east, thenW-…andE-…south to north; by default 1728 m² and 44 windows, 16 of them on the court façades); the footprint is the outer rectangle with the court as its one hole. New component in 2 Generate, with icon: Enclosed Court (ECourt,b9765cd0-a62b-483d-9470-86d5fda287e8). ADR-006 records the details settled in S8.3 and the pipeline consequences. - Every step supports footprints with holes. The court is outside the building: its edges are outdoor walls facing into it, and it has no zone, floor, ceiling, ground slab, or roof. The surface builder requires every footprint edge, court edges included, to be covered by outdoor zone edges over its full length (
UnmatchedEdge), which edge matching alone missed for a zone filling a court (ADR-002). Perimeter Core offsets every ring by the bisector rule, gives the court façades their own zonesP-Court-<Bin>after the outer zones, and leaves a core that is a ring around the court; a wing between the outer and a court façade that is not wider than twice the depth fails withPlanTooNarrow(D-087, ADR-008). Perimeter Core no longer emitsNotSupported, and the test of that behaviour is removed. The other simplifiers, the four floor aggregators, and validation needed no change (ADR-013). No tolerance or validation check was relaxed. - D-091 is implemented in
RhinoGeometryfor every plan generator: zone solids outward-oriented, walls and windows facing their wall's outward side (court walls into the court), floors down, ceilings and roofs up, interzone surfaces included; this repairs the orientation of the Convert2BEM Breps and previews released up tov0.8.1and is documented indocs/architecture/convert2bem.md. The limit of D-090 (zones about 1 m wide on rotated plans) is unchanged. - The fifteen existing snapshots are byte-identical; one canonical snapshot is new. 158 core, 181 generator, and 601 integration tests pass on
net8.0, including a ring footprint and the enclosed court with every simplifier and aggregator on the canonical plan and on the plan rotated by 30° and moved by (5.3, −2.1) m and rotated by 131.4° and moved by (15.08, −153.9) m. - The S8.3 smoke test is recorded in the close-out commit; items it did not perform stay open for a person (D-058).
- S6 (Convert2IDF) and then S9 (Grasshopper UX and release, which depends on S6 and S8.3) can start.
D-093 — Precedent source: the four-source family set of typology-synthesizer¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Accepted · Supersedes: D-082
- Why:
typology-synthesizerhas extended its study from High-Density Housing (DETAIL) to four sources: High-Density Housing, Floor Plan Manual Housing (fifth edition), Architects' Data (second international English edition), and Metric Handbook: Planning and Design Data (second edition). Its active set (records version 3.0.0,data/four_source/, research tagv0.3.0-research, commit459dd2con branchcodex/handbook-integration) has 19 families,SYN-TYP-001toSYN-TYP-019, each with ordered topological generator rules and a symbolic validation report. - BEMGen takes all 19 families as its precedent set, of every program type, not only housing (D-094).
SYN-TYP-001toSYN-TYP-009are the nine High-Density Housing familiesHDHM-TYP-001toHDHM-TYP-009that S8 implemented (D-083), carried with their organisation unchanged (002and007gain further examples), so the existing nine generators stay valid. The new families are010to013(housing, from Floor Plan Manual Housing) and014to019(a branching shopping mall, an operating suite with separate clean and dirty routes, a cafeteria service edge beside a dining area, an office plate around a central service core, an elder-care home with a communal hub, and a public foyer branching to activity spaces). - The owner is updating
typology-synthesizerfor the findings of the 2026-10-02 review (work not yet on itsmain, an outdated README, a contradictory variant statement for007, no counterexamples for014and016–019, and an incomplete list of unassigned plans). BEMGen pins the tag and the SHA-256 of the records it consumes once that update has landed; whether BEMGen adopts theSYN-TYP-*IDs in its documents is settled in the stage plan. - Unchanged from D-082: the rules are topological only and no source supports a metric or count range, so dimensions, counts, and façade choices remain BEMGen design inputs; BEMGen has temporary read-only access to that repository, modifies nothing in it, and commits only paraphrased family rules with their stable IDs, never book content, page renders, or copied drawings.
D-094 — Non-residential programs are fully in scope¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Accepted
- The study and the plugin cover every program type of the precedent set (D-093), not only residential buildings: retail, healthcare (operating suites), food service, office, elder care, and assembly or community uses, alongside housing. Each of the 19 families gets a plan generator.
- Consequences: research question 2 of the research brief ("by residential building typology") widens to building typologies in general, and the repository spec, README, AGENTS.md, and roadmap are aligned with it in the next stage plan; new space types need their own typed program presets (D-061) with explicit, documented assumptions (illustrative values until reference values are chosen, as for the existing presets, D-023); the semantic types and program groups are extended as explicit data, never inferred from names.
D-095 — A plan generator may produce a different plan per storey¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Accepted
- Why:
SYN-TYP-012(stepped dwelling bands whose lower roofs become terraces) has a floor plan that changes from storey to storey, which one plan repeated over every floor cannot represent. - A plan generator may return one plan per storey. The interface change, how the floor aggregators treat storeys that differ (
Stack,StackedFloorZoneMultiplier,SingleZonePerFloorType,SingleZoneBuilding), and how a terrace is designated (the exposed part of a lower storey's roof is an outdoor roof, the covered part an interzone ceiling, D-068) are designed in an ADR in the stage plan.
D-096 — The incremental row (SYN-TYP-013) is set aside¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Accepted · Supersedes: D-094 in part
SYN-TYP-013(built row-house bays alternating with bays reserved for later growth) gets no plan generator for now; the other 18 families of D-093 each get one. D-094 is otherwise unchanged.- Why: modelling the reserved bays needs a choice the precedent does not settle (outdoor gaps, fully built bays, unconditioned shells, or the growth state as an input), and the family has a single provisional precedent. It can be taken up again by a later entry.
D-097 — Non-residential plans are zoned per department¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Accepted; refined by D-160 for the DOE generators
- At the most detailed level a plan generator gives a non-residential floor one zone per department: a contiguous group of rooms of one program in one part of the floor, not one zone per room or tenancy. Examples: the shops of one mall wing, an anchor store, the public mall; the bank of operating theatres, the clean corridor, the dirty corridor, the support rooms; the kitchen and servery, the dining area; the office work area, the service core; one group of care bedrooms, the communal hub; the foyer, each activity hall, the support rooms.
- Housing is unchanged: one zone per dwelling unit and storey (D-085), never split into rooms. Which rooms form a department in each family is set in the ADR of S8.4 and the family's generator documentation.
D-098 — The new families are built in S8.4–S8.7, before S6, and pushed after each sub-stage¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li (order, pushes, unsupervised run); controller (split) · Status: Accepted
- The nine new families of D-093 and D-096 are built in four sub-stages, each run like S8 (D-084: verified reference, headless Rhino check, plan, per-task execution, local merge and tag), without supervision; the controller chooses default dimensions and accepts its ADRs and plans provisionally, pending the owner's reading.
- S8.4 (tag
v0.8.3): the pinned records (D-093), the scope alignment of D-094 in the brief, spec, README, AGENTS.md, and roadmap, the non-residential space types and typed presets with their default preset components and icons, an ADR on program types and department zoning (D-097), and the families 017 (office plate around a central core) and 016 (cafeteria service edge). S8.5 (v0.8.4): branching plans with a shared hub-and-arms layout, families 010, 014, 018, and 019. S8.6 (v0.8.5): 015 (operating suite with separate clean and dirty routes) and 011 (linked clusters around several courts). S8.7 (v0.8.6): plans that differ by storey (D-095, with an ADR) and 012 (stepped bands with terraces). - After each sub-stage's gate and a fresh clone of its tag pass
scripts/verify.ps1,mainand the tag are pushed, never forced. S6 (Convert2IDF) follows S8.7; S9 depends on S6 and S8.7.
D-099 — BEMGen documents use the SYN-TYP-* family IDs¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Why: the precedent set is the four-source family set (D-093), whose IDs
SYN-TYP-001toSYN-TYP-019cover every family;SYN-TYP-001toSYN-TYP-009carry the nine High-Density Housing familiesHDHM-TYP-001toHDHM-TYP-009with the same organisation and rule numbering. - From S8.4 on, BEMGen documents name families by their
SYN-TYP-*IDs and give theHDHM-TYP-*lineage for001to009(syn-typologies.md).hdhm-typologies.mdstays as the issued baseline and is not updated further. Component descriptions and ADR-006 sections written in S8.1–S8.3 may keep theirHDHM-TYP-*IDs as long as theSYN-TYP-*ID is also given; descriptions are updated when a component is next changed, and no GUID changes.
D-100 — The hub-and-arms families have at most four arms¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Why: the families of S8.5 that branch from a hub (
SYN-TYP-010,014,018,019) share one layout,HubArmsLayout, which is axis-aligned in the plan frame of ADR-002, so its arms can only leave the hub on its four sides. - The hub-and-arms families are limited to 3–4 arms,
018to 2–4, as ADR-006 specifies: 3–4 lobes (010), 3–4 branches (014), 2–4 bedroom wings (018), and 2–3 halls (019, whose support wing takes one more arm, so 3–4 arms in all); a larger count isInvalidParameter. This is a limit of BEMGen's layout, not of the families, which set no bound (ADR-006: no family has an upper bound). It is revisited in S8.5 if a family needs a radial layout with arms at other angles.
D-101 — S8.4 complete¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Sub-stage S8.4 (D-098) is complete and tagged
v0.8.3; after its gate and a fresh clone of the tag passscripts/verify.ps1,mainandv0.8.3are pushed (D-098). The four-source records oftypology-synthesizerare pinned atv0.3.1-research(7538ed3) with their SHA-256 and paraphrased indocs/research/precedents/syn-typologies.md(D-093, D-099). The scope of D-094 is in the research brief (question 2, §4), the repository spec, README, AGENTS.md, and the roadmap (S8.4–S8.7 before S6; S9 after S6 and S8.7). - ADR-014, accepted by the controller under D-098 and pending the owner's reading, appends twelve
SpaceTypevalues (Office,Retail,Mall,Kitchen,Dining,OperatingTheatre,CleanCorridor,DirtyCorridor,ClinicalSupport,CareBedroom,CareCommunal,ActivityHall), reusesCore,Lobby, andService, and gives each of the fifteen types one illustrative, conditioned preset inExampleNonResidentialPresets(not DOE or standard values, D-023; ventilation only for the kitchen and the operating theatre; retail schedules with their own Saturday and Sunday) and a default preset component in 1 Program, with icons. It also sets which rooms form one zone in every new family (D-097). ADR-006 specifies all nine new families (010, 011, 012, 014–019) with controller design defaults (D-098), the hub-and-arms families with at most four arms (D-100). - New generators and components in 2 Generate, with icons: Office Plate (
OfficePlateGenerator,SYN-TYP-017, OPlate,783cefaa-b836-4dbc-b16d-8e74820ed361): anOfficering around a centralCorezone without outdoor walls, at least 3 m of work area on every side. Cafeteria (CafeteriaGenerator,SYN-TYP-016, Cafe,95956724-ddbc-48ce-9874-0ec6c244c8fe): aKitchenzone along the whole north edge of a largerDiningzone. The office ring is the first zone with a hole in a footprint without one; the surface builder, every simplifier, and every aggregator handle it without change. No tolerance or check was relaxed. - The sixteen existing snapshots are byte-identical; two canonical snapshots are new. 193 core, 224 generator, and 704 integration tests pass on
net8.0, including both families with every simplifier and aggregator on the canonical plan and on the plan rotated by 30° and moved by (5.3, −2.1) m and rotated by 131.4° and moved by (15.08, −153.9) m. - Smoke test (D-056): the controller's scripted headless Grasshopper session on the verified reference (
f841f0c, whosesrc/,tests/, andscripts/equal the executed branch) in Rhino 8.25.25314.11001 (.NET 8.0.31) passed every performed item: the 15 preset and 2 generator components registered with their GUIDs and 24 × 24 icons; every preset with only its remark; the default office plate (1260 m², core solid of volume 410.4, office solid of 10 faces with 2 inner loops and volume 4377.6, walls toward the core facing into it) and cafeteria; all 96 combinations (both families, three placements, four simplifiers, four aggregators, 1/3/1) with Validate passing and no negative volume; and the error cases. It is recorded indocs/development/grasshopper-smoke-test.mdand the close-out commit; the viewport look, the icons in the toolbar, and example definitions stay open for a person (D-058). - S8.5 (families 010, 014, 018, 019 on the shared hub-and-arms layout) can start.
D-102 — The layout choices of the hub-and-arms families¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Why: ADR-006 fixes the arm order of the shared hub-and-arms layout (east, north, west, south) but not which sides a family with fewer than four arms uses, and its shared zone-order rule (hub first, then arm by arm) differs from the zone order of its section on
SYN-TYP-019; the S8.5 reference settled both and made the layout's internals visible to their tests. - A family with n arms uses the first n sides in the arm order, so two care-hub wings (
SYN-TYP-018) leave the hub east and north, an L, not a bar through the hub; this is the only rule consistent with the foyer with halls (hall 1 east, the support wing north, hall 2 west, hall 3 south) and with three arms whose free hub side faces south. A straight bar for two arms would change only the side selection ofHubArmsLayoutand ADR-006. - The foyer with halls (
SYN-TYP-019) lists its zonesFO,H1…Hn,SV, as its ADR-006 section says: its support wing comes after the halls, the one exception to the arm-by-arm order, which ADR-006's shared paragraph records. Lod.Generators.csprojmakes its internals visible toLod.Generators.Tests(InternalsVisibleTo), the first use of the item in the repository, so the internalHubArmsLayoutis tested directly and not only through its four generators.
D-103 — S8.5 complete¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Sub-stage S8.5 (D-098) is complete and tagged
v0.8.4; after its gate and a fresh clone of the tag passscripts/verify.ps1,mainandv0.8.4are pushed (D-098). The four families that branch from a hub are implemented as ADR-006 specifies them on one shared, axis-aligned layout,HubArmsLayout(internal,Lod.Generators/HubArms/): a square hub with up to four arms of one width centred on its sides in the order east, north, west, south, a family with n arms using the first n sides (D-102), each arm with its own frame in which the families cut their zones; an arm wider than the hub and more than four arms areInvalidParameter(D-100). New generators and components in 2 Generate, with icons: Radial Lobes (RadialLobesGenerator,SYN-TYP-010, RLobes,a9f132c2-40c3-40b9-ba6a-3f3e1f2ced80): 3–4 lobes of two dwellings each around aStaircore; Branching Mall (BranchingMallGenerator,SYN-TYP-014, BMall,d886b33f-7b51-41f9-b160-a550e6aadd2b): oneMallzone through a central court and 3–4 branches ofRetailshop wings and anchors; Care Hub (CareHubGenerator,SYN-TYP-018, CHub,7d3d1002-c695-47df-afd9-48f50c5b3e90): 2–4 wings of aCorridorbetween twoCareBedroomzones around aCareCommunalhub, whose 0.8 m corners beside the default wings get no window (ADR-011); Foyer Halls (FoyerHallsGenerator,SYN-TYP-019, FHalls,6638efe7-c0e8-45b6-89b1-1e9e3f725c15): 2–3ActivityHallzones and aServicewing around aLobbyfoyer, zonesFO,H1…Hn,SV(D-102). No record or default of ADR-006 changed; ADR-006 records the details settled in S8.5. - Perimeter Core keeps the footprint's own vertices in its perimeter zones: it united each orientation bin's trapezoids with a plain union, whose grid rounding turned a footprint edge shorter than about 1 m (the care hub's corners) by more than the angular tolerance on rotated plans, so the single-zone aggregators reported
FacadeNotCovered(up to 26 of 800 combinations in a sweep, and at the test suite's 131.4° placement). Fixed test-first and documented in ADR-008 and ADR-002; no tolerance or check was relaxed, and every existing result is unchanged. - The eighteen existing snapshots are byte-identical; four canonical snapshots are new. 193 core, 360 generator, and 1184 integration tests pass on
net8.0, including every family with every simplifier and aggregator on the canonical plan, rotated by 30° and moved by (5.3, −2.1) m and rotated by 131.4° and moved by (15.08, −153.9) m, and with its fewest and most arms; arms not wider than twice the perimeter depth fail Perimeter Core withPlanTooNarrow(D-087). - Smoke test (D-056): the controller's scripted headless Grasshopper session on the verified reference (
12a7e83, whosesrc/,tests/, andscripts/equal the executed branch) in Rhino 8.25.25314.11001 (.NET 8.0.31) passed every performed item: the 4 generator components registered with their GUIDs, integer count inputs, presets in record order, Preview Location last, and 24 × 24 icons; the default radial lobes (624 m²), branching mall (7744 m²), care hub (1552 m², sixWindowOmittedcorners), and foyer with halls (1800 m²) with every zone a valid solid; all 320 combinations (four families, five placements and counts, four simplifiers, four aggregators, 1/3/1) with Validate passing and no negative volume; and the error cases. It is recorded indocs/development/grasshopper-smoke-test.mdand the close-out commit; the viewport look, the icons in the toolbar, and example definitions stay open for a person (D-058). - S8.6 (families 015 and 011) can start.
D-104 — The layout choices of the operating suite and the court cluster¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Why: ADR-006 fixes the records, defaults, zone IDs, and band and wing order of
SYN-TYP-015andSYN-TYP-011, but not the corridor count that follows from the band order, how Perimeter Core zones several courts, or whether the court cluster shares more code with the enclosed court; the S8.6 reference settled these. - Operating suite: a corridor follows every bank and the corridors alternate from a dirty one in the south, so n banks have ⌈n/2⌉ clean and ⌊n/2⌋ + 1 dirty corridors, numbered separately (
DC1, TB1, CC1, TB2, DC2, TB3, CC2, …); with an odd count the northmost band is a clean corridor. The theatre banks' only outdoor walls are their east ends, which the example theatre preset's WWR 0 leaves without a window and withoutWindowOmitted(ADR-011 rule 1). The generator tests' window check (PlanAssertions.HasOneCentredWindowPerOutdoorWall) now requires no window on an outdoor wall whose zone's preset has WWR 0, a stricter rule that every earlier generator test still passes. - Court cluster: shares
StairBayLayoutwith the enclosed court and nothing else, soEnclosedCourtGeneratorand its snapshot are unchanged; middle wings are named Middle i; the stair bays of the south and north wings are checked only for a validCourtCount. - Perimeter Core with several holes is unchanged: the court trapezoids of one bin are united across all holes, so each court gives its own zone per bin, numbered
P-Court-<Bin>-1…in the union's output order, which is west to east on the canonical plan but is not a court number on a rotated plan (ADR-008). Naming court zones per court is left to a later ADR.
D-105 — S8.6 complete¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Sub-stage S8.6 (D-098) is complete and tagged
v0.8.5; after its gate and a fresh clone of the tag passscripts/verify.ps1,mainandv0.8.5are pushed (D-098). The two families are implemented as ADR-006 specifies them, with no record, default, or rule changed (D-104). New generators and components in 2 Generate, with icons: Operating Suite (OperatingSuiteGenerator,SYN-TYP-015, OSuite,33323afd-abf1-4ecc-bf54-5d67c411c962): aClinicalSupportzone across the west end and bands ofOperatingTheatrebanks between alternatingDirtyCorridorandCleanCorridorzones, every bank between one clean and one dirty corridor, no clean corridor beside a dirty one, every band adjoining the support rooms; Court Cluster (CourtClusterGenerator,SYN-TYP-011, CCluster,01fc2ca9-3ec0-4142-b827-2519a4523d36): wings of stair bays around a row of at least two courts, each middle wing bordering two courts, the first footprint with several holes. - No step of
Lod.Corechanged: every simplifier, aggregator, and validation handles several holes, and Perimeter Core gives each court its own court zones per orientation (ADR-008, ADR-002); wings not wider than twice the perimeter depth, between two courts included, fail it withPlanTooNarrow(D-087). No tolerance or check was relaxed. - The twenty-two existing snapshots are byte-identical; two canonical snapshots are new. 193 core, 427 generator, and 1385 integration tests pass on
net8.0, including both families with every simplifier and aggregator on the canonical plan, rotated by 30° and moved by (5.3, −2.1) m and rotated by 131.4° and moved by (15.08, −153.9) m, with the fewest and the most tested counts (2 and 5 banks, 2 and 4 courts), and with the most at 131.4°; a sweep of 50 rotations for both families and, as a regression, the enclosed court and the S8.5 families found no failure in 14 400 combinations. - Smoke test (D-056): the controller's scripted headless Grasshopper session on the verified reference (
373af00, whosesrc/,tests/, andscripts/equal the executed branch) in Rhino 8.25.25314.11001 (.NET 8.0.31) passed every performed item: the 2 generator components registered with their GUIDs, integer count inputs first, presets in record order, Preview Location last, and 24 × 24 icons; the default operating suite (1046.4 m²) and court cluster (2880 m², Perimeter Core 13 zones, 17 with three courts), with all 92 zone Breps inspected valid solids and no negative volume anywhere; all 160 combinations (two families, five placements and counts, four simplifiers, four aggregators, 1/3/1) with Validate passing; and the error cases. It is recorded indocs/development/grasshopper-smoke-test.mdand the close-out commit; the viewport look, the icons in the toolbar, and example definitions stay open for a person (D-058). - Markup: the S8.5 close-out left a stray code-fence line after D-102 and after D-103 in this log and at the end of
docs/development/grasshopper-smoke-test.md, so the text after it rendered as code. The S8.6 documentation commit removed the line in the smoke-test guide and the S8.6 close-out the two in this log, changing no entry's text; the close-out checks that every Markdown file it touches has balanced code fences and that no appended entry ends with a fence line. - S8.7 (
SYN-TYP-012, plans per storey, D-095) can start.
D-106 — Plans per storey: a sibling generator base, list wiring, and multipliers¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Why: D-095 left the interface, the treatment of storeys that differ, and the designation of terraces to an ADR; ADR-015 records the options and this decision.
- A family whose plan differs per storey is a
MultiStoreyPlanGenerator<TParameters, TPresets>(Lod.Core.Plans) returning one ordinaryIGeneratedPlanper storey, bottom-up, sharing the preset checks, surfaces, windows, and provenance withPlanGeneratorthrough one internal helper (PlanBuilder), so the plans of the sixteen single-plan families are unchanged; each plan recordsStorey(from 0), and a storey's diagnostics are prefixedStorey k:. Rejected: every generator returning a list, a storey index parameter, and a new pipeline type for a stack of storeys. - No floor aggregator, validation rule, or pipeline type changes: the storeys are floor entries with multiplier 1, and the designation on the assembled stack (D-068) already makes the uncovered part of a lower ceiling an outdoor roof (a terrace) and the covered part a piece between storeys, with ground and roof areas that validate (
TerraceTests, which passed on the S8.6 code). Multipliers above 1 are allowed and repeat a storey; the generator's building is the one with every multiplier 1. - In Grasshopper the generator's Plan output is a list, wired item-wise through a simplifier into an aggregator's Floors with Multipliers unconnected; each storey's preview is raised to its elevation, while the plans stay at elevation 0.
SteppedBandGeneratoradditionally requiresLength > StairWidth(a dwelling row of no width otherwise), a check ADR-006 did not list; its Implemented (S8.7) note records it.- With four storeys of the default 14 m base depth the top storey is 7 m deep and fails Perimeter Core with
PlanTooNarrow(D-087); the integration tests therefore run four storeys with a 17 m base depth and test the default-depth case as that failure, and no default changed. The core of the default band's top storey under Perimeter Core is 0.86 m deep, below the about 1 m of D-090, yet every combination validates, at both rotated placements and in a sweep of 50 rotations; D-090 stays the known limit.
D-107 — S8.7 complete; S8 complete¶
- Date: 2026-10-02 · Decided by: controller (unsupervised run, D-098) · Status: Provisional
- Sub-stage S8.7 (D-098) is complete and tagged
v0.8.6; after its gate and a fresh clone of the tag passscripts/verify.ps1,mainandv0.8.6are pushed (D-098). ADR-015, accepted by the controller under D-098 and pending the owner's reading, fixes plans per storey (D-095, D-106). - New generator and component in 2 Generate, with icon: Stepped Band (
SteppedBandGenerator,SYN-TYP-012, SBand,3c685bfb-b45b-45af-98d2-8318a6314922): a band of a west stair, dwellings, and a rear corridor that every dwelling adjoins, stepping backStepDepthper storey so that each lower roof keeps a terrace ofLength × StepDepth; by default 3 storeys of 576, 468, and 360 m², 33 windows. Its Plan output is the list of storeys, bottom-up. - No step of
Lod.Corechanged apart from the new base and its shared helper; no tolerance or check was relaxed. The twenty-four existing snapshots are byte-identical; one canonical snapshot is new. 210 core, 463 generator, and 1485 integration tests pass onnet8.0, including the band with every simplifier and aggregator on the canonical plans, at the two rotated placements, with 2 and 4 storeys, and with 4 at 131.4°; a sweep of 50 rotations for four bands and, as a regression, the linear plan, the enclosed court, the court cluster, and the care hub found no failure in 6400 combinations. - Smoke test (D-056): the controller's scripted headless Grasshopper session on the verified reference (
a98d4c5, whosesrc/,tests/, andscripts/equal the executed branch) in Rhino 8.25.25314.11001 (.NET 8.0.31) passed every performed item: the component registered with its GUID, the integer Storey Count first, the presets in record order, Preview Location last, and a 24 × 24 icon; the Plan list of three storeys of 576, 468, and 360 m², each previewed at its elevation; every simplifier returning three floors and Stack Floors validating with multipliers 1 and with its Multipliers left at the default, roof and ground area 576 m²; the terraces facing outdoors and the covered parts between storeys; all 42 zone Breps inspected valid solids and no negative volume anywhere; all 80 combinations (five placements and counts, four simplifiers, four aggregators) with Validate passing; and the error cases. It is recorded indocs/development/grasshopper-smoke-test.mdand the close-out commit; the viewport look, the icons in the toolbar, and example definitions stay open for a person (D-058). - With S8.7, S8 is complete: 18 plan generators, one for every family of the four-source set except the set-aside
SYN-TYP-013(D-096). S6 (Convert2IDF) can start; S9 follows S6.
D-108 — Storey previews, an any-program preset, and a panel for built-in presets (S8.8)¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li (the three requests); controller (their realisation and the stage) · Status: Accepted
- Why: the owner's review of S8.4–S8.7 found the storey plans of a multi-storey generator previewed at their storey heights but the floors simplified from them previewed at height 0; asked for a data node that turns a program-specific preset into a base preset usable on every program input of every generator; and asked for the built-in presets in a panel of their own.
- Previews. A floor simplified from a storey plan (ADR-015) is previewed at that plan's storey height, as the plan is, so the plan generator and the simplifiers show storeys at the same heights; plans and floors that are not storeys stay at height 0. Only the preview moves (D-062): data, Breps, and Convert2BEM are unchanged.
- Any-program preset. A Grasshopper parameter (a data node like the native Geometry parameter) casts any preset, built-in or general, to a preset without a space type. Every program input of every plan generator accepts such a preset, and the zones keep the space type the generator's layout gives them; a preset that keeps its space type must still match its input (
PresetSpaceTypeMismatch). The use of an any-program preset on an input is recorded in the plan's provenance, so no program assumption changes silently (GLOBAL.md scientific rule 3). - Panel. The 18 built-in preset components (Dwelling Unit, Corridor, Stair, and the fifteen of ADR-014) move to a new panel 1 Program Presets; 1 Program keeps Program Preset, Load, Schedule, and the new parameter. No GUID changes (D-064).
- These are built in revision stage S8.8 (tag
v0.8.7), run like S8.4–S8.7 (D-098) before S6; pushing waits for the owner.
D-109 — How the storey previews, the any-program preset, and the preset panel are built¶
- Date: 2026-10-02 · Decided by: controller (D-108) · Status: Provisional
- Why: D-108 fixed the three requests and left their realisation to the controller; ADR-014 and ADR-015 record the options.
- Any-program preset.
ProgramPreset.SpaceTypeis nullable;AsAnyProgram()returns the preset without a space type and with its name, program, and WWR unchanged; presets are still created typed. The slot check of every generator accepts an any-program preset on every slot, a typed preset must still match (PresetSpaceTypeMismatch); the zones keep the layout's space type.Presetsrecords each slot's space type with the preset name, and each slot given an any-program preset addsAnyProgramPreset.<Input>=<name>; plans of typed presets are unchanged. Rejected: aSpaceType.Anyvalue, a flag beside a kept type, matching by name. - Grasshopper. The parameter Any Program Preset (AnyP,
AnyProgramPresetParameter,24bc6a12-ad06-4c25-bc9d-4e8318b8744c) in 1 Program casts what is wired into it through itsPreferredCast(a preset goo, a plain preset, or a plain preset wrapped by Grasshopper, as a script gives it); the generators' preset inputs (ProgramPresetParameter) take its goo through theirs; one shared sentence in every preset input's description. The eighteen built-in preset components move to1 Program Presetsand keep the Program accent. - Storey previews. Storey plans record
StoreyElevation(the floor heights below, m), one more provenance key on every plan of aMultiStoreyPlanGeneratorsuch as the stepped band;StoreyProvenance.ElevationOffollows operations of one input, so plans, moved plans, and floors of a storey preview at its height; buildings and other plans and floors at 0. Rejected: a field on the pipeline types,Storey × FloorHeight.
D-110 — S8.8 complete¶
- Date: 2026-10-02 · Decided by: controller (D-108) · Status: Provisional
- Revision stage S8.8 (D-108) is complete and tagged
v0.8.7locally; pushing waits for the owner (D-108). Implemented as D-109 decides: the any-program preset (ADR-014), the parameter Any Program Preset (AnyP,24bc6a12-ad06-4c25-bc9d-4e8318b8744c, with icon) in 1 Program, the panel 1 Program Presets with the eighteen built-in preset components, and storey previews atStoreyElevationfor plans, moved plans, and floors (ADR-015). No GUID changed; data, Breps, and Convert2BEM outputs unchanged. - The twenty-five existing snapshots and sixty existing icons are byte-identical. 243 core, 518 generator, and 1518 integration tests pass on
net8.0, including any-program presets on every input of all 18 generators and the linear plan and stepped band with any-program presets through every simplifier and aggregator, each validating. - Smoke test (D-056): the controller's scripted headless Grasshopper session on the verified reference (
d9c3059, whosesrc/,tests/, andscripts/equal the executed branch) in Rhino 8.25.25314.11001 (.NET 8.0.31) passed every performed item: Any Program Preset (AnyP, primary, 24 × 24 icon) in 1 Program with Program Preset, Load, and Schedule, and the eighteen built-in presets in 1 Program Presets with unchanged GUIDs; the cast of Office Preset shown asPreset Example Office (any program, no space type, WWR 0.4); AnyP(Office) on the Linear Plan Generator's Corridor input with no runtime message,AnyProgramPreset.Corridor=Example Officein the provenance, and Validate passing, while Office Preset wired directly still givesPresetSpaceTypeMismatch; AnyP(Kitchen) on both inputs of Office Plate; AnyP(Care Bedroom) on all three inputs of Stepped Band with all 16 simplifier × aggregator combinations validating; and the storeys of the default stepped band drawn at 0–3, 3–6, and 6–9 m by the generator and every simplifier, also through Transform Plan and with a simplifier's Preview Location, while the linear plan and its floors stay at 0–3 m. It is recorded indocs/development/grasshopper-smoke-test.mdand the close-out commit; the viewport look, the toolbar panels and icons, a non-preset value wired into AnyP, and example definitions stay open for a person (D-058). - S6 (Convert2IDF) can start.
D-111 — Lean stage process; the handoff tradition is retired¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Accepted · Supersedes: D-048, D-049, D-055; D-056 in part (the harness moves into the repository)
- Why: the owner's review of S8.4–S8.8 found that every stage's code was written once in a reference build and then reproduced three more times: a plan holding it verbatim, two replays of that plan, and a re-execution task by task. About 60 % of the tokens and half the wall time went into proving that copies equal the original. Real test-first work and every substantive defect found happened in the build and the headless Rhino check, while the copying layers caused defects of their own (stray code fences, broken anchors, line endings).
- From S6 on, every stage is run this way:
- Plan first, briefly. A design note in
docs/plans/of one or two pages, written before the build: scope, decisions, acceptance criteria, and a task list. It holds no copied code. - Build once. One agent builds the stage test-first on feature branches in a worktree, committing focused slices. Those branches are the deliverable; they are merged after the gate. Nothing is rebuilt from a plan.
- Review, not replay. A second agent reviews the diff of each slice or of the stage (correctness, invariants, documentation) before the merge.
- The headless Rhino check is a repository script (
scripts/rhino-smoke/, one spec per family or feature, D-056), run once per stage on the merge candidate. - No handoff archive. The
.handoff/archive, its reference copies,MANIFEST.sha256,stage_manifest.py, andcheck_plans.pyare retired and deleted with their.gitignoreentry, and so are the execution handoff plan and the Checkpoint 1 run summary (both remain in git history), the reference and replay worktrees and branches, and the handoff rules in the agent definitions. Completed plans and past decision-log entries keep their historical text; links to deleted files become plain text. - Lighter records. One decision-log entry per stage (choices and completion together); AGENTS.md "Project state" is a short summary pointing to the roadmap's progress log and is not rewritten every stage; smoke-test checklists stay short.
- Parallel work. Independent parts of a stage (for example separate families) are built in parallel worktrees and merged one after another.
- Kept: test-first development,
scripts/verify.ps1before each merge (not before every commit), the headless Rhino check, byte-identical snapshots and icons unless a change is documented, the fresh-clone gate at tags, and pushes only with the owner's approval. Agents:bemgen-complexbuilds stages and reviews geometry, numerics, and pipeline code;bemgen-boundeddoes documents, records, and bookkeeping and reviews them.
D-112 — Convert2IDF scope: EnergyPlus 25.x, an envelope preset, ideal loads, no runs yet¶
- Date: 2026-10-02 · Decided by: Cheng Xuan Li · Status: Accepted; superseded in part by D-130 (EnergyPlus 26.2, weather and outputs chosen, EnergyPlus run in opt-in tests)
- Convert2IDF (S6) targets EnergyPlus 25.x (25.2), and EnergyPlus is not run, neither by tests nor by the component; the IDF is checked as text and by parsing it back.
- Constructions come from a new typed envelope preset, shared by Convert2IDF and Convert2BEM and the same at every level of detail.
- HVAC is ideal loads for conditioned zones (unconditioned zones get none).
- Simulation scope is not decided yet: no weather, climates, or output selection; the IDF holds only the minimum simulation objects. Both are taken up when simulations are run.
- The realisation is set in ADR-010 (controller choices provisional), and S6 is planned in the S6 design note (D-111).
D-113 — S6 complete¶
- Date: 2026-10-02 · Decided by: controller (D-111, D-112) · Status: Provisional
- Stage S6 (D-112) is complete as version 0.9.0; tagging
v0.9.0and pushing wait for the owner. The plannedv0.6.0becamev0.9.0because the versions since S8 reached 0.8.7 and must not go back; S9 staysv1.0.0. Built as D-111 sets out, from the S6 design note (now Done): part A moved the headless Rhino check intoscripts/rhino-smoke/; part B added the envelope preset (Lod.Core.Envelope:EnvelopePreset,EnvelopeValues,Construction,Layer,GlazingSpec,ExampleEnvelopePresets, illustrative values, envelope presets), the Envelope Preset component (89ebd842-fc6e-4a06-95f5-2fcfe39f08b8) and parameter (efcbaf51-af02-44af-8d57-c4ca82c2ab3d), and Convert2BEM's Envelope input; part C addedLod.Exportwith the IDF writer (ADR-010, convert2idf.md); part D added the Convert2IDF component (2IDF,56dd6611-90b2-4299-b53c-650652843a1a, 5 Convert, with icon) over theConvert2Idfservice, which writes the file only when Write is true and an existing file only when Overwrite is true. Each part was reviewed by a second agent before its merge. - Choices made in S6 beyond ADR-010's draft (recorded in ADR-010, pending the owner's reading):
- Coordinates: relative coordinates with every zone origin at 0, so the vertices are the building's own, and the building orientation (
OrientationDegrees) as theBuildingNorth Axis; the draft's world coordinates with north axis 0 would have dropped the orientation, which EnergyPlus ignores in world coordinates. - Degenerate geometry from grid clipping on rotated plans (D-090): slivers (a cleaned ring with fewer than three vertices or an area of at most the distance tolerance times its perimeter), zero-width spikes, and excursions through a repeated vertex are not written, on both sides of a pair, with the warning
SliverOmittednaming the zone, floor or ceiling, elevation, and the areas removed; a floor or ceiling that is only slivers is omitted before partners are matched; a ring that crosses itself, so that rebuilding it would change its area, is the errorDegeneratePolygon. Edges shorter than the 0.01 m at which EnergyPlus merges vertices give the warningShortEdge; none occurs in the tests. - Numbers are written with fifteen significant digits (
G15), formatted identically by .NET Framework 4.8 and .NET 8; the rounding is far below every tolerance. - Loads with a basis EnergyPlus has no field for (per exterior wall area, per person for infiltration, any basis of hot water) are written as the absolute design level of one zone instance, so no basis is an error.
- Run period: a non-leap calendar year starting on the presets' first day of the year (2018 for Monday), without the weather file's holidays and daylight saving, so the hourly schedules fall on their days.
- Convert2BEM has two new outputs, Constructions (aligned with Surfaces) and Window Constructions (aligned with Windows), appended after Provenance, instead of one.
- Envelope roles: an exposed-floor role was added; an interior floor is listed from the zone below to the zone above, its ceiling side uses the reversed construction, and of two walls between zones the one whose zone ID sorts after the other's uses the reversed interior wall;
ConstructionRolesis the one place that decides, for both converters, and a surface without a role is the errorNoConstructionRole. - IDD check: every class the writer uses was compared with the EnergyPlus 25.2.0 IDD, every object is written to its
\min-fieldswith the IDD defaults explicit, andscripts/idd-check/checks IDF files against an IDD kept outside the repository: 0 problems on the 355 files the tests convert and on the 33 files the Rhino run wrote. - To verify on the first EnergyPlus run (D-112): per-area loads in zones spanning storeys rely on EnergyPlus using the stated zone floor area; the window construction is named like its glazing material.
- Completion: the existing snapshots and icons are byte-identical; five new IDF snapshots. 294 core, 518 generator, 475 export, and 1518 integration tests pass on
net8.0(scripts/verify.ps1, 0 warnings). Headless Rhino check (2026-10-02, Rhino 8.25): the new specconvert-idfpassed (32 of 32 buildings validate, every Convert2IDF output written, Write false, Overwrite, and a missing path behave as specified, Envelope Preset and Convert2BEM's envelope outputs as documented, and a connected Envelope input without an envelope preset warns in both converters), andany-preset-and-previewsandlinear-and-aggregatorsare unchanged; in Rhino the spec counts each IDF's objects and the IDD check runs on the files it wrote, while the parse-back of references and invariants runs in the unit tests (Lod.Export.Tests), not in Rhino; Path accepts only fully qualified paths; a failed validation cannot be built from the components, so blocking and Override are covered by unit tests only. - Open for a person (D-058): the toolbar look of the new icons, and a first EnergyPlus run once a weather file and outputs are chosen (D-112), which also settles the two items above. Next: S9.
D-114 — Floors and ceilings between storeys are clipped as conformed polygons¶
- Date: 2026-10-03 · Decided by: controller (D-111) · Status: Provisional
- Why: S6 found zero-width artefacts on rotated and moved plans, spikes, excursions through a repeated vertex, and slivers about 1e-6 m wide, which validation accepts and Convert2IDF omits with
SliverOmitted(D-090, ADR-010). Built from the zero-width artefacts design note (now Done) as D-111 sets out, and reviewed by a second agent. - Root cause: every artefact lay in a floor or ceiling piece cut by
Storeys.ResolveStacked, none in a zone part or wall. Where a vertex of one storey's zone lies on an edge of a zone of the adjacent storey (a T-junction), clipping on the 1e-6 m grid rounded it off that edge. - Fix: the footprints of each pair of adjacent storeys are prepared together as conformed polygons (
PolygonOps.Conform, the preparation the union of tiles already used): vertices in one grid cell are unified, T-junctions are inserted into the edges they lie on, and result vertices go back to the footprint vertex registered first in their cell. One conformed pair per pair of storeys serves the lower storey's ceilings and the upper storey's floors, so both sides of a slab have the same vertices and area. Within a cell the first registered vertex wins, so vertices up to √2 ×Distanceapart can merge, as in the union of tiles; the area effect is far below the relative area tolerance. Documented in ADR-002, with notes in ADR-010 and ADR-013. No tolerance or validation check changed. - Sweep (not committed): 50 placements of each of the 18 families through all 16 simplifier and aggregator combinations, 14,400 buildings: artefacts in 1,249 buildings (43,232 findings) before, none after.
ZeroWidthArtefactTestskeeps every family canonical and at the two placements of the typology tests free of spikes, repeated vertices, excursions, necks, and slivers. - Intended behaviour change: rounding slivers no longer face outdoors, so a rotated setback stack is fully covered, and Stacked Floor Zone Multiplier no longer splits off its lowest setback storey for exposure (D-046): the storeys of the rotated setback test go from
L0x1,L1x1,L2x1,L3x1,L5x2,L6x1toL0x1,L1x1,L2x1,L4x3,L6x1. This is correct because that storey's floor is fully covered by the storey below; the split was caused only by about 1.4e-5 m² of rounding slivers, not by exposure. Axis-aligned results and every snapshot are unchanged. - Convert2IDF's handling of slivers, spikes, and excursions stays as a guard; no generated conversion of the tests warns
SliverOmittedany more, which the export tests now assert, also at a second placement (131.4°, (15.08, −153.9) m). - Checks:
scripts/verify.ps1passed with 0 warnings (346 core, 518 generator, 635 export, and 1573 integration tests onnet8.0). On346af8ethe controller ran all 11scripts/rhino-smokespecs (every Validate true, no exceptions, errors only in the error scenarios) and the IDD check against the EnergyPlus 25.2.0 IDD (0 problems in all 515 IDF files the export tests write). The later commits cache the conformed pairs, which gives byte-identical floors and ceilings in all 1,152 buildings of four placements of every family, and add tests and documentation. - Version 0.9.1, tag
v0.9.1(controller): results on rotated plans differ fromv0.9.0(no rounding slivers, the setback regrouping), so the change gets its own version for reproducibility; no snapshot changed.
D-115 — S9 is the release: a local docs site, generated examples, a Yak package, MIT, v1.0.0¶
- Date: 2026-10-03 · Decided by: Cheng Xuan Li · Status: Accepted · Supersedes: D-058 in part (example definitions) · Superseded in part by D-117 (where the docs site lives and whether it is published)
- Why: the Grasshopper polish S9 was planned for (2026-09-30) was done along the way (icons and preset components in S4.1, the preset panel and any-program preset in S8.8, runtime messages from diagnostics throughout), so S9 becomes the release, with the owner's additions.
- Docs site. A Markdown site built with MkDocs, served on localhost for now and not published (no GitHub Pages). It separates clearly a user part (getting started, definitions of the concepts, a workflow guide, one end-to-end example, the typologies, and a component reference with every component's inputs, outputs, defaults, icon, and screenshots) from a developer part (the architecture documents, ADRs, and decision log, assembled from
docs/at build time, never copied into the repository). The component reference is generated from the plugin itself, so it cannot drift from the code. sync-docsskill. A project skill checked into the repository (.claude/skills/sync-docs/) that checks whether the docs site and the developer docs match the implementation (components, inputs and outputs, defaults, GUIDs, icons, decisions) and outlines a plan to bring them up to date; it is run on demand and as a step of every stage close-out (D-111).- Example definitions. One
.ghdefinition per typology and the end-to-end example, built and saved by the headless harness (scripts/rhino-smoke), inexamples/; a person opens them to check. This supersedes D-058 for example definitions; other person-only items (viewport look, toolbar) stay open as D-058 says. - Screenshots are captured by script inside Rhino (viewport capture, Grasshopper canvas image export); if the canvas cannot be captured that way, the controller asks the owner once for screen control of Rhino.
- Yak package. A local build script makes the Rhino package; it is never published to the package server without the owner's approval.
- License: MIT, copyright Environmental Systems Lab.
- Version 1.0.0, tag
v1.0.0, marks the first complete release; backward compatibility is still not required (D-064 unchanged), and component GUIDs never change. - Run as D-111 sets out, from the S9 design note.
D-116 — S9 complete; v1.0.0¶
- Date: 2026-10-03 · Decided by: controller (D-111, D-115) · Status: Provisional
- Stage S9 (D-115) is complete as version 1.0.0; the tag
v1.0.0is set at the fresh-clone gate and pushing waits for the owner. Built as D-111 sets out from the S9 design note (now Done), in two parallel builds (R needs Rhino, W does not), each reviewed by a second agent. - Delivered:
- Docs site (MkDocs, Material theme, pinned requirements, served on localhost, never published) with a user part (getting started, concepts, workflow guide, end-to-end example, the 18 typologies) and a developer part, separated in navigation and landing pages.
- Component reference generated from the plugin's own metadata (every component and parameter with icon, GUID, inputs, outputs, defaults), and screenshots of the canvas and viewport of every example.
- 19 example definitions in
examples/(one per plan generator andend-to-end.gh), built and saved by the headless harness. sync-docsproject skill, a local Yak build (scripts/package-yak.ps1,bemgen-<version>-rh8_19-win.yak, nothing published), and the MITLICENSEof Environmental Systems Lab.- Choices beyond D-115:
- The developer part is assembled at build time into a staging folder; its links that point outside
docs/are rewritten to GitHubmainand are not checked bymkdocs build --strict. - The build fails if the generated component reference is missing, unless
-AllowMissingReferenceis passed. - The examples feed several floors into one input through Grasshopper's Merge, because Grasshopper ignores repeated wires from one output to one input.
- The screenshots are captured by script inside Rhino; no screen control was needed.
- The plugin version is read from the
.ghaassembly itself, so that Yak can inspect and load it (bcc3968). - The Grasshopper freeze found in S9. BEMGen's Goo types had no public parameterless constructor.
GH_Param<T>.TypeNameandInstantiateTcallActivator.CreateInstance, which threw, and Grasshopper'sTracing.Assertopened a modal breakpoint dialog that blocked Rhino: on a wire of the wrong type, on the "Data conversion failed" message, and on component help, in every released version up to v0.9.1. Fixed by public parameterless constructors and null-safe empty instances (d8e0650,19c8e2b); the smoke check now reads every parameter's type name, wires a plan into an aggregator's floors input, and fails when an expected runtime error is missing (a3398d8,f4ed7a2,c86746d). - Checks (controller, on
mainat19c8e2b):scripts/verify.ps1passed with 0 warnings (346 core, 518 generator, 1573 integration, and 635 export tests onnet8.0);scripts/docs-site/build.ps1with a fresh virtual environment fromrequirements.txtprintedDOCS BUILD PASSED(mkdocs --strict); all 12scripts/rhino-smokespecs ran in Rhino 8.25.25314.11001 headless with every Validate True, 0 exceptions, 0 missing components, 0 expected errors missing, and runtime errors only in the error scenarios, including the new one that wires a plan into the aggregator's floors input and requiresData conversion failed from Plan to Floor; all 19 example definitions reopen and solve (Validate True 19 of 19, 0 errors, 0 warnings). - Open for a person (D-058): open the examples in Rhino 8 and look at the canvas and the viewport (window fills may show transparency-sorting artefacts); drag the
.yakonto Rhino 8; confirm that a wrong-type wire shows only the conversion error and no dialog; the toolbar look of the icons; and a first EnergyPlus run once a weather file and outputs are chosen (D-112).
D-117 — The docs site moves to a public repository and is deployed to GitHub Pages¶
- Date: 2026-10-03 · Decided by: Cheng Xuan Li (the move, public, GitHub Pages); controller (the plan and its defaults, Provisional until the owner answers the plan's questions) · Status: Accepted · Supersedes: D-115 in part (a site served on localhost only, never published, kept in this repository) · Superseded in part by D-118 (repository name, developer part, distribution)
- The MkDocs site leaves this repository for a separate public repository, proposed as
EnvironmentalSystemsLab/BEMGen-docs, and is deployed to GitHub Pages from GitHub Actions. It is moved, not copied:docs-site/,mkdocs.yml, andscripts/docs-site/are removed here once the new site is live. An agent working in the new repository does the import and the deployment. - Because BEMGen is private:
- the public site cannot check out or link into BEMGen;
- by default its developer part is not published, and the developer docs stay in
docs/here; - the generated reference, icons, and screenshots are still made in Rhino from a BEMGen checkout, now through a docs export (
scripts/docs-export/), and committed in the docs repository. sync-docsis split. BEMGen's skill checks the developer docs and says when the public site must follow; the docs repository's skill checks the user site against the plugin.- Plan, owner questions with defaults, and handover material: docs site move. The Yak package stays unpublished (D-115).
D-118 — Docs move: the owner's answers; the developer part and the release files are public; v1.0.1¶
- Date: 2026-10-03 · Decided by: Cheng Xuan Li (answers 1 to 6); controller (the order of the steps and version 1.0.1, Provisional) · Status: Accepted · Supersedes: D-117 in part (repository name, developer part not published); D-115 in part (release files not published) · Superseded in part by D-119 (redaction scope)
- Repository:
energy-atlas/BEMGen-docs, deployed tohttps://energy-atlas.github.io/BEMGen-docs/from GitHub Actions; MIT, with a fresh history. - Developer part is public.
docs/,GLOBAL.md, andAGENTS.mdstay the sources here and are published through the docs export (scripts/docs-export/). Because BEMGen stays private: - their links to source files become plain paths;
- e-mail addresses and local paths are redacted from the published copy only, and every redaction is listed;
- the log here is not rewritten.
- Typologies page: approved by the owner for publication as it is.
- Decision numbers are removed from component, parameter, input, and output descriptions (text only; GUIDs unchanged), since Grasshopper users and the public reference see them.
- Release files are published on the docs repository's GitHub Releases for each version: the
.ghawith its libraries as a zip, the.yak, the example definitions as a zip, and checksums (scripts/package-release.ps1). The Yak package server stays unused. - Order: BEMGen builds the export, the release script, and the removal of the site as version 1.0.1; then the docs repository's agent imports, deploys, and releases
v1.0.1. Plan: docs site move, now Approved.
D-119 — Docs move, BEMGen side done; v1.0.1¶
- Date: 2026-10-03 · Decided by: controller (D-111, D-117, D-118) · Status: Provisional · Supersedes: D-118 in part (redaction scope)
- Step B of the docs site move is complete as version 1.0.1, tag
v1.0.1. It was built onfeature/docs-moveand reviewed by a second agent before the merge. Step C, the import, deployment, andv1.0.1release inenergy-atlas/BEMGen-docs, goes to the docs repository's agent with the handover. - Delivered:
- Decision and ADR numbers removed from every user-visible string in
src/Lod.Grasshopper(component, parameter, input, and output descriptions, and the components' own messages). The text is otherwise unchanged, and no GUID changed. scripts/docs-export/:- the two Rhino specs, moved from
scripts/docs-site/; the screenshots now go into the export; developer_pages.pywith its unit test;export.ps1, which writes the export folder its README describes.
- the two Rhino specs, moved from
scripts/package-release.ps1: the plugin zip, the.yak, the examples zip, andSHA256SUMS.txt, built only from a clean commit taggedv<version>. The zips use forward-slash entries.- The plugin's contact link and the Yak manifest's URL now point to the public site.
- Removed:
docs-site/,mkdocs.yml, the rest ofscripts/docs-site/, and their ignore entries. - BEMGen's
sync-docsreplaced by the version that checks the sources here and says when the public site must follow. - README, AGENTS, and the other files that mentioned the old site updated.
- Redaction scope (supersedes D-118 in part). The published copy redacts e-mail addresses and user-profile paths (
C:\Users\<name>, also writtenC:/Users/...and/c/Users/...). Lab paths such asD:\BEMGenstay, because they are instructions for the lab machine and not personal data. Today the only redaction is the commit identity's address in D-050. - Review findings fixed before the merge:
- the redaction scope above;
- portable zips, and refusing untagged or dirty release builds;
- the docs repository's skill wording, and the private repository's URL in the shipped plugin;
- fences closed only by a matching run, angle-bracket targets and single-quoted titles in links, and code text for links whose text is the file's stem;
- an export folder inside any git repository refused, and the developer-page link check run before Rhino starts;
- wording in the handover and the prompt.
- Checks:
scripts/verify.ps1passed with 0 warnings: 346 core, 518 generator, 1573 integration, and 635 export tests.- All 12
scripts/rhino-smokespecs passed in Rhino 8.25.25314.11001 on4ad721a, with every Validate True and runtime errors only in the error scenarios. All 19 examples solve. - A trial export on
4ad721agave 64 icons, 57 screenshots, 54 developer pages, 0 description gaps, one redaction, and no decision number left in the component metadata. - The developer-page unit tests pass (8).
- Open:
- Diagnostic messages from
Lod.Core,Lod.Generators, andLod.Exportstill cite decision numbers in 14 strings, and these reach Grasshopper users as runtime messages. The IDF header comment cites D-112. Both are outside D-118's literal scope and are left for the owner. - The example definitions still carry the 1.0.0 build stamp. They solve with 1.0.1 and were not rebuilt.
D-120 — Enum inputs: values in the right-click menu and a dropdown on Extract parameter¶
- Date: 2026-10-04 · Decided by: Cheng Xuan Li (the two ways to choose a value, the branch
features/dropdown-enums); controller (the realisation, Provisional) · Status: Accepted - Why: five inputs took an enum name as typed text, and only their descriptions listed the valid names: Schedule Kind and First Day, Load Type and Basis, and Program Preset Space Type. The owner asked for two things. Extract parameter on such an input should give a dropdown component. Its right-click menu should offer the enum's values next to Grasshopper's own items.
- Built from the enum inputs design note (now Done) as D-111 sets out, test-first against a new headless Rhino spec, and reviewed by a second agent before the merge.
- Realisation (controller, pending the owner's reading):
- The five inputs are
EnumParameter<TEnum>(src/Lod.Grasshopper/Parameters/EnumParameter.cs), an internal generic subclass of Grasshopper'sParam_String. - The data stays text. Panels, wires, saved definitions, and the components' case-insensitive parsing work as before.
- The class keeps the Text parameter's
ComponentGuid, icon, and type name. Grasshopper never registers it, because it is internal and generic. - No component GUID, input name, nickname, access, optional flag, or default changed. The descriptions add one sentence on how to choose a value.
- Right-click menu. The names follow Grasshopper's Set Text, Set Multiple Texts, and Manage Text collection items, in declaration order. The current value is ticked, read as the parser reads it. The names are disabled while the input has a source. A click is one undo step.
- Extract parameter. It places a Value List in dropdown mode left of the input, centred on it, labelled with its name, and wired into it.
- The list has every name in declaration order, each with its quoted name as the expression, so it outputs text. It is selected on the current value, or on the first name when the value is not one.
- Its description is the input's, without the note.
- It moves past any object it would overlap, away from that object, so the dropdowns of neighbouring inputs keep their order.
- The list and its wire are one undo record, built from Grasshopper's add-object and wire actions.
- Tests.
scripts/rhino-smoke/specs/enum-inputs.pymakes 179 checks, among them: the menu, a click, undo and redo of a click and of the extraction, the dropdown's items, selection, placement, and result, the disabled state with a source, no registered enum parameter, adjacent dropdowns in both orders, and a save and reopen.run.ps1now counts a line starting withCHECK FAILEDas a failure of its spec.- Checks (controller, on
a4ecc1a): scripts/verify.ps1passed with 0 warnings: 346 core, 518 generator, 1573 integration, and 635 export tests. No test changed, becauseLod.Grasshopperhas nodotnet testproject.- All 13
scripts/rhino-smokespecs ran in Rhino 8.25.25314.11001 with every Validate True, no exceptions, and no failed checks. Runtime errors came only in the error scenarios. All 19 examples solve. - Open:
- A person checks the menu and the dropdown on the canvas (smoke test).
- The public site's component reference needs a new export, since the five descriptions and the parameter class changed.
- The version for this change is the owner's choice.
D-121 — Version 1.0.2: the enum inputs, released with a docs export¶
- Date: 2026-10-05 · Decided by: Cheng Xuan Li · Status: Accepted
- Version 1.0.2, tag
v1.0.2, releases the enum inputs (D-120). The owner reviewed and approved them, and they were merged intomainand pushed. - The release goes on the docs repository's GitHub Releases, as D-118 sets out, with the files of
scripts/package-release.ps1built from the tagged commit: - the plugin zip, with
BEMGen.ghaand its libraries; - the
.yak; - the examples zip;
SHA256SUMS.txt.- A docs export of
v1.0.2(scripts/docs-export/export.ps1) carries the changed input descriptions to the public site's component reference. The docs repository imports it. - The example definitions are not rebuilt. They still carry the 1.0.0 build stamp and solve with 1.0.2 (D-119).
D-123 — Conditioned Merge, and joined pieces for both merge simplifiers¶
- Date: 2026-10-06 · Decided by: Cheng Xuan Li (a new plan simplifier that merges conditioned and unconditioned zones separately; a boolean on both merge simplifiers for separate pieces, default false; the branch
feature/conditioned-merge); controller (the realisation, Provisional) · Status: Provisional — pending the owner's reading, not merged - Numbered D-123 because the unmerged S10 branch holds D-122. Whichever branch merges second rebases its log entry.
- Why:
- No plan simplifier removed the semantic partitions inside conditioned space while keeping its boundary with unconditioned space. Perimeter Core and Single Zone per Floor enlarge the conditioned floor area wherever they cover a stair.
- Semantic Merge gives one zone per connected piece of a space type. Stairs and stair bays cut floors into many pieces, so "one zone per class" needs zones made of several pieces.
- The owner asked for both modes on both merge simplifiers.
- Built from the design note (now Done) as D-111 sets out: test-first on the branch, reviewed by a second agent twice (core, then Grasshopper), with every finding fixed.
- Realisation (controller, pending the owner's reading):
- Joined pieces.
SemanticMerge(tolerances, joinPieces)and the newConditionedMerge(tolerances, joinPieces)share one grouping. Zones are connected only through a shared wall.- Join Pieces false (the default) gives one zone per connected piece, which is
SemanticMergeunchanged. - True gives one zone per class on the floor, made of all its pieces.
- Provenance records
JoinPieces=trueonly when true, so every existing output is byte-identical.
- Join Pieces false (the default) gives one zone per connected piece, which is
- Conditioned Merge (Z1c). The classes are conditioned and unconditioned, decided only by whether the source's program has a thermostat.
- The targets are
Conditioned-n, thenUnconditioned-n, numbered in plan order. Each takes the space type its sources share, orMixed. - Aggregation is the existing rule (ADR-005, ADR-007, D-047).
- It keeps conditioned floor area exactly in both modes. With the example presets, joined, it gives at most two zones per floor on every family.
- The targets are
- Floor zones of several pieces. A floor zone may have several parts at one elevation:
TargetZone.Pieces, andLayoutSurfaceBuilderbuilds every piece under one ID, with floors and ceilingsF1…andC1…when a zone has several pieces;OverlapMapper,Storeys,SingleZoneMerge, and the overlap validation read every part;- new error codes
TouchingPiecesandDuplicateZoneId; a target without pieces isInvalidParameter; - the IDF writes one
Zonewith every piece's surfaces, and Convert2BEM gives one closed Brep per part; - previews draw same-storey pieces as separate solids; a Boolean union had fused pieces meeting at a corner.
- Components.
- Semantic Merge gains the input Join Pieces (J, default false) before Preview Location. Its GUID is unchanged.
- Conditioned Merge (Z1c,
4bf2f852-144c-4afb-8dcc-8c016787f571) is in 3 Simplify, with the same inputs and its own icon. - Old files. A Semantic Merge saved before Join Pieces is read with Join Pieces at its default, keeping its Preview Location data and wires. Without this, five shipped examples reopened with no output and no message, which D-064 does not intend. A fixture saved by the pre-change plugin keeps this tested.
- Research brief §8. It gains Z1c and the Join Pieces variant of Z1, and a table of what each level does to conditioned floor area (GLOBAL.md scientific rule 7).
- Checks (on
0cebbc6): scripts/verify.ps1passed with 0 warnings: 412 core, 518 generator, 2620 integration, and 1024 export tests.- The integration and export tests run every family × aggregator × new mode, at the default placement and rotated, and validate with no warnings. Conditioned floor area is asserted exact.
- No existing snapshot or assertion changed; the snapshot diff against
mainis additions only.
- All 14
scripts/rhino-smokespecs ran in Rhino 8.25.25314.11001 on0cebbc6with every Validate True, no exceptions, no failed checks, and runtime errors only in the error scenarios. They include the newmerge-piecesspec (four modes × four aggregators on the stair-bay bar, piece previews and Convert2BEM Breps per piece on three families, the pre-change fixture, a save and reopen, the Join Pieces descriptions) and the 19 examples, each with exactly one Validate True. - Open:
- For a person in Rhino 8: the order of 3 Simplify (nothing in BEMGen sets it), the look of Conditioned Merge and its icon, the piece previews in the viewport, and a pre-change file with a wired Preview Location on screen.
- Saved tooltips. Definitions saved with this branch's builds before
168f583keep the short Join Pieces tooltip, because Grasshopper restores descriptions from the file. No released file is affected. - Out of scope: a building-level counterpart, and the example definitions.
- The owner's choices: the merge, the version and tag, and the docs export that follows them.
D-124 — Program mix by floor-area share; infiltration becomes a building-level input¶
- Date: 2026-10-06 · Decided by: Cheng Xuan Li. The owner decided the operation and its rules:
- a component that mixes presets with weights, where a weight is a floor-area share;
- absolute loads added raw;
- setpoints mixed as zone merges mix them;
- the space-type default and the WWR weighted mean;
- infiltration moved wholly to the building level (option (a)), with an illustrative 0.3 ACH;
- the branch
feature/program-mix.
The controller decided the realisation, which is Provisional (ADR-017). · Status: Provisional, pending the owner's reading; not merged.
- Numbering. D-122 (S10 atlas) and D-123 (Conditioned Merge) are on other unmerged branches. Whichever branch merges later rebases its log entry. ProgramPreset.Source is the same cherry-picked commit on this branch and on S10.
- Why. Mixing presets raised two problems:
- loads in different unit bases;
- weights that depend on geometry a preset does not know, which today is infiltration per exterior wall area.
- How it was built. From the design note (now Done), as D-111 sets out: test-first, then reviewed by a second agent twice, first the core and then the Grasshopper side. The core review found a blocker: one input's absolute occupancy diluted another input's per-person load about 50-fold. It and every other finding were fixed.
- Realisation (controller, pending the owner's reading):
- Infiltration
- LoadType.Infiltration and LoadBasis.PerExteriorWallArea leave programs. The aggregator loses its exterior-wall path, and the example presets lose their rates (mall 0.8, lobby 0.6, retail 0.5, operating theatre 0.1, the rest 0.3 ACH).
- EnvelopePreset carries Infiltration: a rate in one basis (per exterior surface area, per exterior wall area, or air changes per hour) with a fraction schedule. The example envelope uses 0.3 ACH, always on.
- Convert2IDF writes one ZoneInfiltration:DesignFlowRate per zone, in the native EnergyPlus method. Convert2BEM outputs the infiltration and each zone's design flow (outputs 18–21), from ZoneInfiltration.
- EnergyPlus 26.2 counts the exterior area as outdoor walls with their windows, roofs, and exposed floors, but not the ground or adiabatic surfaces. That matches what BEMGen writes.
- Zone merges no longer aggregate infiltration; it follows each zone's own surfaces.
- ProgramMix.Mix runs the existing zone merge on a virtual zone of unit floor area:
- densities and ACH are weighted;
- per-person loads are weighted by occupants;
- absolutes are summed raw;
- schedules are weighted by magnitude;
- setpoints are averaged over the conditioned inputs, and the result is conditioned if any input is.
- Mix rules.
- Weights must be finite and greater than 0; they are normalised.
- Absolute occupancy in any input beside a per-person load in any input is an error.
- The space type and the WWR follow the owner's defaults.
- Provenance holds Input.k pairs (name, given weight, normalised weight) and nests each input's own source.
- Equivalence. A mix with weights 0.7/0.3 equals Single Zone per Floor on zones of 70 and 30 m², in every load, every hour of every schedule, and every setpoint.
- Components.
- Mix Programs (744378f8-861d-4e6d-bff4-68c7fe2b62c1) is new.
- Envelope Preset gains three infiltration inputs, and Convert2BEM four outputs.
- The default preset components lose their infiltration inputs.
- Load explains that infiltration belongs on the envelope.
- No existing GUID changed. The 19 examples were rebuilt.
- Research docs. The research brief, program-presets.md, and envelope-presets.md record infiltration as an envelope input and the equivalence of a mix with a zone merge (GLOBAL.md scientific rule 7).
- Checks (on 7c4a7d0 and the record commit after it):
- scripts/verify.ps1 passed with 0 warnings: 417 core, 518 generator, 1624 integration, and 705 export tests.
- 30 snapshots changed, all because of infiltration:
- 25 lose only their infiltration lines;
- 5 IDF snapshots gain the header line;
- 4 of those IDF snapshots also share the envelope's single schedule instead of writing one per zone.
- All 14 scripts/rhino-smoke specs ran in Rhino 8.25.25314.11001 with every Validate True, no exceptions, and no failed checks. The new program-mix spec has 17 scenarios and 76 checks: hand values, the cross-input occupancy error, the envelope bases against the Rhino geometry, and Convert2IDF. All 19 rebuilt examples solved, each with one Validate True.
- The IDD check against the EnergyPlus 25.2.0 IDD: 579 files, all three infiltration methods, 0 problems.
- Open.
- For a person in Rhino 8: the Mix Programs icon, the layouts of Envelope Preset and Convert2BEM, and the rebuilt examples in the editor.
- Old definitions open with Grasshopper's IO dialog. It drops the presets' saved infiltration values, which the release notes must state.
- Integration with S10: the atlas branch's infiltration handling becomes obsolete when both merge.
- The owner's choices: the merge order of the three branches, the version and tag, and the docs export.
D-125 — Version 1.1.0: Conditioned Merge and the program mix, released with a docs export¶
- Date: 2026-10-07 · Decided by: Cheng Xuan Li (approved the two stages; the merge, the tag, the push, and the release, on 2026-10-07) · Status: Accepted
- Version. 1.1.0, tag
v1.1.0. It is a minor version, chosen over 2.0.0 because D-064 waives backward compatibility: the new inputs and outputs and the infiltration that left the presets need no major version. - What it merges.
feature/conditioned-merge(D-123) andfeature/program-mix(D-124, ADR-017), rebased into one linear history (D-004), Conditioned Merge first. Their entries stay as written, with their per-branch counts and "not merged"; this entry gives what holds for the merged result. - The merged facts that D-123 and D-124 give only per branch:
scripts/verify.ps1passed with 0 warnings on the integrated candidate: 484 core, 518 generator, 2695 integration, and 1148 export tests.- Snapshots. Beyond D-124's 30, the four Conditioned Merge snapshots were regenerated for infiltration:
- three floor snapshots lose their infiltration load lines;
- the stair-bay bar's IDF snapshot gains the header line and the envelope's schedule.
- IDD check. 1016 files against the EnergyPlus 25.2.0 IDD, 0 problems, with all three infiltration methods covered (convert2idf.md).
- IDF names. The export tests had named the IDFs of the joined merge modes after the simplifier, as in
ConditionedMerge:JoinPieces. On Windows a colon makes the rest of the name an alternate data stream, so 288 files were never listed and never checked.IdfOut.FileNamenow replaces every character a file name cannot hold with_, andIdfOutTeststests it. - Zones of several pieces take the envelope's infiltration over every piece: the outdoor walls with their windows, roofs, and exposed floors of all pieces, or the volume. This is tested in the export tests, in the integration matrices that run every merge mode through every infiltration basis and the program mixes, and in the
merge-piecessmoke spec; the architecture documents and the research brief state it. - Smoke result. The 19 example definitions were rebuilt with the 1.1.0 plugin (
BEMGEN_EXAMPLES_WRITE=1): all reopen and solve with one Validate True each, no warnings or errors. All 15scripts/rhino-smokespecs ran in Rhino 8.25.25314.11001 on the candidate, with every Validate True, no exceptions, no failed checks, and runtime errors only in the error scenarios;merge-pieces(16 Validate True, with the multi-piece infiltration checks) andprogram-mix(17 scenarios) included. In the full runhub-armsstalled inside one scenario and wrote no result; run again on its own it passed in 45 s with its usual 54 scenarios and 324 Validate True. - What follows:
- The release goes on the docs repository's GitHub Releases, as D-118 sets out, with the files of
scripts/package-release.ps1built from the tagged commit: the plugin zip, the.yak, the examples zip, andSHA256SUMS.txt. - A docs export of
v1.1.0(scripts/docs-export/export.ps1) carries the new components, inputs, outputs, and the changed descriptions to the public site. The docs repository imports it and runs its ownsync-docs. - The release notes state that definitions saved before D-124 open with Grasshopper's IO dialog, which drops the presets' saved infiltration values (D-124).
- Open:
- The S10 atlas branch (D-122) is not in this release. When it merges, its infiltration handling must be removed, because infiltration is no longer a program input, and its log entry rebased.
- For a person in Rhino 8: the checks of the smoke test that only a person can make:
- the icons of Conditioned Merge and Mix Programs, the order of 3 Simplify, and the piece previews in the viewport;
- the layouts of Envelope Preset and Convert2BEM;
- a pre-change file with a wired Preview Location, the rebuilt examples in the editor, and the install from the release.
D-126 — Program presets from program JSON 2.0.0; the atlas fetch components retired¶
- Date: 2026-10-07 · Decided by: Cheng Xuan Li. The owner decided:
- to retire the S10 atlas fetch components (D-122) without backward compatibility;
- one parser component that takes the JSON text, with inputs that complete or override it: a WWR with an ordinary default (0.4), loads the JSON lacks (such as ventilation), each thermostat side and its schedule, the activity schedule, and the people fractions;
- to extend the program model to hold what the contract describes: a per-dwelling-unit basis; an activity schedule and people fractions; two lighting loads (end uses); two setpoint schedules, each with its own on/off; a hot-water target and an inlet temperature; and heat fractions of power loads;
- per-dwelling loads merge weighted by dwelling count, after the conflict with GLOBAL.md scientific rule 1 was raised (area weighting would not conserve installed power);
- shared services are not added;
- a merge of a sensible fraction that mixes a number with
autocalculategivesautocalculatewith a warning, and the other merge rules proposed stand; - an override load replaces the JSON loads of the same type and end use, or is added;
- Year defaults to 2007, with a Holidays input, and a leap year is an error;
- design-day rules are checked and not used;
- the old branch is tagged
archive/archetype-data, then deleted; - a JSON Schema library is bundled to validate the JSON;
- the two atlas schemas and the Medium Office example may be committed;
- one stage for version 1.2.0;
- the build runs without supervision, with a stop line before the merge, the tag, the push, the docs export, and the release;
- the agent may close Rhino when it must.
The controller decided the realisation, which is Provisional (ADR-018). · Status: Provisional, pending the owner's reading; not merged. · Supersedes: D-122 (it exists only in the archive tag) · Supersedes in part: D-047, as to one load per type and basis (now per type, end use, and basis)
- Numbering. D-122 (the S10 atlas stage) and its ADR-016 exist only in the tag archive/archetype-data (cf88cee); the numbers stay unused in this log. The branch was never on origin. Part A of the stage archived it: an annotated local tag, the worktree removed, the branch deleted.
- Retired identities, never to be reused:
- the components Atlas Snapshot c231969e-a062-4f0b-a2b4-45ca60b7cc3e, Atlas Query ec2037f0-b14e-432b-a907-23a6e4566e94, and Atlas Program Preset 43b5b5c4-b180-4be5-94ce-74d982640fd9;
- the parameter Atlas Snapshot dbf8128f-fdd3-4f36-a941-89d849668161;
- the examples atlas-office-plate.gh and atlas-explorer.gh.
Nothing of the atlas code was on main except ProgramPreset.Source, which stays: the JSON preset fills it. From the archive only the ruleset expansion with its calendar and the strict JSON reading were ported, adapted to the 2.0.0 shape.
- How it was built. From the design note (Done), as D-111 sets out: test-first on feature/program-json in the worktree D:\worktrees\program-json, the library in parallel on a second branch, then reviewed by a second agent. The note's Build log holds the findings and the review.
- Realisation (controller, pending the owner's reading) — the details are in ADR-018, program-json.md, and the research documents:
- Model. LoadBasis.PerDwellingUnit with a dwelling count on every zone (1 per generated dwelling-unit zone, Σ fᵢ·Nᵢ after a merge); LoadDefinition.EndUse with a program holding one load per type, end use, and basis; HeatFractions (radiant, latent, lost, visible, return air) on lighting and equipment, with a non-zero value in a field the EnergyPlus object lacks an error; PeopleProperties (activity schedule, radiant fraction, sensible fraction or autocalculate); a Thermostat with optional heating and cooling sides; WaterTemperatures (target and inlet); ScheduleCalendar (a non-leap year and its holidays) with CalendarMismatch for programs on different calendars. The defaults are the values the IDF writer used before, so BEMGen's own presets are unchanged.
- Aggregation. The zone-merge rule value = Q/B with the dwelling count as a basis quantity; heat fractions weighted by the transferred magnitude; the activity weighted every hour by the transferred scheduled occupants; the people fractions by design occupants; the hot-water target and inlet every hour by the transferred scheduled flow; each thermostat side over the sources that have it; a merge whose heating exceeds its cooling is an error. Mix Programs inherits all of it (a weight stands for a share of dwellings as well as of floor area).
- Library. Lod.ProgramJson (a net7.0 library): strict parse, mode, schema (JsonSchema.Net 7.2.3, MIT; the program schema embedded byte for byte), the contract's checks, expansion to 8760 values, mapping to a preset. A rule starting on 29 February starts on 1 March in a non-leap year, and one ending on it ends on 28 February, each with a warning (the contract is silent; reported to the atlas).
- Components. Program JSON (abb0aeb9-5a9f-40ff-b3a1-bb125cfb675c, PJson, panel 1 Program, 14 inputs and 5 outputs) is new; Load, Schedule, Program Preset, and the default preset components gain appended inputs; Convert2BEM gains appended outputs 22 to 30. No existing GUID changed. The example examples/program-json-office.gh and the smoke spec program-json are new.
- Export. Heat fractions, per-dwelling levels (written as absolute levels), people properties, water temperatures, single-sided thermostats (SingleHeating and SingleCooling), and the run period from the calendar (2007 starting on a Monday without one).
- Validation. The conservation checks run per load component and for the building's dwelling count; PerDwellingLoads and Calendar are new checks; the aggregation records are required for the new derived fields.
- New tolerance. ToleranceSettings.AbsoluteFraction (1e-9), for sums of fractions (ADR-004 amended).
- Research documents (GLOBAL.md scientific rule 7): the research brief and program-presets.md record the new rules and defaults.
- Snapshots changed, each with its reason (none was updated to make a test pass):
- 28 text reports gain only lines for the new fields; a script confirmed that removing those lines restores the originals exactly;
- 6 IDF snapshots gain the calendar header line, and their RunPeriod year moves from 2018 to 2007, both years starting on a Monday;
- no IDF snapshot had a water use, so the predicted change of the water-use temperature fields did not occur in snapshots; the fields are covered by the export tests and the IDD check.
- Two regressions found in Rhino and fixed:
- Grasshopper's reader opens its IO dialog for every current parameter that has no saved chunk, so all 19 examples saved with 1.1.0 stalled on reopening. The components now set the appended parameters aside while the saved ones are read, which keeps saved values and wires; the examples spec reads each archive first and fails instead of stalling.
- Definitions saved before infiltration left the programs have trailing Infiltration inputs at the positions of the new Heating On and Cooling On, and were read into them. Trailing saved parameters whose name differs are no longer counted as saved.
- Review fixes (after the realisation above, 4d65af3 and 36b55a2):
- a mix with occupancy in more than one basis and a per-person load, or different people properties among the inputs with occupants, is the error MixedOccupancyBases (the virtual zone of a mix would weight them by a wrong split);
- equal setpoints merged over different sources stay exactly equal, so equal heating and cooling setpoints no longer cross by rounding; Thermostat and WaterTemperatures allow AbsoluteSchedule of slack, as the importer does;
- end uses are keys ignoring case, so an override End Use Lighting replaces the JSON's lighting.
- concurrent schema evaluations of the one shared schema instance could report an invalid document as valid (a flaky test that failed about one run in eight); evaluations are now serialised (fe03c32), and a test on 16 threads that failed 15 of 15 runs before passes 15 of 15.
- Checks (on fe03c32, the last code commit, and the documentation after it):
- scripts/verify.ps1 passed with 0 warnings: 614 core, 518 generator, 160 Lod.ProgramJson, 2698 integration, and 1219 export tests.
- The IDD check against the EnergyPlus 25.2.0 template IDD: 1073 files, 0 problems, with SingleHeating, SingleCooling, the water-use temperatures, and the people sensible fraction covered.
- All 16 scripts/rhino-smoke specs ran once each in Rhino 8.25.25314.11001 on .NET 8.0.31, every one done with no failed checks. program-json has 16 scenarios and 57 checks. The examples spec reopened 20 of 20 definitions, each with Validate True. Earlier in the build, one examples run stalled on the IO dialog (the first regression below) and Rhino was ended under the owner's permission.
- A trial docs export wrote version 1.2.0 with 60 screenshots and 0 description gaps.
- The fresh-clone gate: a clean clone of feature/program-json at 3d7e19c passed scripts/verify.ps1 with the same counts, the atlas fixtures byte-exact.
- Contract gaps reported to the atlas, not worked around:
1. 29 February in a non-leap model year is unspecified.
2. With a non-empty holiday list, defaulted schedules without a Hol rule fall to the prepended Default rule: the Medium Office's lighting and equipment are off on holidays while its occupancy keeps its Default value.
3. "Heating does not exceed cooling in the relevant active periods" is vague; BEMGen checks every hour in which both sides are on.
4. The annual schedule's allOf constrains rules, which annual schedules lack (harmless).
5. Design-day coverage is required by the checks, though a consumer without sizing never uses it.
- Open.
- For a person in Rhino 8: the canvas look of program-json-office.gh and of the Program JSON icon, a definition saved with 1.1.0 opened in the editor (the headless run proved that it reads without the IO dialog, not how it looks), and the install from the .yak.
- The owner's choices: the merge, the version and tag (v1.2.0), the push, the docs export, and the release. The stage stops before all of them (D-111). Only program-json-office.gh was rebuilt; the 19 other examples were reopened, not rebuilt.
- Pending the owner's reading: ADR-018 and the design note's controller choices (the end-use default names, heat fractions in a field EnergyPlus lacks as an error, the 29 February rule, the 64 MiB size bound, the dwelling counts after a split, the water temperatures 60 and 10 °C, a crossed merge as an error, the order of the Convert2BEM additions).
- Known limits:* shared services are not modelled; design-day profiles are not used (the IDF gives design days the Sunday profile); a schema error lists every failing branch, so one wrong rule can give several messages.
D-127 — Version 1.2.0: program JSON, released with a docs export¶
- Date: 2026-10-09 · Decided by: Cheng Xuan Li (asked for the merge, the tag
v1.2.0, and the release on the docs site on 2026-10-09) · Status: Accepted - Version. 1.2.0, tag
v1.2.0, a minor version as D-125 reasons: D-064 waives backward compatibility, and the stage changed no GUID. - What it merges.
feature/program-json(D-126, ADR-018), fast-forwarded intomainat79e3adf; it was 39 commits ahead of0d301c5and none behind. D-126 stays as written, with its "not merged"; this entry gives what holds formain. - Checks on
main: scripts/verify.ps1passed with 0 warnings before the merge: 614 core, 518 generator, 160Lod.ProgramJson, 2698 integration, and 1219 export tests, the counts D-126 gives.- Examples. All 20 example definitions were rebuilt with the 1.2.0 plugin at
79e3adf(BEMGEN_EXAMPLES_WRITE=1, Rhino 8.25.25314.11001), asexamples/README.mdasks at a release: every one reopened and solved with one Validate True, no warnings, errors, or exceptions. D-126 had rebuilt onlyprogram-json-office.gh. - Fresh-clone gate. A clean clone of
mainat8a64e25(the rebuilt examples) passedscripts/verify.ps1with the same counts. - What follows:
- the release on the docs repository's GitHub Releases (D-118), with the files of
scripts/package-release.ps1built from the tagged commit: the plugin zip, the.yak, the examples zip, andSHA256SUMS.txt; - a docs export of
v1.2.0carries Program JSON, the appended inputs and outputs, the new example, and the developer pages to the public site; the docs repository imports it and runs its ownsync-docs; - the release notes say that definitions saved with 1.1.0 open with their values and wires, and that definitions saved before 1.1.0 still lose their presets' infiltration values (D-124).
- Open: the items D-126 leaves to a person in Rhino 8 and to the owner's reading (ADR-018 and the design note's controller choices).
D-128 — A JSON editor window on Program JSON; the program JSON checksum is of the text with LF line endings¶
- Date: 2026-10-09 · Decided by: Cheng Xuan Li. The owner decided:
- a button strip at the bottom of Program JSON, which opens a window on a double-click, as an additional way to give the JSON input its text; every other input and behaviour of the component stays as it is;
- OK stores the text as the JSON input's own value (its persistent data), so a wire into JSON still wins and the text is saved with the definition;
- the window holds one text field, prefilled with the stored text, and OK and Cancel; OK checks the text and keeps the window open with the errors when it is invalid;
- when Grasshopper's CR LF line endings were found to change the checksum (below), to hash the text with LF line endings;
- build, review, and check, and stop before the merge, the version, the tag, the push, and the release.
The controller decided the realisation, which is Provisional pending the owner's reading (design note). · Status: Provisional; not merged · Amends: D-126 and ADR-018, as to the SHA-256 of the JSON text
- The window. ProgramJsonAttributes adds a strip of 22 pixels below the component, labelled Edit JSON… (Edit JSON… (wired) when JSON has a wire); a double-click inside it opens ProgramJsonDialog, an Eto dialog modal over the Grasshopper editor. OK runs ProgramJsonTextCheck.Check (Lod.ProgramJson): empty or white-space text clears the stored text, text the reader accepts is stored exactly as given, and text it rejects is listed and not stored. The check expands the schedules on the component's Year and Holidays when they give one valid calendar, else on 2007 without holidays; the overrides are checked when the component solves. ProgramJsonComponent.SetJsonText stores the text with one undo event; when JSON stores several texts, the window says that OK replaces them with one. No GUID, input, or output changed; the JSON input's description names the button. Details: program-json.md.
- The checksum finding. Grasshopper's archive turns every LF of stored text into CR LF, when a definition is saved and in undo records: the Medium Office's 45 262 characters came back as 46 852. The reader hashed the exact text, so the SHA-256 in the preset's Source changed after a save, an undo, or a paste with CR LF, which already held for text set with Set Text. Since this decision the reader hashes the UTF-8 text with every CR LF and lone CR made LF, so the checksum equals that of the atlas file, which uses LF, however the JSON arrives. JSON allows a raw line break only as whitespace between tokens, and CR and LF never occur inside a UTF-8 multibyte sequence, so the JSON's meaning is unchanged; other whitespace is hashed as it is. The fixtures are LF, so no recorded checksum changed.
- How it was built. As D-111 sets out, on feature/program-popup-window in the worktree D:\worktrees\program-popup-window, from main 3e46108: the note first; ProgramJsonTextCheck test-first (11 tests) and the checksum test-first (2 tests); the smoke spec program-json-editor written first and failing (0 ok, 6 failed); then the build; a review by a second agent, with nothing blocking and five fixes made (a null Year on a wire made the double-click throw; the window now says when JSON stores several texts; five more smoke checks of where the calendar comes from; the note's wording). The note's build log has the details.
- Checks:
- scripts/verify.ps1 passed with 0 warnings: 614 core, 518 generator, 173 Lod.ProgramJson (160 before), 1219 export, and 2698 integration tests.
- All 17 scripts/rhino-smoke specs ran in Rhino 8.25.25314.11001. 16 had no failed check, with every Validate True and the 20 examples reopening. program-json-editor had one failed check in that run, the null-Year case the review had found, because the run used the plugin built before the fix; run again on the fixed build, it passed 34 of 34 checks.
- The owner's test in Rhino 8 (2026-10-10): after OK the component still warned that JSON failed to collect data, because SetJsonText expired the component and not the input, which kept its old collected data; the headless checks had hidden it by solving with a full recompute. SetJsonText now expires the input; a new check of a live document failed before the fix, and program-json-editor passes 40 of 40.
- Open:
- For a person in Rhino 8: the strip's look in both canvas themes, the double-click, the window, paste of a whole file, the error list, Cancel, Esc, undo and redo from the editor, and a saved definition reopened (checklist).
- The owner's choices: the merge, the version (a patch, 1.2.1, or a minor, 1.3.0), the tag, the push, the docs export, and the release.
- On main. This entry was added to main's log in its numeric place when D-207 merged the branch (2026-10-30); its text is the branch's. What holds for main, the conflicts and the checksum's effect on the release 2.x fixtures, is in D-207.
D-129 — Experimental: Program JSON copied on an atlas page, on its own branch¶
- Date: 2026-10-10 · Decided by: Cheng Xuan Li (asked for the feature as an experiment, uncommitted at first; after testing it in Rhino 8, asked for it to be committed on
feature/program-popup-web, kept apart from the editor window of D-128) · Status: Experimental; not merged · Builds on: D-128 - What it does. A second strip on Program JSON, Get from Atlas…, above Edit JSON…. A double-click opens
ProgramJsonAtlasDialog, an Eto dialog with an address box, a web view of an Energy Archetype Atlas page (the example program definitionprogram-3e63db8b2252662cd9e70770first), a status line, Use clipboard now, and Cancel. On the page, Copy JSON then Copy with defaults puts the defaulted program JSON on the clipboard; the window takes it from there, gives it the editor window's check (ProgramJsonTextCheck), and on Store saves it withSetJsonTextand closes. Copy raw gives the raw export, which Program JSON does not read (D-126); the window says so and stays open. The page is never read by BEMGen: only what is on the clipboard is taken, and only after the check. The solve still reads no file and no network. - Clipboard detection.
ClipboardWatcherfollows the Windows clipboard sequence number, which grows with every write. The first version compared the clipboard's text and missed a copy of the same text again, which the page produces every time for one program, so a copy was missed whenever that JSON was already on the clipboard; copying the raw form first changed the text and hid the problem (found by the owner in Rhino 8, reproduced headless by writing the clipboard twice from another process). A write whose text cannot be read yet is not consumed. Use clipboard now checks the clipboard as it is. - Checked. The defaulted JSON of the example page, fetched as the page fetches it (its SHA-256 matching the page's descriptor) and serialised as the page copies it, passes the check and imports as the preset
Bakery_SuperMarketwith 13 assumptions. The owner tested the window in Rhino 8.program-json-editorchecks the strip's layout. The checksum in Source is that of the copied text, which the page re-serialises, not that of the atlas's.json.gzfile. - Open, before it could be merged: it brings web access back to the plugin, through a browser view the user drives, after D-126 retired the atlas fetch components and wrote that Program JSON reads no file or network; merging needs the owner's decision on that, the docs and ADR-018 brought in line, a fixed or configurable start page, and a person's checklist run. Clipboard detection uses a Windows API, with a fallback to comparing text elsewhere.
- On
main. This entry was added tomain's log in its numeric place when D-208 merged the branch (2026-10-30); its text is the branch's. D-208 replaces the clipboard with the page's address and settles three of its four open items (the web access, the docs and ADR-018, the start page); the person's checklist stays open there.
D-130 — A simulation-ready IDF, the experiment outputs, and an EnergyPlus runner¶
- Date: 2026-10-11 · Decided by: Cheng Xuan Li (scope, outputs, reference, ground rule, shading, convex split, the runner's rule change, version 1.3.0 without a tag); the realisation by the controller · Status: Accepted; the controller's realisation Provisional, pending the owner's reading
- Numbering. D-128 and D-129 are held by the kept, unmerged branches
feature/program-popup-windowandfeature/program-popup-web(the owner, 2026-10-11), so this stage takes D-130. Its ADR is ADR-019, the next free number after the archived ADR-016. - Supersedes in part:
- D-112: the EnergyPlus version 25.x, "EnergyPlus is not run", and "no weather, climates, or output selection";
- ADR-010: the fixed ground, the monthly meters, and non-convex surfaces left as they are;
- ADR-009: the empty gaps of a multiplied storey as part of the geometry effect.
- Refines D-016: simulation stays outside the pipeline, but the repository can run EnergyPlus. GLOBAL.md gains architecture rule 7.
- Decided (design note 2026-10-11-idf-out.md, "Decisions at a glance"):
- Convert2IDF targets EnergyPlus 26.2.
- A Weather input takes an
.epwpath:Site:Locationfrom its header, with its name and SHA-256 in the header and provenance. - The outputs are the research set of Experiment outputs: hourly meters behind heating, cooling, and the total load, written only when used; hourly zone mean air temperature; the summary tables; SQLite; and a case record in the header. That page integrates the owner's specification "Experiment Output Variables" and refines §14 of the research brief:
- the reference case is No Simplification + Stack with both heat-transfer options on;
- EUI is reported per conditioned and per total floor area;
- the total load is the four facility meters, gas equipment included;
- peak duration is counted in hours above the reference's percentiles;
- both load-duration-curve measures are computed;
- the building temperature is reported over all zones and over conditioned zones;
- the metrics are computed in
Lod.Simulation.
- Ground: "indoor minus 2 °C". The building's floor-area-weighted monthly setpoint minus 2 °C, and 18 °C without a conditioned zone. The F-factor method was preferred but needs a sourced F-factor (D-023), and ISO 13370 was judged more than needed.
- Every non-convex outdoor surface is split into convex pieces.
- The storeys a multiplied storey stands for cast shadows, through shading surfaces at their walls, generated at converter level by Convert2IDF and Convert2BEM (the research brief's Z5 and V4 changed with it).
- EnergyPlus runs only outside the pipeline:
Lod.Simulationwith opt-in tests (BEMGEN_ENERGYPLUS) and the C# toolscripts/eplus-check; a Grasshopper run component is deferred. - Finding F1 (slabs between × 1 storeys stay adiabatic under Stacked Floor Zone Multiplier) stays a finding.
- The version is 1.3.0, with no tag in this stage.
- Built on
feature/idf-out(frommain3e46108), as D-111 sets out: - four parts by separate agents (writer, shading, runner, end-to-end set), the first three in parallel worktrees, rebased one after another;
- a review by a second agent, whose eight findings were fixed or merged;
- the headless Rhino check;
- the build log in the design note.
- Components and interfaces:
- Convert2IDF gains the input Weather, and Convert2BEM the output Shading. Both are appended, both keep definitions saved with 1.2.0 readable, and no GUID changed.
- A new library
Lod.Simulation(netstandard2.0, depending only onLod.Core) and its test project. - New diagnostics, among them
InvalidWeatherFile,NoWeatherFile,GroundTemperatureDefault,InfiltrationNotWritten,WindowBlocksConvexSplit, and the runner's codes. - Checks:
scripts/verify.ps1passed with 0 warnings: 629 core, 518 generator, 160 program JSON, 1382 export, 2726 integration, and 136 simulation tests, with the 5 opt-in EnergyPlus tests skipped.- With
BEMGEN_ENERGYPLUS=D:\EnergyPlusV26-2-0, the 144 simulation tests passed: 110 EnergyPlus cases, each completed with 0 severe errors and only the expected warning, with zones, areas, end uses, internal loads, and the case record checked. scripts/eplus-checkpassed all 1080 IDFs the export tests write.scripts/idd-checkagainst the EnergyPlus 26.2.0 IDD: 1080 files, 0 problems.- All 16 rhino-smoke specs passed, and the 20 examples reopened and solved.
- The fresh-clone gate: a clean clone of
feature/idf-outate84ab84passedscripts/verify.ps1with the same counts and 0 warnings. The tagv1.3.0is on the commit that records this gate. - Findings:
- ADR-010's two items "to verify on the first EnergyPlus run" hold: EnergyPlus uses the stated zone floor area, and the window construction named like its glazing runs.
- The total load's meters are complete: the end uses sum to it every hour.
- The floor aggregators conserve area, volume, and hourly internal loads through EnergyPlus.
- With shading, the enclosed court under the multiplier is within +0.09 % heating and +0.58 % cooling of the reference, against −2.9 % and +4.7 % without.
- The owner's answers to the build (2026-10-11):
- The ground rule stays as it is. The mapping from zone setpoints to ground temperature is fixed, and that is all that is wanted; the values may differ between zonings, which is one of the differences the study characterises.
- Minimum warm-up days stay 6.
- The F-factor method, F1, and the run component are set aside for now.
- The tag
v1.3.0is set locally. The branch is not merged or pushed while the owner reviews it by hand. - Open for a person (D-058): the Weather input and Shading output in Rhino, a 1.2.0 definition in the editor, and the toolbar look.
D-131 — Version 1.3.0: the simulation-ready IDF, released with a docs export¶
- Date: 2026-10-12 · Decided by: Cheng Xuan Li (reviewed the branch by hand, then asked for the merge, the push, the docs export, and the release on 2026-10-12) · Status: Accepted
- Version. 1.3.0, tag
v1.3.0, a minor version as D-125 reasons: D-064 waives backward compatibility, and the stage changed no GUID. The tag was first set on2827656at the owner's request (D-130), before the merge, and was moved to this entry's commit at the release, as forv1.2.0. It had never been pushed. - What it merges.
feature/idf-out(D-130, ADR-019), fast-forwarded intomainat2827656; it was 48 commits ahead of3e46108and none behind. D-130 stays as written; this entry gives what holds formain. - Checks on
main: - Examples. All 20 example definitions were rebuilt with the 1.3.0 plugin at
2827656(BEMGEN_EXAMPLES_WRITE=1), asexamples/README.mdasks at a release (035feff). Every one reopened and solved with one Validate True and no warnings, errors, or exceptions, in the rebuild and in a separate reopen. - Fresh-clone gate. A clean clone of
mainat035feffpassedscripts/verify.ps1with 0 warnings: 629 core, 518 generator, 160 program JSON, 1382 export, 2726 integration, and 136 simulation tests, with the 5 opt-in EnergyPlus tests skipped. These are the counts D-130 gives. - What follows:
- the release on the docs repository's GitHub Releases (D-118), with the files of
scripts/package-release.ps1built from the tagged commit: the plugin zip, the.yak, the examples zip, andSHA256SUMS.txt; - a docs export of
v1.3.0, which carries the Weather input, the Shading output, the new developer pages (experiment outputs, the EnergyPlus runner, ADR-019), and the rebuilt examples to the public site; the docs repository imports it and runs its ownsync-docs; - the release notes say that definitions saved with 1.2.0 open with their values and wires, and that IDFs now target EnergyPlus 26.2.
- Open (from D-130, unchanged):
- The e2e set does not check that
eplusout.sqlis written. envelope-presets.mddoes not yet say that the F-factor method waits for a sourced F value.experiment-outputs.mddoes not mention the shading of unmodelled storeys.- Set aside by the owner: the F-factor method, F1, and the run component.
D-132 — The batch runner IdfBatchRunner, with a central DuckDB database per run¶
- Date: 2026-10-12 · Decided by: Cheng Xuan Li (a C# command-line tool instead of the Grasshopper run component; hourly results by end use and by fuel; a long table in DuckDB; no case run without a reference; a central database of the useful results with the hourly loads by fuel and by end use, the fuel table stored and never a view over the end uses; one folder per run holding its central database, one folder per case inside it;
--rootorBEMGEN_RESULTS_ROOT;--resume;eplus-checkkeeps its name; no release); the realisation by the controller · Status: Accepted; the controller's realisation Provisional, pending the owner's reading - Refines D-016 and D-130: running batches of cases and storing their results is now in the repository, in a development tool outside
src/that is built and tested with the solution but not packaged or released. Analysis stays outside the repository. The Grasshopper run component that ADR-019 deferred stays deferred. No research definition changes: experiment outputs §1 to §8 are computed by the existingExperimentMetrics. - Design note: 2026-10-12-idf-batch-runner.md. No ADR: no research rule, pipeline type, component, or GUID changes.
- Decided:
scripts/IdfBatchRunner(net8.0, assemblyIdfBatchRunner) runs a set of Convert2IDF files. It groups them by the building, envelope, and weather of their case records. Each group's reference is the case with the zoning linesimplifier NoSimplification; aggregator Stack; IWHT=true FHT=true. References run first, then the variants of each reference that succeeded. A case without a reference is not run, nor is one with an incomplete record, an unresolved or mismatched weather file, a duplicate, or an ambiguous reference.- Each run gets
<root>/run_<yyyyMMdd-HHmmss>/holdingcentral.duckdb,results.duckdb, andbatch.json, and a folder per case with EnergyPlus's files,bemgen-run.json,bemgen-hourly.csv(long), andbemgen-summary.csv. The root is--root, elseBEMGEN_RESULTS_ROOT, and never inside the repository. - The central database holds, per successful case (completed, verdict passed, end uses closed), the case record split into its parts, both floor areas, the annual energy of every end use by fuel, every fuel, and the total, every metric of experiment outputs §2 to §6 with its flag, and the hourly loads by fuel (EnergyPlus's four facility meters) and by end use (the six end-use meters), in W.
mergecombines the central files of runs; a case is the same when its case ID and EnergyPlus build are. Schema version 1. - The batch database holds every case given, run or not, with its status, and the 17 hourly series of each completed case as a long table: six end uses by fuel, four fuels, the total, the two sensible diagnostics, and the four building temperatures. It and the case files are expected to be discarded later.
DuckDB.NET.Data.Full1.5.5 (MIT, with the native DuckDB library) is referenced by the tool and its tests only.Lod.Simulationstaysnetstandard2.0and BCL-only and gains the end-use-by-fuel catalogue, the hourly closure check, the run period's timestamps, the batch plan, the zoning line's parts, and the input expansion now shared witheplus-check.- Size, measured on 80 real cases of the 1.3.0 end-to-end set: the central database takes 361 kB per case with 7 of its 10 hourly series non-zero, up to about 0.5 MB with all ten; the batch database 1.27 MB per case.
- Built on
feature/idf-batch-runner(frommain9dcc5d5), as D-111 sets out: the design note, a test-first build, a review by a second agent whose eleven points were fixed or recorded (the build log of the design note), andscripts/verify.ps1(629 core, 518 generator, 160 program JSON, 1382 export, 2726 integration, 176 simulation, and 41 batch-runner tests; the 6 opt-in EnergyPlus tests skipped; 0 warnings). The opt-in test passed on EnergyPlus 26.2.0 build4bd7a1f26f: the linear plan's reference and two variants ran, and the orphan did not. A run of the tool on the 110 IDFs of the 1.3.0 end-to-end set ran 80 cases, all of which succeeded. It did not run the 30 without a reference: the Medium Office set, written with the default heat-transfer options, and the overhanging stair-bay bar. Python'sduckdb1.5.5 reads both databases. No merge, tag, push, or release.
D-133 — Test suites: sites, test cases, and a generator of IDFs from a test-suite spec¶
- Date: 2026-10-12 · Decided by: Cheng Xuan Li · Status: Accepted (the terms and the answers below); the design note's realisation Draft, pending the owner's approval
- Terms, replacing "case" everywhere, in code, databases, the IDF header, and the docs:
- a site is one building in one zoning, one IDF;
- zoning is the simplifier with its options, the stacker, and the two heat-transfer options;
- a test case is one reference site plus at least one test site, differing only in zoning;
- a test suite is the test cases expanded from one YAML test-suite spec, each test case independent of the others.
The reference stays No Simplification, Stack, both heat-transfer options on (D-130); it is always added and never a test site. The storey count is part of a test case, not of its zoning.
- Refines D-132: the batch runner adopts these terms before it merges (D-064 waives backward compatibility), reads the suite's manifest, and keeps only central.duckdb, batch.json, and the IDFs when the suite asks it to discard EnergyPlus results.
- Decided:
1. A new C# tool under scripts/ writes IDFs only, from a versioned, schema-checked spec. It replaces the Grasshopper workflow for suites.
2. Combination rule. A list is an axis, combined freely; a map in a list is a fixed bundle; range and zip are allowed. The axes are combined in full factorial, with no sampling, and the plan with its counts is shown before anything is written.
3. One plan generator per suite, with the same simplifier on every floor. Buildings with differing floors, SteppedBand among them, wait for a separate spec schema.
4. Programs. Programs come only from program JSON, local or by URL. A slot may take a weighted mix. A program's climate zones will come from its metadata; a documented placeholder in the spec stands in until then.
5. Schedules. A program setting, a schedule-generator setting, and a seed give a program with generated schedules. Draws are per zone; a draw is part of a test case, shared by all its sites; seeded sampling exists only inside the schedule generator.
6. One calendar per suite, default year 2007. Constructions will be envelope-like files; vintage and other labels are tags only. HVAC stays ideal loads.
7. Weather files live in a folder with a registry by climate zone and location.
8. Test cases and sites are named by short IDs. One table holds every test case as a row, with the generation parameters as structured columns. The scale is 100 000 sites or more, queried by grouping on any parameter.
- Design note: 2026-10-12-test-suite-generator.md; ADR-020 (the per-zone schedule draw as a pipeline step, and the manifest-based test cases) at the build.
D-134 — Test suites: the owner's second answers; the schedule generator deferred to a recorded requirement¶
- Date: 2026-10-12 · Decided by: Cheng Xuan Li · Status: Accepted
- Refines D-133, items 5 and 6. Items 5 and 6 of D-133 stand as requirements; the generator is not designed in the test-suite stage. Programs are used with the schedules their program JSON defines, and a test-suite spec has no schedule section or seed.
- The schedule generator's requirement is recorded in schedule-generator-requirement.md.
- It runs once per test case (first recorded here as "once per site", a misreading of the owner's answer, corrected the same day before the branch merged).
- It generates schedules per zone, with shifted seeds across zones, so that units of the same type differ.
- It shares its draws across a test case's sites.
- Every site of a test case shares the same schedule sources: a merged zone's schedules aggregate its source zones' draws, so loads stay conserved (GLOBAL scientific rules 1 and 4), though a simplified site's actual schedules may differ.
- Decided for the test-suite generator (design note):
- The IDF header's
! Case …lines become! Site …. - The window-to-wall ratio is a suite axis.
- Labels attach to bundles; a label axis is an error.
- Storeys are explicit floor entries or a count N, meaning ground, N − 2 typical, and top.
- Constructions are the built-in example envelope only, until their schema exists.
max_sitesdefaults to 20 000.- The tool is
scripts/TestSuiteGenerator.
D-135 — The test-suite generator built, the runner on declared test cases, and the case → site rename¶
- Date: 2026-10-12 · Decided by: the owner's decisions in D-133 and D-134; the realisation by the controller · Status: Accepted; the controller's realisation Provisional, pending the owner's reading
- What it builds (design note, ADR-020), on
feature/test-suite-generator, fromfeature/idf-batch-runner; neither branch is merged: - The rename. "Case" becomes "site" and "group" becomes "test case" across
Lod.Simulation,Lod.Export(the IDF header's! Site …lines; 6 snapshots), the batch runner and its databases (central schema version 2), the smoke spec, and the current docs. Past records keep their words. scripts/TestSuiteGeneratorwrites a suite's IDFs from a YAML spec checked by an embedded schema: 17 generators by name, axes in full factorial with bundles,range, andzip, program JSON by file or pinned URL, mixes, the climate-zone placeholder, andmax_sites. A suite folder holdssuite.json,suite.duckdb(one row per test case with typed parameter columns, a long parameter table, and the sites), andidf/<test case>/<site code>.idf.- The runner's
--suitetakes membership and references fromsuite.json(BatchPlan.CreateForTestCases). It refuses a site changed since it was written (ModifiedSinceWritten), copies the suite's tables into the central database with the viewsite_results, merges them across runs, and honoursdiscard_ep_results. - Realisation choices (ADR-020):
- test case IDs of 16 hexadecimal digits, not the note's 10, and site codes of 8;
- labels left out of the test case ID;
- the site ID shared by the generator and the runner;
- a test case that cannot generate is
GenerationFailed. - Checks: scripts/verify.ps1 passed with 0 warnings (629 core, 518 generator, 160 program JSON, 1382 export, 2726 integration, 180 simulation, 89 test-suite generator, and 52 batch-runner tests). Both opt-in EnergyPlus tests passed on 26.2.0 build
4bd7a1f26f, among them a generated suite of 8 sites run through the runner. A review by a second agent found no blocking bug. Its eight points are fixed, with tests: constructions by content in the ID, duplicate references, one-program mixes, programs fetched and imported before writing, the zoning columns insite_results, failed sites kept on discard, the holidays' order, and long ranges. - Open:
- buildings whose floors differ, among them SteppedBand;
- the schedule generator (requirement);
- constructions files and HVAC systems;
- the leftover worktree
D:\BEMGen\.claude\worktrees\suite-rename(branchfeature/suite-rename), whose content is all in this branch. The owner allowed its removal (2026-10-12), and it was already gone. - Not merged (the owner, 2026-10-12):
feature/test-suite-generatorandfeature/idf-batch-runnerstay unmerged until the owner says otherwise.
D-136 — The batch runner and test suites merged into main, without a release¶
- Date: 2026-10-12 · Decided by: Cheng Xuan Li (asked for the merge; no release; work continues on
feature/test-suite-generator) · Status: Accepted - What it merges.
feature/test-suite-generator, fast-forwarded intomain. It holdsfeature/idf-batch-runner(D-132) and the test-suite stage (D-133 to D-135, ADR-020): 29 commits ahead of9dcc5d5(v1.3.0), this record included, and none behind. D-132 to D-135 stay as written; this entry supersedes their "not merged" lines. - No release. The version stays 1.3.0, with no tag, no docs export, and no release files; the public site follows at the next tagged version. Nothing is pushed: a push needs the owner's yes.
- Check before the merge:
scripts/verify.ps1on the merged commit (see the roadmap's progress log). - What follows: work continues on
feature/test-suite-generator(the owner, 2026-10-12), not on a new branch frommain. The pilot suite and the deferred designs of D-133 and D-134 come next, at the owner's direction.
D-137 — Constructions from the atlas's construction JSON: scope, ground, mass residual, fixtures¶
- Date: 2026-10-15 · Decided by: Cheng Xuan Li · Status: Accepted · Superseded in part by D-145 (the ground rule for construction sets)
- Scope. A stage on
feature/construction-json(frommainb64805c) imports the atlas's construction JSON (unified first, then detailed) and connects it to Convert2IDF through a new Construction JSON component and an appended Construction input. Program JSON v3, test-suite wiring, and Convert2BEM are not part of it. Design note: 2026-10-15-construction-json (Draft). - Ground. The atlas's constructions have no ground floor; ground surfaces take the Example envelope's ground floor, with no new input, and the ground-temperature rule of D-130 is unchanged.
- Mass residual. When the geometry's explicit capacity exceeds the atlas's reference budget, no mass is added for that family, explicit mass is kept, and a warning records the overshoot.
- Fixtures. The limited packet the atlas prepares (two construction schemas and the pilot detailed/unified pairs, with manifest and notices) may be committed byte for byte. Nothing else from the atlas.
- Branch (the controller's choice, Provisional). This stage has its own branch rather than
feature/test-suite-generator, which D-136 keeps for the pilot suite.
D-138 — Roles a construction JSON lacks but the geometry needs take the Example envelope's construction¶
- Date: 2026-10-15 · Decided by: Cheng Xuan Li · Status: Accepted
- Decision. When the geometry needs a role whose component the construction JSON marks
not_applicable, the surface takes the Example envelope's construction for that role. This is the same rule as for the ground (D-137). Each such role is recorded in provenance and raised as a warning. The handover's rule, that a role the set lacks blocks conversion, applies only to components markedblocked. - Why. The atlas's residential pair comes from a single-family detached house, so it has no interior walls, interior floors, or exposed floors. Every BEMGen residential plan has walls and slabs between zones, so blocking would make the residential pair unusable.
- Note: 2026-10-15-construction-json, build log "Part A, the packet, and what it changes", item 6.
D-139 — The construction JSON stage closed on the unified path, not merged¶
- Date: 2026-10-15 · Decided by: Cheng Xuan Li (close the stage on the unified path first; Part E later); the realisation by the controller · Status: Accepted; the controller's realisation Provisional, pending the owner's reading
- What is built. On
feature/construction-json, Parts A to D of the design note: - Part A: the pinned atlas packet,
bemgen-construction-7693e08-v1. - Part B: typed opaque layers (massive, massless, air gap) with surface finishes, and windows of simple or layered glazing, in the envelope and the IDF.
- Part C:
Lod.ConstructionJson, which imports the unifiedbuilding_level_equivalent1.1.0, together with construction sets and the mass ledger inLod.Core, and Convert2IDF taking a set. - Part D: Construction JSON (GUID
b2802511-fee7-49ab-bdcb-9329ce293461), the parameter Construction Set (ef90ede8-f0ff-4495-822b-11c5e61a6fe5), and Convert2IDF's appended Construction input.
The rationale is in ADR-021.
- Checks.
- scripts/verify.ps1 passed.
- All 17 headless Rhino specs passed, the new construction-json among them.
- The 168 construction IDFs had 0 IDD problems against 26.2 and all completed in EnergyPlus 26.2.
- Each part was reviewed by a second agent and its findings fixed.
- The fresh-clone gate passed (see the roadmap's progress log).
- Controller choices recorded in the note's build log:
- a zero budget is information;
- the finish applies to both faces of a layer;
- InteriorCeiling is unused;
- removed slabs count once in the ledger;
- the IDD check allows window names in SurfaceProperty:IncidentSolarMultiplier, with evidence;
- the strict reader and the schema evaluation of Lod.ProgramJson are public and shared;
- Construction JSON sits in panel 1 Program.
- Not done. Not merged, tagged, pushed, or released, so the public site still describes 1.3.0. Part E (detailed documents) is open. The open findings are listed in ADR-021.
D-140 — Residential construction sets: packet v2, opt-in Example fill, attic roofs, adiabatic source floors¶
- Date: 2026-10-15 · Decided by: Cheng Xuan Li · Status: Accepted. The detection rules in the last bullet are the controller's and are Provisional, pending the owner's reading. Superseded in part by D-142 (the residential rules, removed in code by D-143)
- Fixtures. The atlas packet
bemgen-construction-7693e08-v2(manifest SHA-2565b8dd4cf…) may be committed whole. It replaces v1, whose files it keeps byte for byte, and adds two residential pairs,residential-multistorey-garage-4Aandresidential-raised-exposed-floor-1A. - Example fill is opt-in. Supersedes D-138's default.
- A role or window that the construction set lacks but the building needs is an actionable error by default.
- Convert2IDF gains Fill From Example, default false. When true, such roles take the Example envelope's construction with a warning, as D-138 did.
- The ground stays the Example's, with a remark (D-137).
- Attic roofs. Where the source's roof sits over a vented attic, the roof of conditioned zones takes the set's
InteriorCeilingrecipe. This is an approximation that leaves out the attic air and the roof deck, recorded in provenance with a warning. A modelled attic zone is not planned. - Adiabatic source floors. A residential
InteriorFloorrecipe comes from floors that are adiabatic in the source. With floor heat transfer on, it is still written as a conductive slab, with a warning. - Order. C2 (the unified fixes) and then E1 (reading the detailed documents) run without a stop in between. E2, the detailed assignment, comes later.
- Detection (controller, Provisional). The unified document carries no space types, so:
- an attic roof is read from a
whole_dwellingset whoseInteriorCeilingis ready; - a residential interior wall is read as a wall to unconditioned space (a garage), and a residential interior floor as one from adiabatic source floors, both from the
whole_dwellingscope; - each is recorded as such in provenance. An explicit flag from the atlas would replace these rules.
D-141 — C2 and E1 closed: residential construction sets converted, detailed documents read, not merged¶
- Date: 2026-10-15 · Decided by: the owner's decisions in D-140; the realisation by the controller · Status: Accepted; the controller's realisation Provisional, pending the owner's reading. Superseded in part by D-142 (the residential rules and E2)
- What is built on
feature/construction-json: - packet v2 pinned (five pairs);
- Fill From Example opt-in on Convert2IDF (input 10, default false), with the actionable error
ConstructionMissing; - the residential rules: an attic ceiling as the roof (an approximation), the garage-wall caveat, and the warning on interior floors that are adiabatic in the source, given only when a floor is written conductive;
- reading detailed
construction_setdocuments intoDetailedConstructionDocument, all rules, conditions, and geometry instructions kept, with unsupported assemblies as warnings, and the set refused withConstructionAssignmentUnavailable.
The rationale is in ADR-022.
- Checks.
- scripts/verify.ps1 passed.
- All 17 headless Rhino specs passed; construction-json has 8 scenarios and 22 checks.
- The 284 construction IDFs of C2 had 0 IDD problems and passed in EnergyPlus 26.2.
- All ten packet documents parse.
- C2 and E1 were each reviewed by a second agent and every finding fixed.
- The fresh-clone gate passed (roadmap progress log).
- Wording. D-140's "adiabatic source floors" reads as "mostly adiabatic": the 4A interior floor blends an adiabatic floor between storeys (about 92.5%) with the floor to the garage.
- Not done. Not merged, tagged, pushed, or released. E2, binding detailed conditions to zones, needs its own design note. Open: whether EnergyPlus applies the incident solar multiplier with a blank schedule has not been compared numerically; the AirGap and Gas property names are assumed.
D-142 — Construction JSON scope: unified building-scope sets only; detailed binding (E2) dropped; single-dwelling sets rejected¶
- Date: 2026-10-16 · Decided by: Cheng Xuan Li (answers to the E2 note's questions) · Status: Accepted. Supersedes the parts of D-140 and D-141 named here.
- Unified only. BEMGen converts only unified (
building_level_equivalent) sets, which are space-type agnostic: one assembly per semantic for the whole building. Binding detailed rules to zones (E2) is dropped, and its draft note is superseded. Detailed documents stay readable (E1) for evidence and are never converted. - No single-dwelling sets. Sets with the scope
whole_dwelling(the ResStock single-family fixtures) are rejected. BEMGen's residential buildings are apartments, which use building-scope apartment sets, not single-dwelling ones. This supersedes D-140's residential rules (the attic ceiling as the roof, the garage-wall caveat, the adiabatic-source floor warning): with whole-dwelling sets rejected, nothing reaches them. - Unconditioned is unconditioned. No garage or attic category is modelled.
- Windows. BEMGen windows are fixed. A unified set's window is already resolved by its source's window areas: in the packet, it is the fixed recipe at weight 1.0. So operability raises no question on the unified path.
- Kept from D-140: Fill From Example stays opt-in, for a role a building-scope set marks not applicable. The ground stays the Example's (D-137). The branch stays
feature/construction-json.
D-143 — Construction packet v3, D-142 in code, removed slabs written once per face, and the solar multiplier checked¶
- Date: 2026-10-16 · Decided by: Cheng Xuan Li (commit packet v3; "Fix: half-thickness"; an apartment set has the medium office's schema) · Status: Accepted. Refines how D-031 is written in an IDF; D-031's rule (merged slabs become internal mass, keeping their storage and exchange area) stands. Superseded in part by D-145 (the packet pin).
- Packet v3 pinned.
bemgen-construction-7693e08-v3, manifest SHA-25657a71b7c675901d8e36201f6301569c0e626cbd8103fa76550de60ceca92b4f0, seven pairs: v2's five, byte for byte, plusmidrise-apartment-2019-3B(scopebuilding) andresidential-apartment-unit-1990s-4A(scopewhole_dwelling). - The apartment set is the residential case. The midrise apartment's unified set has the medium office's shape: every role BEMGen uses is ready, its
InteriorWindowis not applicable withsource_reference_zero_exposure(valid, and not listed as unused), and it has budgets, furnishings, and a floor-distributed mass. It converts the linear plan on every zoning without Fill From Example, and the mass ledger holds on every zoning. - D-142 in code.
- A unified set whose scope is not
buildingis refused withConstructionJsonScopeNotSupported. The packet's four single-dwelling sets, the apartment unit among them, are refused; their detailed documents are still read as evidence. - The residential rules of D-140 are removed with their codes: the attic ceiling as the roof, the garage-wall caveat, the adiabatic-source floor warning,
ConstructionApproximation, andAdiabaticSourceConducted. - Removed slabs (the fix).
- EnergyPlus
InternalMassis one-sided, with an adiabatic back. Writing a removed slab as its full construction on slab area × exposed faces gave each face the whole slab's resistance and stored twice the slab's heat capacity. The mass ledger (D-139) counts the slab's capacity once, so the IDF disagreed with the ledger wherever storeys were merged into one zone. - Now each exposed face is a slice of the slab,
Construction.Sliced(faces): a two-face slab is written on twice its area with"{name} Half", whose massive layers have half their thickness and whose massless layers and air gaps have half their resistance. The IDF then holds the slab's capacity once, and each face sees the resistance to the slab's mid-plane. - This is exact for a symmetric slab. For an asymmetric one, such as the Example interior floor (gypsum board, concrete, screed), each half keeps every layer in order, so this is an approximation.
- A slice whose massless layer would fall below EnergyPlus's minimum resistance (0.001 m²·K/W) is the error
EnvelopeValue, naming the layer. - Two IDF snapshots changed (single-zone aggregators). Convert2BEM's Internal Mass output is unchanged: it is the exposed area, and its construction is the converter's (D-023).
- Solar multiplier checked. The 5A set's IDF on the linear plan (No Simplification, Stack Floors) was run for a year in EnergyPlus 26.2 in Golden, CO (TMY3), with and without its 40
SurfaceProperty:IncidentSolarMultiplierobjects. The windows' transmitted solar (Surface Window Transmitted Solar Radiation Energy, summed) was in the ratio 0.8276, the multiplier, with a blank schedule. This closes the open question of D-141 and ADR-021. An end-to-end test now checks that every window gets the set's multiplier. - Deferred by the owner: Program JSON v3, the test-suite generator's construction input, and Convert2BEM with construction sets.
- Not done. Not merged, tagged, pushed, or released.
D-144 — Version 1.4.0: construction sets from the atlas's construction JSON, released with a docs export¶
- Date: 2026-10-16 · Decided by: Cheng Xuan Li (tested the branch in Grasshopper and Rhino, then asked for the merge, the push, the tag, the release with the plugin files, and the docs sync on 2026-10-16) · Status: Accepted
- Version. 1.4.0, tag
v1.4.0, a minor version as D-125 reasons: D-064 waives backward compatibility, and the stage changed no GUID. It adds one component and one parameter (Construction JSON, Construction Set) and two inputs appended to Convert2IDF (Construction, Fill From Example). - What it merges.
feature/construction-json(D-137 to D-143, ADR-021, ADR-022), fast-forwarded intomainat8e03abb. It was 61 commits ahead ofb64805cand none behind.mainalso carries the batch runner and the test-suite generator (D-132 to D-136); they are tools, not part of the plugin, and are first tagged here. - Checks on
main: - Examples. All 20 example definitions were rebuilt with the 1.4.0 plugin (
BEMGEN_EXAMPLES_WRITE=1,fd32a54). Every one reopened and solved with one Validate True and no warnings, errors, or exceptions, both in the rebuild and in a separate reopen. - Fresh-clone gate. A clean clone of
mainatfd32a54(D:\BEMGen-gate\v1.4.0) passedscripts/verify.ps1with 0 warnings. The counts are 681 core, 518 generator, 160 program JSON, 73 construction JSON, 1739 export, 2726 integration, 180 simulation, 52 batch-runner, and 89 test-suite tests; 7 opt-in EnergyPlus tests were skipped. - What follows:
- the release on the docs repository's GitHub Releases (D-118), with the files of
scripts/package-release.ps1built from the tagged commit: the plugin zip, the.yak, the examples zip, andSHA256SUMS.txt; - a docs export of
v1.4.0, imported by the docs repository, which runs its ownsync-docs. The owner asked for a minimal change to the hand-written pages: the new component and inputs, and the IDF change. The atlas's data and contracts are not described on the public site. - Release notes.
- Definitions saved with 1.3.0 open as they were, with Construction unset and Fill From Example false.
- IDF files of zonings that merge storeys into one zone change: each slab inside the zone is written as twice its area of half its thickness, so its heat capacity counts once.
D-145 — Construction packet v7: native ground methods, v3 retired, D-142 kept, ground temperatures from the source's rules¶
- Date: 2026-10-18 · Decided by: Cheng Xuan Li (answers in the session that took the atlas's v7 handover) · Status: Accepted. Supersedes D-143's packet pin and D-137's ground rule for construction sets; D-142 stands; D-130's ground rule stands for buildings converted without a construction set.
- Packet v7 replaces v3.
bemgen-construction-foundation-v7(manifest SHA-256 verified on 2026-10-18; it names v6 as its parent) is pinned whole, byte for byte, in place of v3: 19 detailed 3.0.0 / unified 2.0.0 pairs, the four schemas, the notices, and the licence. v3's seven pairs are all in v7 (same source contexts, five renamedretained-*), with the same ordinary assemblies and components; v7 adds the foundation roles,foundation_ground, materials, evidence, and the shared mass contract 1.0.0. The unified 1.1.0 and detailed 2.0.0 readers are retired, with no backward compatibility (D-064). - D-142 kept. Convert2IDF converts only unified sets of scope
building. Detailed documents and single-dwelling sets are imported and their foundations instantiated from declared geometry (the handover's R1–R6, K1–K6, I1–I6, on both representations), but not converted. Kiva therefore runs only in importer-generated EnergyPlus smoke cases outside the pipeline. - Native ground in Convert2IDF. For a v7 set, a ground floor takes the set's own
GroundFloormethod instead of the Example's ground floor (D-137): - F-factor:
Construction:FfactorGroundFloorwith the set's F, the surface's actual area, and its exposed perimeter (shared zone edges cancelled, zero allowed), onGroundFCfactorMethod; - layered ground: the set's layers on
Ground. - F/C ground temperatures.
Site:GroundTemperature:FCfactorMethodis the weather file's monthly mean dry-bulb, delayed three months and rounded to 0.1 °C. This is OpenStudio Standards' method (the STAT file's monthly dry-bulb,rotate(-3)), computed from the EPW's hours because BEMGen takes no STAT file. A v7 set with F- or C-factor surfaces and no weather file is refused. - Layered-ground temperatures.
Site:GroundTemperature:BuildingSurfaceis 18 °C in every month for a v7 set. That is EnergyPlus's default, which the source's prototypes ran with: OpenStudio Standards writes no BuildingSurface object when it finds the STAT file. Provenance records it as the source's condition. - Why. The atlas asks that native methods not take the "indoor minus 2 °C" rule. EPW ground temperatures are absent from many AMY files (16 of 157 local EPWs, ComStock's and ResStock's AMY files among them) and, where present, are undisturbed soil values that EnergyPlus advises against under a building.
D-146 — Construction foundation v7 built: packet v7 read, foundations instantiated, native ground in Convert2IDF¶
- Date: 2026-10-18 · Decided by: controller (the owner's decisions in D-145) · Status: Accepted; the controller's realisation Provisional, pending the owner's reading. Realises D-145; rationale and alternatives in ADR-023; design note 2026-10-18-construction-foundation-v7.md (a build log for each part).
- Part A, packet v7 and the readers.
- Packet v7 is pinned whole in place of v3 (61 files, every file's SHA-256 and size checked in
PacketTests); the four schemas are embedded byte for byte and evaluated without the network. ConstructionJsonImporter.Readreads either representation (unified 2.0.0, detailed 3.0.0) of any scope with full closure;ImportisReadplus D-142's scope refusal.- The foundation data are plain types in
Lod.Core.Foundations(FoundationData, ground methods and model parameters,DimensionOperand), carried onConstructionSet.Foundation; the shared readers areFoundationReader,MaterialReader, andMassContract. - Part B, instantiation (
Lod.Core.Foundations.Instantiation).FoundationInstantiator.Instantiate(FoundationData, FoundationInstantiationRequest)returns resolved surfaces and oneKivaFoundationInstanceper group;KivaInitialization.Deriveis the source'sresstock_source_start_month_1.0.0;FoundationModelScope.MayShareandCombinegive the scoped reuse;FoundationResourceInternercombines several documents. The handover's R1 to R6, K1 to K6, and I1 to I6 pass on both representations. - Part C, native ground in Convert2IDF.
ExposedPerimeter(shared edges cancelled, zero allowed);NativeGroundFloorthroughConstructionSet.Resolve;EpwDryBulbandNativeGroundTemperaturesforSite:GroundTemperature:FCfactorMethod; layered ground with aBuildingSurfaceof 18 °C.- New diagnostics
GroundMethodNotSupportedandGroundTemperatureWeatherMissing, and, after the review,GroundAssignmentSetAside. - The header and provenance (
GROUND:,UNUSED:) record the rule. A conversion without a construction set is unchanged (D-130). - Part D, outside the pipeline.
IdfFoundations.Write(FoundationModel)writesConstruction:CfactorUndergroundWall,Construction:FfactorGroundFloor, layered constructions,Foundation:KivawithFoundation:Kiva:SettingsandSurfaceProperty:ExposedFoundationPerimeter, and the ground temperature objects. Convert2IDF does not use it. What EnergyPlus cannot hold isFoundationNotWritable. - Review fixes. A second agent reviewed the diff: one major, five minor, and nits, all fixed test-first.
- Major: the user-facing descriptions of Construction JSON and Convert2IDF still described the Example's ground. Roles now follows
ResolvethroughConstructionSet.DescribeGround(), andGroundFlooris no longer in the unused list. - Minor: ground rules set aside silently (now
GroundAssignmentSetAside); the Kiva material check ran after the construction cache was read; a start-month off-by-one that the old test did not catch (twelve distinct monthly means now); an inline layered equivalent under a layered-ground or Kiva method (refused at read time); and the written F-factor perimeters for every family. - The last of these found a real defect. On turned plans with courts the written pieces did not keep the footprint perimeter: 586.2 m against 456 m for Court Cluster and 337.6 m against 288 m for Enclosed Court through Perimeter Core, and 89.27 m against 88 m for Point Plate through the merging simplifiers. Edge coincidence was measured at a neighbour's far corners; it is now measured at both ends of the overlap, with the tolerance unchanged.
- Nits: boundary keywords in one place (
IdfBoundaries), F-factor names throughIdfNames.Unique, the ten-block Kiva limit documented, the Kiva operand check counting errors only, and the mass contract check using the tolerances the importer is given. - Checks.
scripts/verify.ps1:VERIFY PASSED, 0 warnings. Tests: Core 740, Generators 518, Integration 2762, Export 2830 (1 opt-in skipped), ConstructionJson 286, ProgramJson 160, Simulation 180 (12 opt-in skipped), IdfBatchRunner 52 (2 skipped), TestSuiteGenerator 89.- With
BEMGEN_ENERGYPLUS=D:\EnergyPlusV26-2-0: Simulation 205 of 205 and Export 2831 of 2831. scripts/idd-check: 0 problems on the 18 IDFs of Part D and on 458 export IDFs in Part C.- Headless Rhino check: all 17 specs passed;
construction-json23 checks ok. Results inD:\BEMGen-smoke\foundation-v7-rhino-2. - EnergyPlus 26.2 results (Chicago O'Hare TMY3; pass means a completed run with no severe or fatal error and no unexpected warning):
| Case | Outcome |
|---|---|
| Medium office F-factor, No Simplification and Perimeter Core | pass |
| Warehouse F-factor, both zonings | pass |
| Retained midrise apartment F-factor | pass |
Tall building F-factor (FoundationWall unused) |
pass |
| Small data centre layered, windowless | pass |
| Small data centre layered, Example windows (Fill From Example) | fail: 6 severe errors, the run completes |
| Legacy hospital basement layered; hospital 2019 3B layered | pass |
| R4: C-factor walls of 2 m and 3 m, both representations | pass |
| K4: retained 5A Kiva slab, both representations | pass |
| K2: two equal 5A slabs, both representations | pass |
| K1: vented crawlspace, coupled slab and walls, both representations | pass |
| K1 crawlspace air exchange | unsupported |
For the F-factor cases, EnergyPlus's U without films equals 1/(A/(P·F) − 0.165) for every construction, the written perimeters times multipliers sum to the footprint's 84 m, and the .eio ground temperatures equal those written.
- Consumer results. R1 to R6, K1 to K6, and I1 to I6 all pass on both representations. They check the importer and the instantiator against the atlas's contract; they do not establish that BEMGen's conversion is correct.
- Open items (also in ADR-023):
- The small data centre's windowless source set with Fill From Example windows gives 6 severe errors (the ground floor absorbs no solar). A warning is proposed, not built.
- A crawlspace or garage host's air exchange (SLA with the ASHRAE 62.2 weather factor, Sherman-Grimsrud) is not instantiated (unsupported).
- Kiva start-day setpoints come from the caller, not from program schedules.
- The 5A Kiva method gives no footing wall construction, so EnergyPlus uses its default.
- The C-factor soil resistance appears capped (about 0.92 m²·K/W on a 3 m wall), inferred from output.
- Part C's ground matching and Part B's instantiator are separate; a shared selection helper is a follow-up.
- The v7 schemas' internal $id versions (construction-equivalent:1.2.0, construction-set:2.1.0) differ from the documents' versions 2.0.0 and 3.0.0; to report to the atlas.
- F-factor cases of turned plans with courts were not run in EnergyPlus.
- Not done. Not merged, tagged, pushed, or released; no docs export. An atlas validation does not establish BEMGen's correctness.
D-147 — A warning when Example windows go into zones whose ground floor absorbs no solar¶
- Date: 2026-10-18 · Decided by: Cheng Xuan Li ("Add a Convert2IDF warning when Example windows go into zones whose ground floor absorbs no solar (the data centre case)") · Status: Accepted. Closes the data-centre open item of D-146 and ADR-023.
- Decision. Convert2IDF gives the warning
ExampleWindowSolarNotAbsorbedwhen three things hold: - the construction set has no window, so the building's windows are the Example's under Fill From Example;
- the resolved ground floor's inside layer has an explicit solar absorptance of 0;
- some zones with windows have only ground floors.
The warning names those zones. The source's values are written as given; nothing is repaired. An unset absorptance is EnergyPlus's default, 0.7, which absorbs.
- Why. The small data centre's source has no windows, and its ground floor is one massless layer with solar absorptance 0. With the Example windows, EnergyPlus reports one severe error per such zone ("floor cannot absorb any solar gains"). The run completes, but scripts/eplus-check fails it. The warning tells the user beforehand to use a windowless building with that set, or a set with windows.
- Checks.
- IdfNativeGroundTests: the warning names exactly the windowed ground-only zones of the linear plan, and is absent on a windowless building, on the office's F-factor set, and on the legacy hospital's layered ground, whose inside layer absorbs.
- The opt-in EnergyPlus case: the warning names as many zones as EnergyPlus reports severe errors (6), and the windowless run has no warning.
- Not covered. A set with its own windows over such a floor; no packet set has one.
D-148 — Turned plans with courts run in EnergyPlus: the merging simplifier passes, Perimeter Core's near-zero core perimeters stall EnergyPlus¶
- Date: 2026-10-19 · Decided by: controller (D-146's open item, taken up as a follow-up session) · Status: Provisional, pending the owner's reading. Follows up D-146's open item "F-factor cases of turned plans with courts were not run in EnergyPlus"; it does not close it.
- Follow-up of D-146. Enclosed Court and Court Cluster, turned 30°, with the medium office v7 set: Conditioned Merge passes, with Σ P × multiplier 288 m and 456 m, the footprints' outer and court perimeters. Perimeter Core fails: its turned core pieces are written with P ≈ 1e-6 m instead of 0, and EnergyPlus does not finish initialisation within 30 minutes. The fix to
ExposedPerimeteris left to the stage owner. Results and diagnosis are in the design note's build log.
D-149 — An uncovered stretch of a ground-floor edge no longer than the distance tolerance counts as covered, so a core's exposed perimeter is exactly 0¶
- Date: 2026-10-19 · Decided by: controller (D-148's finding, fixed as a follow-up session) · Status: Provisional, pending the owner's reading. Refines D-145's exposed-perimeter rule as D-146 realised it; closes D-148's Perimeter Core failures.
- Rule.
ExposedPerimeterexposes what its neighbours leave of each edge stretch by stretch, and a stretch no longer thanToleranceSettings.Distance(a gap between two covering edges, or between a covering edge and an end of the edge) counts as covered. On a turned plan the 1e-6 m clipping grid leaves neighbours' corners a fraction of the tolerance short of a core edge's ends, and the old subtraction exposed those slivers: up to 4.5e-6 m on the bottom-storey core of every turned family through Perimeter Core, and floating-point remainders (2.8e-14 m where inspected) on six unturned ones. EnergyPlus takes only P = 0 as no perimeter; from a micrometre it builds an insulation layer of about 1e8 m²·K/W and stalls in initialisation. Now a piece with no edge on the footprint's boundary is written with P = 0 exactly. - Not changed. No tolerance was added or widened; no positive minimum is ever added; edge coincidence (D-146,
ec4835c) is unchanged; the per-level total keeps the footprint perimeter within the distance per edge, as the family tests already allowed. An edge itself no longer than the tolerance, which could not be matched before, now counts as covered too. - Tests. Written first:
ExposedPerimeterTests(a turned core whose neighbours' corners stop 0.4 µm short gives 0.0; a 10 µm gap stays exposed), andFFactorPerimeterFamilyTestsnow requires every written ground piece with no edge on the footprint's boundary to have P == 0 (it failed for 24 of 36 family cases before the fix). No IDF, integration, or program snapshot changed. The four opt-in turned-court EnergyPlus cases of D-148 pass. Details in the design note's build log.
D-150 — The construction foundation v7 stage merged into main, without a push or release¶
- Date: 2026-10-19 · Decided by: Cheng Xuan Li ("Rerun the Rhino check and fast-forward main to the v7 branch with a decision-log entry; leave all branches and worktrees as they are. Don't push.") · Status: Accepted
- What it merges.
claude/bemgen-construction-v7-update-bd63b1is fast-forwarded intomain. It is 39 commits ahead ofe6c073e(v1.4.0), this entry included, and none behind. It holds: - D-145 to D-149 and ADR-023;
- the shared assignment-selection rule (
AssignmentSelectorinLod.Core), which ground-floor resolution in Convert2IDF and the foundation instantiation service now both use. It was built onclaude/blissful-lamport-65f6c3, applied here by cherry-pick with its build-log section kept beside D-148's and D-149's, and changes no behaviour, snapshot, or test.
The follow-up branches claude/nifty-sanderson-4e38ed (D-148) and claude/nervous-payne-4d8585 (D-149) were fast-forwarded into the stage branch first.
- Checks on the merge candidate:
- scripts/verify.ps1: VERIFY PASSED, 0 warnings. Core 753, Generators 518, Integration 2762, Export 2832 (1 opt-in skipped), ConstructionJson 286, ProgramJson 160, Simulation 180 (13 opt-in skipped), IdfBatchRunner 52 (2 skipped), TestSuiteGenerator 89.
- With BEMGEN_ENERGYPLUS set: Simulation 209/209, Export 2833/2833, IdfBatchRunner 54/54. These include the four turned-court F-factor cases and the data-centre warning case.
- Headless Rhino check: all 17 specs passed, and construction-json passed 23 checks.
- Not done. No push, tag, release, fresh-clone gate, or docs export; the version stays 1.4.0. Branches and worktrees are left as they are, on the owner's instruction.
D-151 — Test suites take construction sets paired by climate, and the research outputs gain the hourly ground heat transfer¶
- Date: 2026-10-19 · Decided by: Cheng Xuan Li (the note's six questions: "test suite generators will handle the pairing; by design, climate should match construction-ground", "ok to mark as invalid and dont write it", ground heat transfer "add it"; the note "approved" with the OpenEPW Las Vegas 3B weather file), realised by the controller · Status: Accepted for the owner's decisions; the realisation is Provisional, pending the owner's reading
- Design note: 2026-10-19-suite-constructions-ground.md, with its build log. Refines ADR-020 (its consequences record the change).
- Test suites with construction sets (
scripts/TestSuiteGenerator): - A constructions entry is
builtin: Exampleor a unified construction JSON (construction_json, a file or a URL pinned bysha256) withfill_from_example. The test case ID holds its content:json:<sha256>;fill=<bool>; the built-in text, and so every earlier built-in ID, is unchanged. - A set is paired only with the weather files of its climate zone; a mismatched pair is never a test case. The Example pairs with every file; a set that pairs with none is a plan error.
- Sets are imported before anything is written. Refused: a scope other than
building, a detailed document (D-142), a schema or closure error, a ground floor the set cannot place, and an F- or C-factor set with a weather file lacking a full year. - A site whose conversion warns
ExampleWindowSolarNotAbsorbed(D-147) isInvalidand not written; every site keeps its conversion warnings. test_casesgainsconstructions_kind,constructions_sha256,constructions_climate_zone,fill_from_example, andground_method(IndoorMinus2,FFactor,Layered).- Ground heat transfer (experiment outputs §9): G(t) = Σ m_z · Q_s(t) over surfaces on
GroundorGroundFCfactorMethod, with Q_s EnergyPlus'sSurface Inside Face Conduction Heat Transfer Energyand m_z the zone multiplier from the.eio, in W as hourly means; positive toward the zone. Annual net, gain, and loss in kWh. - Convert2IDF requests the variable keyed per ground surface and
Output:Surfaces:List, Details;Lod.Simulationreads the surfaces' zones and boundaries from the.eio; the batch runner stores the series,hourly_ground, and three annual columns, in central schema version 3. Version 1 and 2 files are refused, and a run folder from before this stage cannot be resumed (D-064). - Checks:
scripts/verify.ps1:VERIFY PASSED. Core 753, Generators 518, Integration 2762, Export 2837 (1 skipped), ConstructionJson 286, ProgramJson 160, Simulation 198 (14 opt-in skipped), IdfBatchRunner 57 (3 skipped), TestSuiteGenerator 117.- Opt-in, EnergyPlus 26.2.0
4bd7a1f26fwith the owner's Las Vegas file: the medium office F-factor set on two storeys ran with 0 warnings, its.eioground surfaces equal to the IDF's (annual G net −3715.2 kWh);scripts/eplus-checkpassed with no new warning; the pilot suite (the office and warehouse F-factor sets, 2 test cases of 8 sites) ran with all 16 sites succeeding and each reference's comparison metrics 0. - Headless Rhino check: all 17 specs ran to the end with no exception, missing component, missing expected error, or failed check; the only unwritten IDFs are
convert-idf's five expected error scenarios, as in the v1.4.0 run (results inD:\BEMGen-smoke\d151-all). - A second agent reviewed the diff and found nothing blocking; its should-fix items (a test that the conversion warnings reach
site_results, the untested invalid-test-site branch now documented) and nits were applied. - Next: Program JSON 3.0.0 as its own stage, from the atlas's
codex/foundation-groundate9a982d(read only); program JSON 4.0.0's fuel-fired hot water is a research change left for a later decision. Not merged intomain, tagged, pushed, or released.
D-152 — D-151 merged into main; the runner reports sites as they finish and keeps a status row for every site¶
- Date: 2026-10-21 · Decided by: Cheng Xuan Li (after the first mock test suite: "the runner's progress log should indeed be flushed each line", "keep a status row for each site is necessary", "fast forward main first", "local cleanup agreed", "after deletion, ok to delete remote branches"), realised by the controller · Status: Accepted for the owner's decisions; the realisation is Provisional, pending the owner's reading
- Design note: 2026-10-21-runner-progress-and-site-status.md.
- Merge and cleanup.
mainis fast-forwarded tofeature/test-suite-generatorat1ba4840(D-151), without a push. The merged worktrees and local branches are removed (feature/ground-heat-transfer,feature/suite-constructions,claude/bemgen-construction-v7-update-bd63b1,feature/idf-out,feature/construction-json,feature/program-json,feature/idf-batch-runner, and the agents' and controller's leftover branches), and the remote branchesfeature/suite-constructions,feature/idf-out, andfeature/program-json, all merged intomain, are deleted.feature/program-popup-webandfeature/program-popup-windowstay, unmerged, as the owner asked earlier. - Progress lines (
scripts/IdfBatchRunner). The mock suite showed that the runner writes sites one at a time, in plan order, and printed each line only then, so with 16 EnergyPlus processes the log fell far behind the simulations. Each site's line is now printed and flushed when its simulation finishes, with the counts simulated and written; the databases, the site files,batch.json, and the summary keep their plan order. Writer throughput under load is a follow-up. site_status. The central database gains a table with one row per site of a run, whatever happened to it (status, message,succeeded, verdict, closure, exit code, warnings, runtime), and the viewsite_overview(every site with its results where it succeeded).sitesandsite_resultskeep the successes only. Merge keeps one row per site, known by its site ID and EnergyPlus build. Central schema version 4; a version 3 file is refused.- Checks:
scripts/verify.ps1ona91b95f:VERIFY PASSED, 0 warnings. Core 753, Generators 518, Integration 2762, Export 2837 (1 skipped), ConstructionJson 286, ProgramJson 160, Simulation 198 (14 opt-in skipped), IdfBatchRunner 66 (3 skipped), TestSuiteGenerator 117.- The mock suite again on EnergyPlus 26.2.0
4bd7a1f26f, 16 at a time, with the change before the review fixes: 9 minutes 8 seconds; progress lines from the first finished simulation on;site_status1,024 rows (896 succeeded, the two 4A references completed and failed on their unexpected warning, 126ReferenceFailed),site_overview1,024 rows with 896 results. When the last simulation finished the writer had written 366 sites: its throughput under load is the follow-up. - A second agent reviewed the diff and found nothing blocking; its should-fix (the progress total kept the test sites of failed references) and its nits (stale comments, the README's status columns, one status row for a site held twice in one source,
CREATE OR REPLACEfor the temporary table) are applied. - Not done. No merge of
feature/runner-site-statusintomain, no push, tag, or release.
D-153 — The results folder: a light central database, hourly Parquet per site, and raw and failure folders¶
- Date: 2026-10-22 · Decided by: Cheng Xuan Li (the layout and its revisions in conversation: "central duckdb only stores metric relevant info alongside suite, case, and site identifiers", the hourly folder "as parquet (18 series)", a
raw/folder keeping onlyeplusout.err, discard on by default, failures infailure/<test-case-id>/<site code>/,hourly/<test case id>/…file names, plan-order scheduling, and merge refusing a name collision), realised by the controller · Status: Accepted for the owner's decisions; the realisation is Provisional, pending the owner's reading - Design note: 2026-10-22-results-folder-redesign.md. Supersedes D-132's hourly tables in the central database and the batch database, and D-133's spec setting
discard_ep_results. - Why. Timed on 128 sites of the mock suite, the runner's single writer spent about 86 of its 94 ms per site on hourly rows written three times (central database, batch database, CSV). The run's log lagged because
Parallel.Forgave each worker a block of the plan, so the in-order writer waited for the first block; writer cost was the same with and without EnergyPlus running. - The run folder.
batch.json(with each site's IDF and weather paths and the weather SHA-256),central.duckdb,hourly/<test case id>/<site code>.parquet,raw/<test case id>/<site code>/eplusout.err(the whole EnergyPlus folder with--keep-raw), andfailure/<test case id>/<site code>/(the whole folder of a site that ran and did not succeed). EnergyPlus runs in a shortwork/<site key>folder. Removed:results.duckdb,bemgen-hourly.csv,bemgen-summary.csv,bemgen-run.json, the central hourly tables and their views. - Central schema version 5.
sites:- drops the raw header lines, the per-site EnergyPlus version, the weather name and SHA-256, the concurrency columns;
- keeps
energyplus_build; - adds all-hours heating and cooling unmet hours (System Summary, Time Setpoint Not Met, Facility row; the occupied-hours table depends on each zone's occupancy, so on the zoning).
site_status:- drops the warning text (
eplusout.erris kept); - adds the closure residual, meters written, concurrency, runtime comparability, IDF version, run warnings, finish time, and the IDF's
code_version(provenance:batches.tool_versionis the runner's version, not Convert2IDF's).
- drops the warning text (
- Transactions: a site's
sitesandsite_statusrows are written in one transaction. - Hourly Parquet.
- Contents: the 18 series of the batch database in a long table with
site_idandenergyplus_buildin every row, ZSTD. - Writing: by the worker that ran the site, before its rows.
- Reading: no view is stored, since DuckDB resolves a view's relative path against the reading process's working directory (checked); the README gives the
read_parquetjoin. - Runner behaviour.
- Discard moves to the runner, on by default; a spec with
discard_ep_resultsis refused. - Resume is incremental: a site with a status row is done; a reference's loads are read back from its Parquet file, and a missing or damaged one is run again.
merge --out <folder>writescentral.duckdbandhourly/, copying the added sites' files, and refuses a name collision, including the same site under two EnergyPlus builds, and schema version 4 or earlier.- Sites start in plan order.
- Research documentation. Experiment outputs §8 now says what a run keeps by default: metrics, the hourly series, and
eplusout.err; the other EnergyPlus files only with--keep-rawor on failure. No metric changes.ExperimentMetrics.Computeagainst a reference's three loads equals the old path exactly (tested). - Checks:
scripts/verify.ps1onaef5b56:VERIFY PASSED, 0 warnings. Core 753, Generators 518, Integration 2762, Export 2837 (1 skipped), ConstructionJson 286, ProgramJson 160, Simulation 205 (14 skipped), IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. WithBEMGEN_ENERGYPLUSandBEMGEN_WEATHER_3B, the runner tests 91/91.- The mock suite on EnergyPlus 26.2.0
4bd7a1f26f, 16 at a time, with the code before the review fixes, which change no normal-path result:- time: 8 min 24 s (9 min 8 s with D-152's writer);
- writer: 885 of 898 sites written when the last simulation finished, against 366 before;
- sizes:
central.duckdb3.6 MB (was 331 MB),hourly/383 MB for 896 files (about 430 kB per site, 18 series),raw/3.7 MB,failure/117 MB for the two failed Sterling references; - statuses: 896 succeeded, 2 failed, 126
ReferenceFailed; the documented join gives 896 × 18 × 8760 rows; - unmet hours: 0 everywhere, with ideal loads.
-
Review: a second agent found nothing blocking. Its should-fix items are applied:
- one write per site even after a writer failure;
- a Parquet write failure making only that site
RunFailed; code_versionkept;- the weather SHA-256 in
batch.json.
So are its nits (an
.errkept on a repeated finish, a damaged reference file rerun, merge temporary files removed,WriteBatchin one transaction). - Open. An oldsuite.jsonthat still hasdiscard_ep_resultsis ignored by the runner. Not merged intomain, pushed, tagged, or released.
D-154 — The runner's progress, site status, and results folder stages merged into main and pushed¶
- Date: 2026-10-22 · Decided by: Cheng Xuan Li ("all good. merge and push", after running the small mock suite) · Status: Accepted
- What.
feature/runner-site-status(D-152, D-153: 15 commits after1ba4840, this entry included) is fast-forwarded intomain, andmainis pushed toorigin.mainat1ba4840(D-151) was already onorigin. - Checks.
scripts/verify.ps1passed onaef5b56(D-153); the commits after it change documentation only. WithBEMGEN_ENERGYPLUSandBEMGEN_WEATHER_3Bthe runner tests passed 91/91, and the mock suite ran on EnergyPlus 26.2 (D-153). - Not done. No tag, release, docs export, or fresh-clone gate; the version stays 1.4.0. The branch
feature/runner-site-statusstays until the owner asks to remove it.
D-155 — Branch and worktree cleanup; the program JSON popup branches kept unmerged on origin¶
- Date: 2026-10-25 · Decided by: Cheng Xuan Li ("merge most of them (except for the popup 2 branches) and delete merged except current worktree", "for popup: keep that branch(s) do not merge. push worktree commits if not already") · Status: Accepted
- Merged branches deleted. Every branch other than the popup branches was already in
main(D-151, D-154). Deleted:feature/test-suite-generator, with its worktreeidf-batch-runner-plan-859669and its remote branchorigin/feature/test-suite-generator.feature/runner-site-statusand its worktree stay for now: they equalmainand hold the session that did this cleanup. The leftover non-git foldersD:\worktrees\conditioned-merge,dropdown-enums, andprogram-mix(old IDE state) are deleted. - Popup branches.
feature/program-popup-window(the JSON editor window on Program JSON, D-128) andfeature/program-popup-web(the experimental atlas web view, D-129, which contains the window branch) are kept unmerged and are now pushed toorigin, so they no longer exist on one machine only. They have no worktree. D-128 and D-129 are recorded only on those branches, which is why the log onmaingoes from D-127 to D-130. A trial merge intomainconflicts in five documentation files (AGENTS.md, this log, the roadmap, and the two smoke-test documents); a later merge resolves them and runsscripts/verify.ps1and the Rhino check. - Untouched.
origin/batch1-savedandorigin/feature/reduced-order-5r1care a collaborator's branches and are left as they are.
D-156 — Paired evaluation metrics: ideal sensible loads, peaks, hourly fit, temperature, and computation¶
- Date: 2026-10-26 · Decided by: Cheng Xuan Li (the metrics and the implementation requirements: "update the IDF simulation evaluation pipeline to compute and export the following metrics … supporting subsequent paired comparisons against a designated high-detail reference"); the realisation by the controller · Status: Accepted for the scope; the realisation superseded in part by D-157, which settles the rest
- What. A second metric set, experiment outputs §10, beside §2 to §5, which do not change. Design note: 2026-10-26-evaluation-metrics.md. Built on
feature/evaluation-metrics, in its own worktree, frommainatb80477b. - Loads: the ideal sensible heating and cooling, summed over the zones' ideal-loads systems apart at each hour, never netted, without latent or non-HVAC loads: annual kWh and kWh/m², the maximum, P99, and P95 in kW, each with its relative error; the hours above the reference's P99 and P95 for the site and the reference; CV(RMSE) and NMBE (site minus reference) in %; the load-duration-curve NRMSE over all ranks and the top 1, 5, and 10 % (88, 438, 876 hours), each normalised by the reference's mean within the interval; KS.
- Temperature: the conditioned-floor-area-weighted mean zone air temperature's MBE and RMSE against the reference's over all hours and over the common unconditioned hours, which come from the ideal loads' availability schedules, never from zero loads.
- Computation: wall-clock and EnergyPlus runtime, zone and represented zone counts, average and peak process memory (sampled every 50 ms by the process runner), and each one's ratio over the reference's.
- Pairing: a site is compared with its test case's reference only (building, envelope, weather, run period, D-132, D-153), and the hourly internal-load end uses are checked against the reference's; a site that fails keeps its metrics with the problem in its status message.
- Near-zero denominators.
ToleranceSettingsgainsNegligibleLoadIntensity, 0.01 W/m² of the reference's total floor area (ADR-004 amended): a metric normalised by a reference load at or below it is missing withNearZeroReference, never a percentage. New flags:NearZeroReference,NoCommonUnconditionedHours,AvailabilityUnknown,NotMeasured. - The multiplier, found in the build. EnergyPlus 26.2 reports the ideal-loads zone variables with the zone multiplier in. In the linear plan with multipliers above 1, the plain sum of the zones' sensible heating equals
Heating:DistrictHeatingWaterto 1e-15 in all but rare hours (one hour in each of three of the 56 configurations, 1e-6 below it; in the atlas medium office 57 hours, at most 2 × 10⁻⁵), and the plain sum of their total cooling equalsCooling:DistrictCoolingto 1e-12; the sensible cooling is never above it. The first draft multiplied them again, as §9 does for surface variables, and overcounted by up to 2.8 times; the opt-in runs caught it.Heating:EnergyTransferandCooling:EnergyTransferare not the zone sensible loads (up to 0.8 % apart in an hour) and stay diagnostics. - Stored. Central schema version 6: the §10 columns of
siteswith their_flagcolumns, the tablemetric_definitions(one row per evaluation column), andbatches.negligible_load_w_m2; the hourly files gainSensibleHeating,SensibleCooling, andConditioningAvailable(21 series). Resume rebuilds a reference's profile from its hourly file and itssitesrow. - Cannot be computed today. The temperature over common unconditioned hours is always
NoCommonUnconditionedHours, because Convert2IDF leaves the availability schedules blank (conditioning every hour; setbacks are conditioning); memory exists only for runs of the batch runner from this stage; results folders of schema version 5 are not resumed or merged. - Checks.
scripts/verify.ps1passed (2026-10-26, uncommitted tree). WithBEMGEN_ENERGYPLUS(EnergyPlus 26.2.0, the installation's Chicago O'Hare TMY3) and withoutBEMGEN_WEATHER_3B:Lod.Simulation.Tests279 passed, 1 skipped (needs 3B weather);IdfBatchRunner.Tests97 passed, 1 skipped (the pilot suite, needs 3B weather). A second agent reviewed the diff: no serious finding; its fixes are in (a flag column for every metric that may be missing, the memory sampler's start-up sample dropped, a zero-weight temperature keeps its own flag, an infinite reference gives no ratio, the end-to-end availability check for unconditioned buildings, and documentation). - Committed on
feature/evaluation-metricsas six focused commits afterb80477b, this entry the last (the owner, 2026-10-26: "commit … as focused commits (no push, no merge)"). - Not done. No merge, push, tag, or release; the stage close-out's
sync-docspass waits for the merge.
D-157 — Evaluation metrics: the owner's decisions on signs, P90, unconditioned hours, and measuring cost in parallel¶
- Date: 2026-10-26 · Decided by: Cheng Xuan Li, reading D-156's open choices · Status: Accepted · Supersedes in part: D-156 (the NMBE sign, the temperature over common unconditioned hours, the memory measure alone)
- Kept as D-156 built them: the near-zero threshold (0.01 W/m² of the reference's total floor area), the zone ideal sensible variables, the top intervals as the first ⌈k · n / 100⌉ ranks, the working set, and the pairing check that keeps a failing site's metrics.
- Bias sign. NMBE uses ASHRAE Guideline 14's sign, reference minus site ("let's use the ashrae convention: reference - site"): positive when the site's load is below the reference's. The temperature MBE follows, as the same kind of bias error; the relative errors of annual and peak values stay (site − reference) / reference.
- P90 is added beside P95 and P99 for the sensible peaks, their errors, and the exceedance hours, as experiment outputs §3 has it.
- No temperature over unconditioned hours ("let's not complete the conditioned hour temperatures ever. remove that part from the current code and plan"). No program is off by the hour: a zone is conditioned all year or not at all, and Convert2IDF writes no availability schedule. Sourced programs set back their setpoints (the atlas medium office from 21/24 °C to 15.6/26.7 °C at night, Saturday afternoons, Sundays, and holidays), which is conditioning. The availability reading, the flags
NoCommonUnconditionedHoursandAvailabilityUnknown, their columns, and theConditioningAvailablehourly series are removed (20 hourly series); the temperature is compared over all hours. - Cost in parallel runs (the owner chose the recommended "robust measures + timing pass"). Batches keep running in parallel.
- Every run records the processor time (user and kernel) and the peak committed private memory, which other processes change far less than the wall clock and the working set; both get ratios over the reference.
- An optional serial timing pass (
--timing-repeats N,--timing-test-cases K) re-runs the succeeded sites of the timed test cases one at a time once every site is written, test cases in the order of their IDs' SHA-256, repeats as the outer loop, each reference just before its variants. It writessite_timing; the viewsite_timing_summaryholds the medians and their ratios to the reference's. Resume and merge carry it. - Central schema version 7: D-156's version 6 existed only on this branch, and a folder of it is refused by resume and merge.
- Recommended use:
--parallelat most the number of physical cores, the processor-time and committed-memory ratios of every site, and a serial sample to report runtimes and to calibrate the parallel measures. - Built on
feature/evaluation-metricsafter D-156, with the experiment outputs §10, the design note, and the runner's README. - Checks. A second agent reviewed the diff: no serious finding; its fixes are in (schema version 7, a P90 test that tells the percentiles apart, an unknown committed memory gives no ratio, a timing run that could not start has no wall clock, documentation).
scripts/verify.ps1passed after them. WithBEMGEN_ENERGYPLUS(EnergyPlus 26.2.0, Chicago O'Hare TMY3), withoutBEMGEN_WEATHER_3B:IdfBatchRunner.Tests105 passed and 1 skipped after the fixes (the real batch with--timing-repeats 1among them);Lod.Simulation.Tests275 passed and 1 skipped before the last two fixes, which do not touch what the opt-in runs check. - Headless Rhino check (the owner, 2026-10-26):
scripts/rhino-smoke/run.ps1on58d0522, all 17 specs, Rhino 8, 2026-10-26 04:22 to 04:28: every spec done, no exception, missing component, missing expected error, or failed check; the runtime errors are the specs' error scenarios. - Not done. No merge, push, tag, or release. The leftover worktree
idf-evaluation-metrics-60072band its branch, equal tomain, are removed (the owner, 2026-10-26).
D-158 — The paired evaluation metrics stage merged into main, without a push or release¶
- Date: 2026-10-26 · Decided by: Cheng Xuan Li ("Fast-forward main to feature/evaluation-metrics and run the sync-docs close-out; don't push") · Status: Accepted
- What.
mainis fast-forwarded fromb80477b(D-155) tofeature/evaluation-metrics(D-156, D-157), and then to this entry with the close-out's documentation edits. Not pushed, tagged, or released; the version stays 1.4.0. - Checks. Those of D-156 and D-157:
scripts/verify.ps1, the opt-in EnergyPlus 26.2 suites, and all 17 headless Rhino specs passed, with two reviews by a second agent. - Close-out (
sync-docs). Links and code fences checked (developer_pages.py --check: 80 pages, no broken link); this entry, D-156's status, the design note's status, AGENTS.md's project state, the roadmap's progress log, and README's line on the batch runner brought up to date. The public site describes 1.4.0 (e6c073e); it follows at the next tagged version with the docs export (D-118), which then also carries the developer pages of D-145 to D-158.
D-159 — Main pushed with the paired evaluation metrics, without a tag or release¶
- Date: 2026-10-26 · Decided by: Cheng Xuan Li ("Push main to origin (no tag, no release)") · Status: Accepted
- What.
mainis pushed tooriginas a fast-forward,b80477b..7d3c941(D-156 to D-158), and then with this entry. No tag, release, or docs export; the version stays 1.4.0.
D-160 — The DOE zone is the most detailed level of a DOE generator; D-097 is relaxed for them¶
- Date: 2026-10-25 · Decided by: Cheng Xuan Li (owner, O4 of the DOE geometry design note); wording by the controller, pending the owner's reading · Status: Accepted by the owner's approval of PR #1 (D-176) · Refines: D-097
- Numbering. The DOE geometry entries were written as D-156 to D-167 on
feature/doe-geom, became D-159 to D-170 at the merge ofmainwith the evaluation-metrics entries D-156 to D-158, and are D-160 to D-171 since the second merge ofmain, which added D-159 (the owner's decision to renumber aftermain's entries, 2026-10-26). Commit subjects on the branch keep the numbers of their time. - Merge of
main(7d3c941, the paired evaluation metrics, D-156 to D-158) intofeature/doe-geomasd168942: the conflicts inAGENTS.md, ADR-004's status, this log, and the roadmap's progress log keep both sides;ToleranceSettingstakes both new optional parameters,negligibleLoadIntensitythenacceptance(every caller names them).scripts/verify.ps1: VERIFY PASSED, 0 warnings: Core 922, Generators 3051 (1 skipped, A2), Integration 3584, Export 3106 (1 skipped), ConstructionJson 286, ProgramJson 175, Simulation 246 (14 skipped), IdfBatchRunner 103 (3 skipped), TestSuiteGenerator 119. The headless Rhino check: all 25 specs ran, no Validate False, exception, missing component, failed check, or missing expected error. - Second merge of
main(98aa569, D-159, the push ofmain) intofeature/doe-geom, after renumbering these entries by one more: the one conflict, in this log, keeps both sides withmain's entry first. - Why (the owner's words): relax D-097 for the DOE generators: the DOE zone, whatever it covers (one room, grouped rooms, a classroom pod), is their most detailed level; Semantic Merge then gives the department level.
- For the DOE reference geometry generators (ADR-025, design note 2026-10-25-doe-geometry-generators.md), the zone of the source (the DOE reference zone, or the detailed-office space for offices) is the most detailed level. D-097 (one zone per department, never per room or tenancy) is relaxed for these generators only.
- Unchanged: D-097 for every other non-residential generator (the six precedent generators
OfficePlate,Cafeteria,BranchingMall,OperatingSuite,CareHub,FoyerHalls), and D-009 and D-085 for housing. ADR-014's department table is not changed. - A DOE zone carries its standards space type as an explicit
ProgramKey(ADR-025, S4); the simplifiers and aggregators coarsen DOE zones as they coarsen any others.
D-161 — DOE geometry stage P0a: ADR-025, layout records, grid plan layout, program keys, and the comparison harness¶
- Date: 2026-10-25 · Decided by: the controller under D-098, pending the owner's reading; the program-key rule by the coordinating session that dispatched the controller · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P0a; ADR-025 (S1–S10, accepted by the controller, provisional); docs: doe-layouts.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p0a-adr(18269cc) andfeature/doe-geom-p0a(ce859a7). - Built:
GridPlanLayout(S1): storeys from x-cuts, y-cuts, and zones as unions of grid cells; duplicate IDs areDuplicateZoneId; a court touching the outline at a corner is a hole.GridAxis(S3): fixed intervals keep their length, flexible ones share a change in proportion to their default length and stay at least 1 m, one axis per building, lengths compared withinToleranceSettings.Distance.scripts/doe-geometry/extract.py(stdlib,python -I,--check) writes 15 layout records (src/Lod.Generators/Doe/Layouts/, embedded, loaded byDoeLayoutRecords) and 15 answer keys (tests/fixtures/doe/), each with its sources' SHA-256, the DOE credit, and the openstudio-standards BSD-3 licence.ProgramKeyonPlannedZone,Zone, andTargetZone(nullfor every earlier generator, so every snapshot is unchanged; only Semantic Merge sets it on new zones); Semantic Merge groups by space type and key;PlanPresets.PresetFor; the fixed tableDoeSpaceTypes;DoeProgramMap(a missing key isMissingPreset).ToleranceSettings.Acceptancewith the §5 tolerances (ADR-004 amended); the comparison harness inLod.Generators.Tests(gap zones excluded zone by zone and from the T4 totals).- Program keys: for every reference zone, in order: (1) the zone name is a standards space type of the type's building type in openstudio-standards
doe_ref_1980_2004.spc_typ.json; (2) the prototype geometry OSM thatprototype_inputsnames for DOE Ref 1980-2004 has a space of the zone's name, whose assigned standards space type is the key; (2b) OSM spaces of exactly one standards space type tile the zone within the ADR-004 distance and relative-area tolerances; (3) otherwise the extraction stops. Each zone records its rule, the OSM spaces used, and the file with its SHA-256; the OSM only names the type, the geometry is the reference's (O8). All zones of the 15 records resolve (rule 1: restaurants, supermarket, warehouse office, retail back space; 2b: warehouse bulk and fine storage; 2: the rest); the offices take their rooms' standards space types. Every key is pending A1 (no atlas program yet). - Office T4 inputs (test fixture): per orientation, the reference's glazing (windows and glass doors) over the reference's outdoor wall area (below the 2.74 m ceiling in the medium and large offices, S6; the full 3.05 m wall in the small office), on every room facing it; the rooms reproduce the note's window areas, the small office's 20.64 m² south (3.91 m² glass door included) too.
- Routine choices: fifteen records (the note's "14" is one short of its own §4 table); ADR-025 written on its own docs branch; kebab-case record names and
DoeBuildingType; source coordinates within 5 mm put on one cut (one adjustment per storey, largest 0.018 m² in the hospital); storey multiplier = smallest zone multiplier on the storey; floor height = area-weighted most common zone-plus-plenum height; G4 copies only for the outpatient stairs and lift; the warehouse's fine-storage floor above the office dropped (G4, −236.88 m²); outdoor walls in answer keys as BEMGen builds them; a flexible interval already under 1 m by default may grow but not shrink. - Source facts against the note: the large office's 46,320 m² includes the 3,563.1 m² basement (42,757.25 m² above grade, G3); the offices' perimeter depth is 4.5733 m (medium) and 4.5732 m (large), not 4.57 m, so T7 takes the depth from the answer key (at 4.57 m the cores miss by 0.43 and 0.65 m²); the small office has no plenum (G2 only, not G1); the outpatient record has 59/41/26 zones per storey, the note's 59/37/22 plus the G4 stair and lift copies.
- Checks:
scripts/verify.ps1once859a7: VERIFY PASSED, 0 warnings: Core 782, Generators 868, Integration 2762, Export 2837 (1 skipped), ConstructionJson 286, ProgramJson 160, Simulation 205 (14 skipped), IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119.extract.py --check: 30 files identical. Every record matches its answer key on T1–T3 throughGridPlanLayout; T7 passes for the three offices. A second agent reviewed ADR-025 (two rounds) and the stage (three rounds); every finding is applied. - Pending: the hospital's ground storey (a room drawn apart from the plate, two footprint polygons,
NotSupporteduntil P5) and the hospital's T3 total; the full T4 run until P0b; A1 for every program key.
D-162 — DOE geometry stage P0b: glazing per orientation, skylights, and the true outdoor wall basis of T4¶
- Date: 2026-10-25 · Decided by: the controller under D-098, pending the owner's reading; the T4 basis by the coordinating session that dispatched the controller · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P0b (S5, S6, O3). Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p0b(30f6df1). - What.
ProgramPresetcarries a WWR per orientation (WindowToWallRatios, N/E/S/W) and aSkylightRatio; a scalar WWR means all four and no skylight, so every existing snapshot, report, provenance, IDF, and icon is unchanged. An outdoor wall takes the WWR of the sector its true azimuth falls in (S5).PlanParameters.GlazingReferenceHeight(optional, (0, floor height], in provenance when set) sizes windows as WWR × length × height (S6). - Skylights (ADR-012, extended). One centred skylight per zone ceiling, in its largest grid rectangle (vertex coordinates, plus edge midpoints when the roof is not rectilinear). Simplifiers give each target ceiling, and
Storeys.Assembleeach outdoor roof piece (terraces included), Σ ratio × overlap of the detailed ceilings: covered ceilings get none, and the result does not depend on the method.SkylightDoesNotFit, never capped;SkylightNotHostedis a guard. Validation addsSkylightsandBuilding.SkylightsOnRoofswhen skylights exist. - Program JSON. Options
WindowToWallRatiosandSkylightRatio, and, until atlas input A2, a glazing fixture (bemgen-program-glazing-fixture0.1.0, keyed bysource.program_id, its SHA-256 in provenance). - Converters and Grasshopper. Convert2IDF writes skylights as
FenestrationSurface:DetailedWindowin their roof piece; courts are cut along whichever axis keeps them whole (documented limit: a skylight covering one court's x-range and another's y-range isDegeneratePolygon; no generator reaches it). Convert2BEM lists skylights in Windows. The default preset components, Program Preset, and Program JSON gain WWR North/East/South/West and Skylight Ratio, appended; set inputs always win, a uniform set included; no GUID or icon changes. - T4 basis. The WWR per zone and orientation is the source glazing over the source's true outdoor wall: the zone's Outdoors walls less every stretch that coincides with a wall of another counted zone, whatever its boundary, over the height both walls share, by the D-041 façade-line test within the ADR-004 tolerances.
extract.pycomputes it from the source only and lists every stretch with its partner's boundary class: 18 stretches, Outdoors 8 (secondary school bathrooms and corridors against the auditorium), Surface paired 2 (small officeCorridor_1, large officeLobby_Bot_ZN_2), Surface self-referencing 8 (outpatientFloor_2_Reception_Hall). Partners are counted zones only: plenums, attics, and basements are excluded, consistent with G1; a probe found 0 stretches against them. T2 checks the true outdoor wall against what BEMGen draws, and every record passes. Atlas input A2 must use the same basis. - Source boundary artifacts. The stretches against a non-Outdoors wall are source boundary artifacts that BEMGen corrects geometrically, for example the outpatient reception hall's 26.5 m of south wall, which the source exposes to outdoor air although it adjoins the X-ray and exam rooms. Any later energy comparison against the source must know that these walls differ. How BEMGen treats self-referencing partners thermally belongs to P5a.
- T4 exclusions by sourced rule. G4: the warehouse office (shorter than its storey, under the dropped fine-storage floor) and fine storage; the secondary school's tall zones and the walls against a tall zone drawn on its own storey only; the outpatient's G4 copies. Pending S8/P5a: walls toward multiplied rooms (large hotel 8 quantities, 3 still disagreeing; hospital 46), ratcheted separately in T4 and T2. Pending P5: the hospital's ground storey and totals. All 15 records pass T4, skylights per zone and in total included.
- Checks.
scripts/verify.ps1on30f6df1: VERIFY PASSED, 0 warnings: Core 862, Generators 957, Integration 2929, Export 3098 (1 skipped), ProgramJson 175, Simulation 205 (14 skipped), ConstructionJson 286, IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119.extract.py --check: 30 files identical;test_extract.py: 10 tests OK. Rhino smoke on5cd2cc5: 18 specs, no failed check (new specglazing). A second agent reviewed the stage in five rounds; every finding is applied. - Open. For a person in Rhino 8: skylights in the viewport, an EnergyPlus run of a skylit IDF, the editor checklist. Sourced glazing per program waits on A2.
D-163 — DOE geometry stage P5a: multiplied rooms, the copy space, the self-adjacent boundary, and per-key glazing inputs¶
- Date: 2026-10-26 · Decided by: the controller under D-098, pending the owner's reading; the Conditioned Merge rule and the one-plate design by the controller; the treatment of walls on nothing and the per-key glazing inputs by the coordinating session that dispatched the controller (2026-10-25) · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P5a (S8, O5); ADR-026 (accepted by the controller, provisional; amended to the build); docs: doe-layouts.md, pipeline.md, domain-model.md, validation.md, convert2idf.md, convert2bem.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p5a-adr(96a4c70) andfeature/doe-geom-p5a(00404c8). - Multiplier.
PlannedZone,TargetZone, andLayoutZonecarryMultiplier(default 1, whole, below 1InvalidParameter); the plan builder gives it toZone;Storeys.PlaceAtmultiplies it by a group's storey count (a ×19 room on a ×4 floor is ×76). Every total counts zone × multiplier. - One plate with a copy space. On a storey with a multiplied zone
GridPlanLayoutputs every grid cell in the footprint, which is one polygon (the hospital's ground storey included); the copy space is the footprint less the drawn zones (CopySpace.Of); drawn zones plus copy space must make the footprint's area (CopySpaceAreaMismatch); every placed storey carries it (PlacedStorey.CopySpace). Without a multiplied zone, cells in no zone stay outside and a footprint of several polygons staysNotSupported. T1 compares the drawn outline. This replaces ADR-026's first draft (several footprint polygons). BoundaryCondition.SelfAdjacent(no adjacent zone), from the footprint: a zone edge on the copy space (not on the plate's outline), an edge between zones of different multipliers (both sides, never paired), and a floor or ceiling piece between different multipliers or on the copy space of the storey beyond. It is neither exterior, ground, roof, nor exposed; an F-factor ground floor's edge toward the ground copy space is not exposed perimeter. The D-069 options leave it unchanged; it takes the interior construction of its kind; Convert2IDF writes aSurfacenaming itself, each piece its own name; Convert2BEM printsSelfAdjacent.InterzoneMultiplierMismatchstays as the writer's guard for other buildings. Stacked Floor Zone Multiplier makes every piece between storeys adiabatic, a self-adjacent one too (D-075). The source's self-referencing walls between zones of equal multiplier stayInterzone.- Steps. Semantic Merge and Conditioned Merge merge only zones of equal multiplier and keep it; Perimeter Core, Single Zone per Floor, Single Zone per Floor Type, and Single Zone Building return
NotSupportedfor floors with multiplied rooms. Validation addsMultiplierPairingandBuilding.MultiplierPairingonly when a source plan has room multipliers. - Walls on nothing (geometry decides, as D-162).
extract.pycounts every stretch of a source wall not marked Outdoors with no counted zone and no copy space beyond it as a true outdoor wall, whatever its source boundary, and lists it in the answer key (nonOutdoorExteriorWalls); a stretch toward the copy space is self-adjacent. - Source boundary artifacts. The source treats 16.8 m of the hospital plate's east façade as adiabatic:
OR_NurseSat_Flr_2_Wall_1_East(x = 70.104 m, y 0 to 16.764 m) is a self-referencingSurfacewith no zone and no copy beyond it; BEMGen draws it as an outdoor wall ofOR_NurseStn_Lobby_Flr_2. No other wall of the fifteen records has nothing beyond it and a boundary other than Outdoors. With D-162's artifacts (the outpatient reception hall's 26.5 m of south wall, the small and large offices' short paired stretches), these are the walls an energy comparison against the source must name. - Per-key glazing inputs (A2 basis). Each answer key gives each program key's WWR per orientation (source glazing over the true outdoor wall, or the wall below the source ceiling under G1, summed over the key's zones times their multipliers without gap zones and orientations) and its skylight ratio (
programGlazing), and lists every zone whose key ratio would miss its own source glazing by more than T4's 1 % (glazingRatioDifferences): small hotel 43 (among themGuestRoom206_208S, 0.191 against the key's 0.158), outpatient 38, hospital 6, every other record 0. T4 runs on the generators' path, one program per key throughDoeProgramMap: window area per orientation in total and per key within 1 % for every record; per-zone glazing is checked where every zone of a key shares its ratio, the listed zones are reported, and a test checks the list is exact. The 1 % threshold for "differs" is a controller routine choice tied toAcceptance.RelativeGlazingArea. - Harness. T2 splits the key's walls on no other zone into
self-adjacent walland the outdoor rest, counts back-to-back self-adjacent walls as shared, and compares per zone the roof and exposed floor area withsourceRoofAreaandsourceExposedFloorArea. Thepending: S8, P5aandpending: P5, two-polygon ground storeyexclusions of D-161 and D-162 are gone: the large hotel's 8 and the hospital's 58 T4 quantities, their T2 true outdoor walls, and the hospital's ground storey and T3 total pass. - Source fact. The hospital's ×4
Office1leaves a 12.1975 m gap (room for four copies, not three), so copy-space area and Σ(m − 1)·area are not tied. - Checks.
scripts/verify.ps1on the merge74cbde4: VERIFY PASSED, 0 warnings: Core 917, Generators 1024, Integration 2929, Export 3106 (1 skipped), ConstructionJson 286, ProgramJson 175, Simulation 205 (14 skipped), IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119.extract.py --check: 30 files identical;test_extract.py: 17 tests OK. A second agent reviewed ADR-026 (four rounds) and the stage (three rounds); every finding is applied; six self-referencing IDFs passed EnergyPlus 26.2 in review (0 warnings, 0 severe). - Open. For a person in Rhino 8 (after P5b's components): the Convert2BEM
SelfAdjacentoutput and viewport.
D-164 — DOE geometry stage P1: the ring offices, storey multipliers, the DOE program map component, S3 by slack, and the O3 wording¶
- Date: 2026-10-26 · Decided by: the controller under D-098, pending the owner's reading; the S3 amendment and the O3 wording by the coordinating session that dispatched the controller (2026-10-25) · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P1 (§1 O3 and §2 S3 amended in the same merge); ADR-025 S3 and O3 amended; ADR-015 amended; docs: doe-ring-office.md, doe-components.md, doe-layouts.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p1(ca725cd). - Built:
RingOfficeGenerator(records Small, Medium, Large; 17; 23/21/21; 26/26/26 zones) onGridPlanLayout, one plan per record storey, presets from aDoeProgramMap; gaps G1 (medium, large), G2 (small), G3 (large);RingOfficeGenerator.Limits.MultiStoreyPlanGenerator.StoreyMultiplier(default 1; provenanceStoreyMultiplierwhen not 1; it counts in the elevations above): the large office's middle plan stands forStoreys − 2(10), so its storeys stand at the reference's 0, 19.816 (×10), and 43.5952 m. Grasshopper: DOE Program Map (keys and presets, or one preset for every key;DoeProgramMap.Create), theDoeGeneratorComponentbase (inputs in one order for every DOE family: counts, sizes, Floor Height, Orientation, Program Map, Preview Location; outputs Plan, Multipliers, Program Keys, the keys listed even with no map and with an input that has no data), DOE Small/Medium/Large Office. - S3 amendment (decided above the controller). Fixed bands keep their length; flexible intervals share a plate change in proportion to their slack (length minus 1 m), shrinking and growing alike, so all reach 1 m at the shortest valid plate (fixed bands plus 1 m per flexible interval); an interval under 1 m has no slack and keeps its length (replacing P0a's "may grow but not shrink"); a total slack within the distance tolerance is none, and then any change is
InvalidParameter; the defaults stay exact. InGridAxis, for every family. Why: under the first rule the large office could not shorten by even 1 % (core intervals of 1.023 and 1.036 m). P0a tests changed accordingly (the small office's half plate is now valid; the resized primary school is checked against hand-shared cuts). - T5 rule (decided above the controller). Every size parameter at 0.5×, 1×, and 2× of its default, or, where a factor is invalid under S3, at its computed limit (which keeps the topology) and just beyond it (
InvalidParameter); each family documents its limits and reports the valid range. Ring offices (m, at the defaults): plate length ≥ 16.573 / 23.994 / 49.107; plate depth ≥ 13.582 / 17.121 / 34.402; perimeter depth 0.5–7.011 / 0.593–13.420 / 0.5–19.321; corridor width 0.5–3.658 / 0.5–9.296 / 0.5–8.692; every limit at least ±20 % of its default. - O3 wording (decided above the controller). "Window-to-wall ratio per orientation (N, E, S, W) and a skylight ratio are program data from the atlas, per zone type. Window shape is not reproduced. Glazed area per orientation is reproduced in total and per program key, and per zone where a key's zones share one ratio." The owner's decision was the ratio per zone type; "glazed area per zone" in the first wording went further. A second per-zone input path is rejected; atlas input A2 is unchanged (one ratio per program and orientation, true-outdoor-wall basis).
- Routine choices:
GridAxis.MinimumFixedBand = 0.5m for every band a size parameter sets (per band, not per grid interval), for every DOE family; ring defaults are the top storey's south bands, rounded to the records' 1e-6 m (RecordDecimals, tied toextract.py); the glazing reference height keeps the source's share of the floor height (a fixed 2.74 m would exceed the floor height at 0.5×); one component per record; size and count inputs optional (each keeps its record default; an input wired to no data is an error naming it); minimum storeys = the record's storeys; T6 with eight different programs over the keys; T4 through the per-key glazing (offices: no per-zone differences); GUIDs DOE Program Map0e471c68-8cb1-4375-b82a-71b36d797b94, DOE Small Officec8f1e87b-b238-4b4f-a6de-c510d1e5d02b, DOE Medium Officee7998061-adad-41d7-a08f-aeb372863442, DOE Large Office8bb8e1b0-83e8-4cdf-b36b-3364315e6006, Program Map parameterec21d323-a3c1-41d7-aed8-b1e13dab50df. - Open, for the owner: under D-045, Stacked Floor Zone Multiplier models the large office's ×10 floor at storey 6 (23.779 m), while the reference draws it at storey 5 (19.816 m), its lower middle storey; the large hotel's ×4 floor likewise (storey 3 against 2). D-045 is unchanged, because changing it would alter existing snapshots with even multipliers.
- Checks:
scripts/verify.ps1onca725cd: VERIFY PASSED, 0 warnings: Core 920, Generators 1161 (1 skipped, A2), Integration 3184, Export 3106 (1 skipped), ProgramJson 175, Simulation 205 (14 skipped), ConstructionJson 286, IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. Rhino smoke on36a61d8: 19 specs, no failed check (doe-offices16 scenarios, 14 checks). On the merge4807c6e: VERIFY PASSED with the same counts, and the full Rhino smoke check ran 19 specs with no failed check. A second agent reviewed the stage in three rounds; every finding is applied. - Pending: T4 with the atlas's glazing (A2); A1 and A3; icons for the five new classes (stage C); for a person in Rhino 8, the toolbar group and the viewport previews.
D-165 — DOE geometry stage P3: the school generator and its components¶
- Date: 2026-10-26 · Decided by: the controller under D-098, pending the owner's reading · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P3; docs: doe-school.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p3(536f242). - Built:
SchoolGenerator(Lod.Generators.Doe.School, aMultiStoreyPlanGeneratoroverDoeProgramMap, ADR-015): the primary school (25 zones, one storey) and the secondary school (25/21 zones) from their records throughGridPlanLayout; the defaults (SchoolParameters.For) reproduce the records vertex for vertex. The structure (classroom wings west of a spine; at least three source wings; each storey on the grid of the one below and standing on it) is found from cells and program keys, never from names (D-061), with the generator's tolerances (template cache keyed by school and distance tolerance). Grasshopper: DOE Primary School (4f67ab4c-7c7e-4a27-afbe-8e76cd48097d) and DOE Secondary School (913bc6e4-ba40-4f81-949d-fc7d616ee1db) onDoeGeneratorComponent, one per record; smoke specdoe-schools. - Parameters:
WingCount(minimum 2: the source's first and last wing; each wing between them repeats the source's middle wing with the court after it; copies are_Wing{n}),Length(S3 by slack; the main corridor's column fixed),CorridorWidth(the wing corridors only) andClassroomDepth(every row; both at leastMinimumFixedBand),FloorHeight. No plate-depth parameter: the depth follows from the wings and the source's courts (routine choice; "plate" in note §4 read as plate length). - Spine (S3 as amended, D-164): the spine's cuts move through
GridAxis.Resize: by slack within each end-wing row, kept in the first and last court, by slack within each middle-wing row and then along the span of every middle-wing copy; with two wings the source's middle is resized onto the single court. Every wing adjoins, zone by zone, the spine zones its source wing adjoins (pinned in T5 at 2, 3, and 6 wings; review round 1 found the first rule detached repeated wings from the main corridor, and it was replaced). Valid ranges:Length≥ 18 m (primary) / 19 m (secondary);CorridorWidth≥ 0.5 m;ClassroomDepth≥ 2 m (secondary at every count; primary from three wings, 0.5 m at two); no upper limits; every limit at least ±20 % of its default. - Gaps: G4 for the secondary school's gym, auxiliary gym, and auditorium (4 m storey height, ground storey only); walls against them on the first floor excluded by G4 (D-162).
- Acceptance: T1–T6 pass for both records. T4 runs on the generator path with one program per key (
programGlazingviaHarnessFixtures.KeyGlazing): totals, per key, and per zone within 1 % (noglazingRatioDifferences); the primary school's 13.38 m² of skylights pass per zone, per key, and in total. Perimeter Core at 4.57 m isPlanTooNarrowon the secondary school's first floor (a 9 m wide mechanical room, D-087, asserted); T6 and the smoke matrix run it at 4.0 m there. - Checks:
scripts/verify.ps1on536f242: VERIFY PASSED, 0 warnings: Core 920, Generators 1438 (1 skipped), Integration 3184, Export 3106 (1 skipped), ProgramJson 175, Simulation 205 (14 skipped), ConstructionJson 286, IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. Full Rhino smoke on6598d84: 20 specs, no failed check (doe-schools13 scenarios, 66 Validate True, 18 checks). On the merge3149f94: VERIFY PASSED (Generators 1438, 1 skipped) and the full Rhino smoke check, 20 specs with no failed check. A second agent reviewed the stage in four rounds; every finding is applied. Commit4954e81's subject says "without gap exclusions"; the change leaves the gap-excluded zones out (history not rewritten). - Pending: icons for the two components (stage C); A1–A3; for a person in Rhino 8, the toolbar group and the viewport previews.
D-166 — DOE geometry stage P5b: the tower hotel and hospital generators and their components¶
- Date: 2026-10-26 · Decided by: the controller under D-098, pending the owner's reading · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P5b; ADR-025, ADR-026, ADR-015; docs: doe-tower-hotel.md, doe-hospital.md, doe-components.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p5b(7339a4c); the smoke-test checklist, the smoke README, andcommon.pymerged by keeping both stages' appended entries. TowerHotel(large hotel, three plans of 7 zones):Storeys(≥ 3; the typical plan stands for Storeys − 2 throughStoreyMultiplier),GuestRoomsPerRow(the typical floor's two repeated rooms' multiplier, ≥ 2; the top floor keeps ×9),Length,Depth,FloorHeight(G5: the typical storey's 3.048 m). Along x only the run of copies is flexible, so one source bay (3.657 m) per added room follows the count on the typical floor (within the source's own 0.027 m², changing by 0.0067 m² per room; the top storey's copy space grows while its room stays ×9); along y the typical corridor band is fixed. Gaps G3, G5.Hospital(five plans, 9/11/13/13/8 zones, one plate with a copy space each):RoomMultipliers(17, inMultipliedRoomsorder, each ≥ 2, read-only),PlateLength,PlateDepth,CorridorWidth(the narrowest corridor leg, 4.572 m; every corridor band, a corridor rectangle at least 3 × as long as it is wide, scales with it; at leastMinimumFixedBand),FloorHeight. Gap G3.OR_NurseSat_Flr_2_Wall_1_Eastis drawn outdoors (D-163).- Counts change multipliers, not the drawing (ADR-026). Limits (S3 as amended, D-164), found by bisecting the generator's own check and cross-checked by hand: hotel
Length≥ 30.384 m,Depth≥ 7.657 m; hospitalPlateLength≥ 35.908 m,PlateDepth≥ 24.192 m,CorridorWidth0.5–10.607 m; every limit at least ±20 % of its default; T5 tests the hospital'sPlateLengthat its limit in place of 0.5×. - Acceptance: T1–T6 pass for both records. G5 excludes exactly the hotel's ground floor height, the elevations above it, and the ground storey's windows and their totals; each is checked against what G5 makes of it (+0.9144 m; × 3.048/3.9624). T4 runs one program per key; the hospital's six
glazingRatioDifferencesare reported, 210 per-zone window findings pass. P5: every total scales with the multipliers; Semantic Merge and Conditioned Merge never merge across multipliers and do merge zones of equal key and multiplier; Perimeter Core, Single Zone per Floor, and both single-zone aggregators areNotSupported. - Components: DOE Large Hotel
8b61c4c7-2d19-4a1a-b83d-cea9254b634a, DOE Hospitalda2d1dab-1994-420f-87d6-8cae8ea567a4(one per family, onDoeGeneratorComponent; the room multipliers are a list input whose description names each room by index); smoke specdoe-multiplied-rooms. - Routine choices: shared
Doe/MultipliedRooms/RecordGrid; the hospital's leg aspect of 3; parameter equality compares the multipliers item by item; zone IDs are the source's; the source bay width and ground height read from the record. - Open, for the owner: D-045 models the hotel's ×4 floor at storey 3; the reference draws storey 2. D-045 is unchanged (as D-164).
- Checks:
scripts/verify.ps1on7339a4c: VERIFY PASSED, 0 warnings (Generators 1300, 1 skipped; Integration 3270). On the mergee139a48: VERIFY PASSED, 0 warnings: Core 920, Generators 1577 (1 skipped), Integration 3270, Export 3106 (1 skipped), ConstructionJson 286, ProgramJson 175, Simulation 205 (14 skipped), IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. Rhino smoke on37a3d3e: 20 specs, no failed check (doe-multiplied-rooms12 scenarios, 31 checks). The full Rhino smoke check onf19b6eb(P5b and P6 merged): 22 specs, no failed check. A second agent reviewed the stage in two rounds; every finding is applied. - Pending: A1–A3; icons (stage C); for a person in Rhino 8, the toolbar, the viewport previews, and the Convert2BEM
SelfAdjacentoutput.
D-167 — DOE geometry stage P6: the outpatient generator and its component¶
- Date: 2026-10-26 · Decided by: the controller under D-098, pending the owner's reading · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P6; docs: doe-outpatient.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p6(f149455); the smoke-test checklist and the smoke README merged by keeping both stages' appended entries. - Built:
OutpatientGenerator(Lod.Generators.Doe.Outpatient): three storeys of 59/41/26 zones (the note's 59/37/22 plus the G4 stair and lift copies) from theoutpatientrecord, one plan per storey throughMultiStoreyPlanGeneratorandGridPlanLayout, presets byDoeProgramMap(32 keys). Parameters:PlateLength,PlateDepth,CorridorWidth,FloorHeight,OrientationDegrees; no counts.OutpatientGenerator.SizeLimits. Grasshopper: DOE Outpatient (5d0c7a3e-9b41-4f6e-a8d2-3c1e7b590f64) onDoeGeneratorComponent; smoke specdoe-outpatient. - S3 classes: a corridor strip is a
Corridor-type zone of one cell rectangle at least 3 times as long as wide (17 strips); its widths are fixed bands scaled byCorridorWidthover 1.8288 m (the most common strip width), each at leastMinimumFixedBand; other intervals under 1 m are fixed; the rest (17 along x, 9 along y) share the plate by slack (D-164). - Limits (at the defaults): plate at least 34.9832 × 26.3736 m; corridors 0.6–3.7538 m (the narrowest strip, the 1.524 m IT hall, reaches 0.5 m at 0.6 m); all reach ±20 %; T5 tests the 0.5× plates at their limits. Review found the first corridor rule had no lower limit (1e-6 m gave
OverlappingZones); fixed. - Gaps and artifacts: G4 stairs and lift drawn one zone per storey, excluded by their gap (T2 16 quantities, T4 20). The source's self-referencing walls between ×1 zones (398 of 448
Surfacewalls) and the reception hall's 26.52 m of south wall are interzone walls (D-162). - T4 (D-163): one program per key; totals and 31 keys within 1 %; the 38 listed zone ratio differences reported, not failed.
- Source facts against the note: the outlines have 18 and 14 vertices (the upper storeys include the stair shafts), not 18 and 12. Perimeter Core needs a depth under half the east bay's north–south extent (1.0668 m at the defaults, 0.8048 m at the shortest plate); T6 runs it at 1 m and 0.8 m, and 4.57 m is
PlanTooNarrow(asserted). - Routine choices:
MinimumCorridorAspect = 3; the default corridor width is the most common strip width; the G4 copies keep the ground storey's IDs; one component for the family; the smoke spec's GUID kept local to the spec. - Checks:
scripts/verify.ps1onf149455: VERIFY PASSED, 0 warnings (Generators 1215, 1 skipped; Integration 3356). On the mergef19b6eb: VERIFY PASSED, 0 warnings: Core 920, Generators 1631 (1 skipped), Integration 3442, Export 3106 (1 skipped), ConstructionJson 286, ProgramJson 175, Simulation 205 (14 skipped), IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. Rhino smoke on3adb0de: 20 specs, no failed check (doe-outpatient11 scenarios, 16 checks). The full Rhino smoke check onf19b6eb(P5b and P6 merged): 22 specs, no failed check. A second agent reviewed the stage in two rounds; every finding is applied. The fixed-band tests were not written test-first (the builder's note). - Pending: A1–A3; the icon (stage C); for a person in Rhino 8, the toolbar and the viewport.
D-168 — DOE geometry stage P2: the single-storey generators and their components¶
- Date: 2026-10-26 · Decided by: the controller under D-098, pending the owner's reading · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P2; docs: doe-single-storey.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p2(6bfb0ba); the smoke-test checklist, the smoke README, andcommon.pymerged by keeping both sides' appended entries. - Built:
RestaurantGenerator(full-service, quick-service),StoreRowGenerator(strip mall),RetailBoxGenerator(stand-alone retail),SupermarketGenerator,WarehouseGenerator, each aPlanGeneratoron its layout record's grid with aDoeProgramMapand itsProgramKeys; parameter records withDefaultread from the record (rounded to its 1e-6 m,RecordDecimals, tied toextract.py); provenanceRecord,Gaps,Source(reference file and SHA-256;DoeLayoutRecords.ReferenceRole). Grasshopper: one component per record onDoeGeneratorComponent: DOE Full-Service Restaurantb4ca8365-a4dc-4af7-a6b5-0fc68cdb37a9, DOE Quick-Service Restaurantcf72627b-3035-4f91-858c-f51772989c07, DOE Strip Mall94be46f9-90c9-49ec-8104-8d572004856e, DOE Stand-alone Retail1bf76d74-0917-4acc-8d9c-e0eaa36adcbe, DOE Supermarket2f12351b-adfd-40aa-a763-be4394bd814b, DOE Warehouse814d27a4-8483-4aba-bf78-2bf4daa62f46; smoke specdoe-single-storey. - Parameters (two deviations from note §4, accepted by the controller because §4 as written contradicts its own T5): restaurant
Length,DiningDepth,KitchenDepth, the plate depth their sum, no kitchen-smaller rule (with the plate depth fixed, T5's 2× quick-service kitchen, 15.24 m, would fill the 15.24 m plate); warehouseLength,Depth,OfficeDepth,FineStorageDepth, the office width flexible (a fixed 25.91 m office cannot fit T5's 22.86 m half plate). Also: strip mallDepth,LargeStoreWidth,SmallStoreWidth,Stores(runs of size, tenancy, count, west to east; IDsLGstore<n>/SMstore<n>in row order; in Grasshopper three lists, sizes read in any case); stand-alone retailLength,Depth,BackSpaceDepth,FrontDepth(one band, the entry its source half),EntryWidth(centred); supermarketLength,Depth,StorageDepth,PreparationDepth. - S3 (D-164): bands a parameter sets at least
MinimumFixedBand; flexible intervals share a change by slack; a band changed past its maximum is named in its error. Every size reaches ±20 % of its default and 0.5× and 2× are valid for all six records; the range table in the family doc gives validity limits, not plausibility (the warehouse is valid down to a 2 m length with a 1 m office). The amended rule changed the warehouse office's and the supermarket's flexible shares (T5 expectations updated). - Gaps: G2 restaurants (flat roof at the zone top); G4 warehouse (office to the storey height, fine-storage floor above it dropped; D-161, D-162).
- Acceptance: T1 (elevation and multiplier from a
Stackof the plan), T2, T3; T4 on the generators' path withHarnessFixtures.KeyGlazing(totals and per key within 1 %, every zone; no ratio differences in the six records); T5 (0.5×, 1×, 2×, each limit and just beyond, bands at 0.5 m and just below, strip-mall counts at 1 and 2×; every zone's area and part height); T6 (7 simplifiers × 4 aggregators, canonical and two rotated placements; determinism; snapshots). - Routine choices: one component per record; shared code in
Doe/SingleStorey/(source and tests); a null or unknown store run, lists of different lengths, and a size other than Large/Small areInvalidParameter; the strip mall lists all three tenancy keys; T6 inLod.Generators.Testswith one floor entry. - Checks:
scripts/verify.ps1onc06d5a3: VERIFY PASSED, 0 warnings (Generators 1910, 1 skipped). On the merge011edbc: VERIFY PASSED, 0 warnings: Core 920, Generators 2380 (1 skipped), Integration 3442, Export 3106 (1 skipped), ConstructionJson 286, ProgramJson 175, Simulation 205 (14 skipped), IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. Rhino smoke onc06d5a3: 20 specs, no failed check (doe-single-storey28 scenarios, 134 Validate True, 41 checks). The full Rhino smoke check on the merge011edbc: 23 specs, no failed check. A second agent reviewed the stage in four rounds; every finding is applied. - Pending: icons (stage C); for a person in Rhino 8, the toolbar group and the viewport previews; A1–A3.
D-169 — DOE geometry stage P4: the corridor hotel generator of the small hotel, its bay axis, and the DOE Small Hotel component¶
- Date: 2026-10-26 · Decided by: the controller under D-098, pending the owner's reading · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage P4; docs: doe-corridor-hotel.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p4(824a63b); the smoke-test checklist, the smoke README, andcommon.pymerged by keeping both sides' appended entries. - Built:
CorridorHotelGenerator(one plan per storey, ADR-015) andCorridorHotelParameters, whoseDefaultreproducessmall-hotel(19/16/16/16 zones, 54.8646 × 18.2886 m, 4 storeys of 2.7432 m).CorridorHotelTemplatereads the structure from the record's cells and keys (D-061), with the generator's tolerances: bays are the x intervals under the typical storey's northGuestRoomzones (11 of 3.9624 m), each its own flexible interval (bay cuts no storey uses split the run of three), so S3 by slack keeps them equal; the stairs, storage, lift lobby, and lift (11.2782 m) are fixed.GridAxis.Maptakes an optional index remap for an axis of another interval count (without it the equal-count rule holds unchanged). Grasshopper: DOE Small Hotel (7b31e279-617d-4a06-a7c9-e8824b7ae396) onDoeGeneratorComponent; smoke specdoe-hotel. - Parameters and limits (S3 as amended, D-164):
Length≥ 11.2782 m plus 1 m per bay (22.2782 m at 11 bays);RoomDepth(the rows' mean, source ratio kept) ≥ 0.500018 m andCorridorWidth≥ 0.5 m (MinimumFixedBand);GuestRoomBays9 to 43 at the default length, the change added to or removed from the run of three bays no storey divides;StoreyCount≥ 2, storeys above the record repeat its top storey;FloorHeight> 0. Every size parameter is valid at 0.5× and 2× and reaches ±20 %. - Deviation from S3's wording: S3 lists the room bay as a fixed dimension; P4 makes the bays flexible and the service intervals fixed, the only reading that keeps both
LengthandGuestRoomBaysas parameters (accepted by the controller). Review round 1 found that a run of three bays as one interval would part the bays under the amended S3; one interval per bay fixed it. - G5: one floor height, the typical storey's 2.7432 m (source ground 3.3528 m). Excluded and checked against their G5 values: the ground storey's floor height (the upper storeys' are compared), the elevations above the ground (−0.6096 m), the ground storey's windows (× 2.7432/3.3528), and the window totals in all and per key where the ground storey glazes (each key's source total less (1 − 2.7432/3.3528) of its ground share, resting on a test that each key's window area is its ratio times its zones' true outdoor walls).
- T4 (D-163): one program per key through
DoeProgramMap; totals per orientation and per key within 1 %. Through a program map the per-zone windows ofGuestRoom(0.124 ground and 0.151 upper, N and S, 0.191 S for 206–208, against the key's 0.149 N and 0.158 S) andCorridor(E/W) are not the source's: the answer key's 43glazingRatioDifferencesare reported and the list is checked exact. - Routine choices: bay count at a fixed length (at 22 bays the bays are 1.98 m); copy IDs drop the source floor suffix and take
_Flr{n}(Corridor_Flr5; guest rooms keep their room number,GuestRoom401_Flr5); minimum counts 2 storeys and 9 bays; one component for the family's one record;SizeLimits(the base class has aLimits). - Checks:
scripts/verify.ps1on824a63b: VERIFY PASSED, 0 warnings (Generators 1231, 1 skipped; Integration 3326). On the mergebc679c7: VERIFY PASSED, 0 warnings: Core 920, Generators 2450 (1 skipped), Integration 3584, Export 3106 (1 skipped), ProgramJson 175, Simulation 205 (14 skipped), ConstructionJson 286, IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. Rhino smoke on266c674: 20 specs, no failed check (doe-hotel11 scenarios, 49 Validate True, 19 checks). The full Rhino smoke check on the mergebc679c7: 24 specs, no failed check. A second agent reviewed the stage in two rounds; every finding is applied. The merge1534628ande25ca56on the stage branch are not green on their own;f36363dand later are. - Pending: the icon (stage C); for a person in Rhino 8, the toolbar group and the viewport previews; A1–A3.
D-170 — DOE geometry stage C: the close-out, the icons of the DOE components, and the sync-docs pass¶
- Date: 2026-10-26 · Decided by: the controller under D-098, pending the owner's reading · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md, stage C (status now Done, pending the owner's reading); docs: doe-components.md, doe-layouts.md, pipeline.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-c(b973cac); the merged tree equals the stage tip. - Icons.
scripts/make_icons.pydraws 17 new icons, one per new class: DOE Program Map and the Program Map parameter (Program accent) and the 15 DOE generator components (Generate accent), each from its reference building and distinct from the others and from the six precedent generators (table in doe-components.md). The 69 existing icons are byte-identical; a rerun of the script reproduces all 86 byte for byte. - Sync-docs pass. README (version label 1.4.0, scope, layout, not yet released), AGENTS.md (one project-state bullet), the pipeline doc (panel rows including DOE Program Map, the DOE-generators bullet, open items, related documents), doe-layouts (the family table), doe-components (all 15 components with GUIDs, the icons), the family docs (checked against D-161 to D-169, gaps filled), ADR-012's status (skylights, D-162), and the smoke checklists. The public site follows at the next tag: export the tagged commit, import it in the docs repository, run its
sync-docs. - Small code items. The
GridAxis.Resizeshortest-length message leaves out the fixed length when an axis has no fixed interval (one pinned test changed, one added); the shared description text of the DOE components no longer cites a decision number (sync-docs, D-118). - Decision log. D-097 is marked "Refined by D-160" in its status (its text unchanged).
- Checks.
scripts/verify.ps1onb973cacand on the mergef7f8b59: VERIFY PASSED, 0 warnings: Core 920, Generators 2451 (1 skipped, A2), Integration 3584, Export 3106 (1 skipped), ConstructionJson 286, ProgramJson 175, Simulation 205 (14 skipped), IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. The full Rhino smoke check on6d4e331(plugin sources equal the final commit's): 24 specs, no failed check, every icon 24 × 24; the metadata dump lists 85 objects and no gap. A second agent reviewed the stage in two rounds; every finding is applied. - Open, for the owner. The reading of D-160 to D-170 and ADR-025, ADR-026; D-045's representative storey of a multiplied floor (D-164); for a person in Rhino 8: the toolbar and the icons as drawn, the viewport previews, the Convert2BEM
SelfAdjacentoutput, skylights in the viewport and in an EnergyPlus run; atlas inputs A1–A3 (A2 on the true-outdoor-wall basis, D-162, D-163).
D-171 — DOE geometry stage P7: the DOE program-map presets¶
- Date: 2026-10-26 · Decided by: Cheng Xuan Li (the request, and the values: illustrative example presets with the source's glazing per key; design note §9); the controller under D-098 (the Service fallback, the source glazing of keys under a gap, their realisation), pending the owner's reading · Status: Accepted by the owner's approval of PR #1 (D-176)
- Design note: 2026-10-25-doe-geometry-generators.md §9, stage P7; ADR-025 (consequence amended); docs: doe-components.md, doe-layouts.md. Merged into
feature/doe-geomwith--no-fffromfeature/doe-geom-p7(b1e0774); the merged tree equals the stage tip. - Why: the owner accepts the program map (S4) as the current design and asks for a ready preset for every DOE plan generator, so each can be used and tested without assembling a map by hand.
- Shipped glazing.
extract.pywrites each record's per-key glazing into the layout record (programGlazing, record format version 2; answer keys byte-identical;--checkpasses): every key once, in ordinal order, its WWR N/E/S/W and skylight ratio, and its basis:true_outdoor_wall(the answer key'sprogramGlazing, D-162, D-163),office_reference(the offices'officeGlazingon every orientation),gap_source_wall(every zone of the key under a gap: the same rule with the gap zones and orientations counted and the skylight over the source roof, from the source only), orinterior(no counted outdoor wall or roof, no ratio; 4 hospital and 7 outpatient keys). The loader rejects an unknown basis, aninteriorkey with a ratio, any other key without one, and agap_source_wallkey with a zone outside a gap. - Built:
DoeProgramPresets.For(DoeBuildingType)(cached;Createbuilds anew): one preset per key of the generator'sProgramKeys, the illustrative example preset of the key's space type (residential examples for Corridor and Stair) renamed to the key, with the record's glazing. A key whose space type has no example preset (Mechanical, Other) takes the example Service preset as an any-program preset named<key> (Example Service), recorded by the plan asAnyProgramPreset.<key>. Keys under a gap (warehouse Fine and Office, secondary school Auditorium, Gym, Gym - audience, outpatient Stair) keep the source's glazing of their zones, so their window area differs from the source's by what the gap changes, described per record (the warehouse office drawn to the 8.534 m storey height doubles its walls and windows, S 11.15 to 22.31 m², W 6.51 to 13.01 m²; the secondary school's 8 m tall zones at 4 m halve theirs; the outpatient stairs and lift drawn per storey); the harness's T4 exclusions are unchanged. Each map carries one shared provenance source (DoeProgramPresets: record, reference SHA-256, loads, glazing, Service fallback, gap glazing). Service-fallback keys per record: small, medium, large office 2, 3, 5; restaurants 0, 0; strip mall 0; stand-alone retail 1; supermarket 5; warehouse 2; primary, secondary school 2, 3; small hotel 9; large hotel 7; hospital 12; outpatient 22. - Components: fifteen output-only DOE … Programs in panel 1 Program Presets (secondary exposure), one per DOE generator component, outputs Program Map, Keys, Presets, with remarks that the loads are illustrative example values and the glazing is the source's for each key's zones, naming the fallback keys, and naming the keys under a gap with the record's gap change; icons drawn by
make_icons.py, the 86 existing icons byte-identical; GUIDs in doe-components.md. - Acceptance: for every record the map's keys are the generator's, every preset has its space type or is the fallback, its glazing equals the record's and the answer key's, the defaults validate, every supported simplifier × aggregator validates; T4 runs through the shipped map in every family's acceptance tests with the same G4 and G5 exclusions; two maps built apart describe alike; the stacked glazing totals are pinned; no snapshot changed.
- Routine choices: the fallback name suffix; the shared provenance source; office keys take the reference ratio on every orientation; the
interiorbasis (a key with no outdoor surface); nicknames<generator nickname>P. - Checks:
scripts/verify.ps1onf74e867and on the merge: VERIFY PASSED, 0 warnings: Core 920, Generators 3051 (1 skipped), Integration 3584, Export 3106 (1 skipped), ConstructionJson 286, ProgramJson 175, Simulation 205 (14 skipped), IdfBatchRunner 88 (3 skipped), TestSuiteGenerator 119. Full Rhino smoke onf74e867(the merged code): 25 specs, no failed check (doe-presets93 checks, among them a preset replaced and rebuilt with DOE Program Map). A second agent reviewed the stage in three rounds; every finding is applied. - Pending: A1–A3 (the loads stay illustrative until then); for a person in Rhino 8, the toolbar group, the icons as drawn, and the viewport.
D-172 — Atlas release 2.0.0: program JSON 5.4.0 and constructions 4.3.0 / 3.1.0 only, daylighting controls carried as given, written only on request¶
- Date: 2026-10-28 · Decided by: Cheng Xuan Li (the request of 2026-10-28: move BEMGen to atlas release 2.0.0, apply the atlas's handoff note, fixtures exported from the release); the controller (the compatibility decision and the realisation), pending the owner's reading · Status: Provisional; refined by D-173 and D-174 · Supersedes in part: D-126 (the program JSON 2.0.0 reader), D-145 (the packet v7 pin; its reference material stays) · ADR-027, design note
- Compatibility. BEMGen reads only the release 2.0.0 contracts: program JSON 5.4.0 (
sourceandfilled), detailed constructions 4.3.0, unified 3.1.0. Program JSON 2.0.0 and the packet v7 versions (detailed 3.0.0, unified 2.0.0) are refused with a message to export again. Why: release 2.0.0 is the only atlas release and its exporters refuse 1.x; D-064 waives backward compatibility; D-126 and D-145 retired earlier readers the same way; the program contract changed shape; the construction change is additive, and the 19 packet v7 contexts re-exported from 2.0.0 have equal values (within 2e-9 relative). - Program JSON 5.4.0. Both versions; Climate Zone and Context inputs appended to Program JSON (GUID unchanged) resolve values by climate and loads by
applies_with; WWR has no default, so the JSON'sfenestrationgives the glazing when no input does; the source version's unrecorded values are left out or take BEMGen's defaults withProgramJsonValueNotRecorded; exterior lighting, building-level water heating, humidity setpoints, water-use gains, and building services are listed in Left Out. - Handoff item 1, daylighting.
lighting_controlsbecomesZoneProgram.Daylightingas given (never recomputed from geometry; source zero andunknowngive none; floor fraction and zone counts are provenance); a merge conserves the controlled lighting power. Convert2IDF can write one reference point per glazed zone (IdfOptions.WriteDaylighting) but leaves it out by default: with the controls written, the opt-in EnergyPlus end-to-end test failed the hourly lighting pairing check (D-157, GLOBAL.md scientific rule 1) in every zoning, so the handoff note conflicts with the owner's pairing rule. The owner decides whether daylight dimming is measured as a geometry effect or held fixed. - Handoff item 2, IDs. BEMGen stores no 1.x or 2.0.0 atlas ID: a scan of
feature/doe-geomfound none of the 11,507id-map.jsonkeys. The v0.x IDs in tests and the synthetic glazing fixture were re-keyed to the 2.0.0 IDs of the same program or construction context by structural matching; the DOE presets hold no atlas IDs. - Handoff item 3, alternates. Read as information (
DetailedConstructionDocument.Alternates, Roles lines), never assigned. - Water mains. A hot-water inlet
CorrelationFromWeatherFileisWaterTemperatures.InletFromWeatherFile, written withSite:WaterMainsTemperature. - Atlas defects reported: the IECC construction sets carry mass-entry
evidence_ids the shared mass contract forbids, and file the ground storey core's F-factor floor underInteriorFloor; both make BEMGen refuse them. Minor: the 5.4.0 schema's daylighting description predates L-1; some fitted values moved by one ULP; alternate area shares are rounded to six decimals. - Open: the owner's decision on daylighting in the IDF; rebuilding
examples/program-json-office.ghin Rhino; Convert2BEM daylighting output; the IECC zone design-flow infiltration; PNNL alternates as geometry; building services and source model constructions; the merge and the push.
D-173 — Atlas release 2.0.0 as corrected in place: shared mass contract 1.1.0, the IECC ground slabs under GroundFloor, fixtures re-pinned to atlas f617b30b¶
- Date: 2026-10-28 · Decided by: Cheng Xuan Li (the request of 2026-10-28: take the corrected release); the controller (the realisation), pending the owner's reading · Status: Provisional; refined by D-174 · Refines: D-172 (its fixture pin and the two IECC defects it reported) · ADR-027 (amended), design note §3–§5
- Why a new entry. D-172's report led the atlas to correct release 2.0.0 in place (atlas PR #28, merge
f617b30b1ca1552364af648a82797d2b39854c01; its handoff note section 4 and ADR 0046 section 11). The log's rule is not to rewrite a past entry, so this one records what changed; D-172's status says it is refined here. - Mass contract 1.1.0.
construction-mass-accounting-v1.1.schema.json(urn:archetype-atlas:construction-mass-accounting:1.1.0, 1.0.0 with the optional mass-entryevidence_idthat detailed 4.3.0 and unified 3.1.0 embed) replaces 1.0.0 as the embedded schemaLod.ConstructionJsonchecks themass_accountingsection against, byte for byte. Nothing BEMGen reads needs 1.0.0, so it is not kept beside it. The field is still typed by the contract (a non-empty string; a test pins an empty one asConstructionJsonSchemaViolation); it is provenance and is not resolved. - Program JSON 5.4.0 schema. The atlas changed only the
lighting_controls.daylightingdescription ("applied as given", L-1); the new file is embedded insrc/Lod.ProgramJson/Schemas/and copied to the fixtures, byte for byte. The seven program exports are byte-identical to D-172's. - IECC ground-floor erratum. In the five affected IECC types (Medium and Small Office, Outpatient, Primary and Secondary School) of every edition, the adiabatic F-factor slabs with
PerimeterExposed0 moved fromInteriorFloor(with aGroundcondition) to theGroundFloorbuilding default, their selection decision rulesource_boundary_correction; 30 set IDs changed. Of the fixture set and program IDs onlymedium-office-iecc-2021-5B's is in the release'sid-map.json(iecc-2021.medium-office.construction-package.8e0c58to…a8a7b6); the exporter's list takes the new ID. BEMGen had no workaround to remove: it refused the old documents and its check that onlyGroundFloorandFoundationWallassignments may name a ground method stays, now pinned by a synthetic test on the 90.1 office, since the IECC fixture no longer trips it. A test pinsCore_bot_ZN_5_Floorin theGroundFloorassignment with its ground method and noInteriorFloorassignment with one; the F-factor method takes the exposed perimeter from BEMGen's geometry. - What the IECC fixture now does. Both documents of
medium-office-iecc-2021-5Bare read with full closure and one diagnostic, the warningConstructionJsonInfiltrationMethodNotSupported; the unified set refuses conversion (AssignmentUnavailable) for its source design-flow infiltration alone, and a test shows the same document with the 90.1 office's building-level infiltration converts without a warning. The atlas answered that this infiltration is the source's method, recorded faithfully, and that a building-level equivalent belongs to its deferred IECC unified representation, so BEMGen keeps the refusal (ADR-027 decision 6). The fixture joins the full-closure foundation test (42 documents); it stays out ofPacket.Names, whose building-scope sets convert, asPacket.NotConvertible(wasRefused). - Not changed (atlas: not defects). Absorptances such as 0.44999999999999996 are IEEE area-weighted means (the two tests that assert them are unchanged); alternate
area_sharevalues are rounded to six decimals and BEMGen does not check their sum. - Fixtures.
python scripts/atlas-fixtures/export_fixtures.py --atlas <checkout>re-exported every fixture atf617b30b(release manifest SHA-256b7ce92a1694a85b42746bdc57ffa0ed1f21f11384561184af078d95a4865e40e, checked against the file); the manifests,PacketTests,SchemaTests, both fixture READMEs, and the design note carry the new pins. Only the two IECC documents differ in value; the other 40 construction documents are equal as JSON, and 15 of them differ in bytes only by key order. - Exporter pins the hash seed. The first
--checkafter the re-export failed on 17 example documents and the manifest, all equal as JSON: the order of some keys in the atlas's exports follows Python's string hashing (one export run underPYTHONHASHSEED0, 1, 2, 0 gave the same bytes for the two runs with seed 0 and other bytes for 1 and 2), so D-172's byte-for-byte check held only by chance.export_fixtures.pynow runs the atlas withPYTHONHASHSEED=0; the comparison stays byte for byte. Reported to the atlas as a determinism finding; BEMGen does not change the atlas. - Unchanged:
IdfOptions.WriteDaylightingstays off by default (the owner's decision on D-157 pending, D-172). - Checks: the new tests failed first for the expected reason (the IECC mass entries'
evidence_idrefused under 1.0.0, and the 1.1.0 schema not embedded).export_fixtures.py --checkwith the atlas atf617b30b: CHECK PASSED, run twice (the second from a shell withPYTHONHASHSEED=7).scripts/verify.ps1on the working tree of the commit: VERIFY PASSED, 0 warnings: Core 938, Generators 3051 (1 skipped), Integration 3584, Export 3135 (1 skipped), ConstructionJson 311, ProgramJson 200, Simulation 247 (14 skipped), IdfBatchRunner 103 (3 skipped), TestSuiteGenerator 119.python scripts/docs-export/developer_pages.py --check: 94 pages checked. No Rhino smoke run. - Open: as D-172, less the two IECC defects: the owner's decision on daylighting in the IDF; rebuilding
examples/program-json-office.ghin Rhino and the smoke checklist's Construction JSON line for the IECC set; the IECC zone design-flow infiltration; PNNL alternates as geometry; building services and source model constructions; the merge and the push.
D-174 — No daylight dimming in IDF runs, ever: the daylighting writer removed, the controls kept as source data¶
- Date: 2026-10-29 · Decided by: Cheng Xuan Li (the decision); the controller (the realisation), pending the owner's reading · Status: Accepted (the decision); the realisation Provisional; amended by D-233 (no dimming becomes the default, code-rule daylighting opt-in) · Answers: the daylighting question D-172 and D-173 left open · Refines: D-172, D-173 · ADR-027 (amended), design note §3, convert2idf.md
- Decision. The owner, 2026-10-29: "let's not include dimming for our idf runs ever". Daylight dimming is held fixed as part of the lighting load and is never measured as a geometry effect (GLOBAL.md scientific rules 1 and 4). The evaluation metrics' pairing check (D-157) stays as it is.
- Removed, so that "never" holds in the code.
IdfOptions.WriteDaylighting; the writer's per-zoneDaylighting:ReferencePointandDaylighting:Controls(IdfWriter.Daylighting, the recordsIdfDaylightingControlsandIdfDaylightingReferencePoint,IdfNameKind.Daylighting,IdfDefaults.DaylightingReferenceHeight); the run check's expected warningPartialDaylightingControl(ExpectedWarnings.Defaultis againZoneFloorAreaalone, so EnergyPlus's daylighting warning would fail a run); its row inscripts/eplus-check/README.md; and the export test that wrote the controls on request across the simplifier × aggregator matrix.IdfDefaults,IdfNames, andRunVerdict.csequal their state before D-172. No opt-in EnergyPlus end-to-end variant wrote daylighting, so none was removed. - Nothing made inert. No Grasshopper component had an input or output for the option (Convert2IDF never exposed it, D-172), so no component's parameters or GUID change.
- Kept. Program JSON still reads
lighting_controlsintoZoneProgram.Daylighting, and a merge or mix still conserves the controlled lighting power (D-172): source data and provenance, never written to an IDF. A building whose programs carry controls gets the header lineDaylighting: never written (D-174); n zone(s) with controls in their programsand the infoDaylightingNotWrittennaming the zones; a building without controls is written as before, so no snapshot changes. Convert2BEM does not output the controls either. - Tests.
IdfChecks(the parse-back checks every export test runs) now fails any IDF with a record whose class starts withDaylighting;AnIdfOfAProgramWithDaylightingControlsHasNoDaylightingObjectsconverts the 90.1 medium office program, whose zones carry controls, and finds no daylighting object;ADaylightingControlWarningIsOffTheListAndFailsTheRunreplaces D-172's test that accepted the warning. The new assertions failed first for the expected reasons (the old header and diagnostic text, the warning still on the list) before the writer was removed. - Docs. convert2idf.md (the daylighting section), program-json.md, program-presets.md §7, ADR-027 (decision 4 and the consequences), the design note, the smoke checklist and the
program-jsonsmoke spec, the eplus-check README, AGENTS.md. - Checks:
scripts/verify.ps1on the working tree of the commit: VERIFY PASSED, 0 warnings: Core 938, Generators 3051 (1 skipped), Integration 3584, Export 3108 (1 skipped; 27 fewer than D-173: the 28 cases of the removed opt-in theory, one new test), ConstructionJson 311, ProgramJson 200, Simulation 247 (14 skipped), IdfBatchRunner 103 (3 skipped), TestSuiteGenerator 119.python scripts/docs-export/developer_pages.py --check: the developer pages' links. No Rhino smoke run and no EnergyPlus run. - Open: for a person in Rhino 8, the smoke spec
program-jsonwith its new header check; whether Convert2BEM should ever output the controls (the decision names IDF runs; nothing is output today); the merge and the push of PR #2.
D-175 — Version 1.5.0: DOE reference geometry, native ground, the evaluation tools, and atlas release 2.0.0, for the geometric LOD study¶
- Date: 2026-10-29 · Decided by: Cheng Xuan Li (the request of 2026-10-29: "close everything up and ship a stable release" so that the geometric LOD study can start); the controller (the preparation) · Status: Accepted (the request); the release itself waits on the owner's tag, push, and publication
- Version. 1.5.0, prepared on
release/v1.5.0frommainat54f990d, a minor version as D-125 reasons: D-064 waives backward compatibility, and no component or parameter GUID changed or moved sincev1.4.0(every one of its 69 GUIDs is still in the same source file; 32 are new). It adds components and appends inputs; nothing is removed from a component. - What it merges since
v1.4.0(273 commits onmain): - Construction foundation v7 and native ground (D-145 to D-150, ADR-023): a construction set's ground floors take its own F-factor or layered ground, with ground temperatures from the weather file (F-factor) or 18 °C (layered); the warning
ExampleWindowSolarNotAbsorbed(D-147); turned plans with courts (D-148, D-149). - Evaluation tools (D-151 to D-159): test suites with construction sets paired by climate; the hourly ground heat transfer in the research outputs; the batch runner's progress, site status, and results folder (central schema version 7, hourly Parquet per site); the paired evaluation metrics and the timing pass. These are tools, not part of the plugin.
- DOE reference geometry generators (D-160 to D-171, ADR-025, ADR-026, PR #1): 11 generators for the 15 DOE commercial building types, 15 DOE components, DOE Program Map, 15 DOE … Programs presets (illustrative loads, the source's glazing per program key), glazing per orientation and skylights on every preset, and multiplied rooms with the
SelfAdjacentboundary. - Atlas release 2.0.0 contracts (D-172 to D-174, ADR-027, PR #2): Program JSON reads program JSON 5.4.0 (
filledandsource) with Climate Zone and Context; Construction JSON reads detailed 4.3.0 and unified 3.1.0 with the shared mass contract 1.1.0; hot water can take the weather file's water mains; daylight dimming is never written to an IDF (D-174). - Made in this preparation.
- The version in
Directory.Build.props; the 20 example definitions rebuilt with the 1.5.0 plugin (BEMGEN_EXAMPLES_WRITE=1),program-json-office.ghnow with the 5.4.0 filled export of the 90.1-2019 medium office. - The
construction-jsonsmoke spec gains the checklist's IECC line: the IECC 2021 medium office is read with only its infiltration warning, and Convert2IDF refuses it for its source design-flow infiltration. Its first run failed one check: Construction JSON also gave the warning "A detailed document: … connect the unified document of the same context" for this unified document, because the component showed it for every set that refuses assignment. The warning now shows only for a detailed document; the run then passed. The component's description now names unified 3.1.0 and detailed 4.3.0 (it still said 2.0.0 and 3.0.0). No GUID, input, or output changed. - The sync-docs pass (below).
- Checks on the release candidate:
- Examples. All 20 rebuilt with the 1.5.0 plugin; each reopened and solved with one Validate True and no warnings, errors, or exceptions, in the rebuild and in the full run below.
- Definitions saved with 1.4.0. The 20 example files as committed before the rebuild (saved with the 1.4.0 plugin) reopened with the 1.5.0 plugin: every archive read without a warning or error (no Grasshopper IO dialog), and 19 solved with one Validate True;
program-json-office.gh, holding program JSON 2.0.0, gave the errorProgramJsonVersionNotSupportedasking for an export from atlas release 2.0.0, as the release notes say. - Rhino smoke. All 25 specs of
scripts/rhino-smokein Rhino 8.25.25314.11001 on the final code: 1514 Validate True and none False, 911 spec checks passed and none failed (the IECC check andprogram-json's D-174 checks among them: no daylighting object, the headerDaylighting: never written (D-174)), no exception, missing component, or missing expected error; runtime errors only in the error scenarios and, in the DOE specs, on the component placed without a program map, as in their stage runs. scripts/verify.ps1on the candidate's tree: VERIFY PASSED, 0 warnings: Core 938, Generators 3051 (1 skipped), Integration 3584, Export 3108 (1 skipped), ConstructionJson 311, ProgramJson 200, Simulation 247 (14 opt-in skipped), IdfBatchRunner 103 (3 skipped), TestSuiteGenerator 119.- Fresh-clone gate. A clean clone of
release/v1.5.0at4efc1a8(D:\BEMGen-gate\v1.5.0) passedscripts/verify.ps1with 0 warnings: Core 938, Generators 3051 (1 skipped), Integration 3584, Export 3108 (1 skipped), ConstructionJson 311, ProgramJson 200, Simulation 247 (14 opt-in skipped), IdfBatchRunner 103 (3 skipped), TestSuiteGenerator 119. The commit that records this changes only this entry. - Sync-docs. Applied: README (version, the 1.5.0 paragraph), AGENTS.md (the Done line and the stage bullets' release state), the statuses of the plans of D-145 to D-174, the roadmap's progress log,
examples/README.md(the rebuilt Program JSON example), the smoke README (the IECC check), ADR-027 and the atlas design note (the example rebuilt), the Construction JSON description. Recorded, not done here: the public site still describes 1.4.0 (reference-source.jsonate6c073e); the docs export of the tagged commit, its import in the docs repository, and that repository's ownsync-docsfollow the tag. - Release notes.
- What opens differently. Definitions saved with 1.4.0 open with their saved values. Program JSON shows two new inputs, Climate Zone and Context, unset, and its WWR no longer has a default: without a WWR input the JSON's
fenestrationgives the glazing, else 0.4. A Program JSON or Construction JSON holding an older atlas text (program JSON 2.0.0, unified 2.0.0, detailed 3.0.0) now gives an error asking for a release 2.0.0 export; give it the new text. The preset components show the appended inputs WWR North, WWR East, WWR South, WWR West, and Skylight Ratio, unset. - What IDFs change. Every IDF with ground floors requests the hourly inside-face conduction of each ground surface and
Output:Surfaces:List, Details(D-151). With a construction set, ground floors take the set's own F-factor (GroundFCfactorMethod, ground temperatures from the Weather file, which is then required) or layered ground (18 °C) instead of the Example's ground floor. A program from Program JSON 5.4.0 changes its loads to the atlas's release 2.0.0 values, and a filled program's hot water takes the weather file's water mains (Site:WaterMainsTemperature). Presets with per-orientation glazing or skylights write those windows. An IDF written without these is unchanged. - Daylighting. Programs from the atlas may carry daylighting controls; Convert2IDF never writes them (no
Daylighting:Controlsor reference points; D-174): daylight dimming is held fixed as part of the lighting load. The IDF header says so and names the zones. - New: the 15 DOE generators and their presets; IECC construction sets are read but do not convert (their per-zone design-flow infiltration).
- What follows:
- the owner's tag
v1.5.0, push, and the release on the docs repository's GitHub Releases (D-118) with the files ofscripts/package-release.ps1built again from the tagged commit (the files built here are drafts, since the commit is not tagged); - the docs export of
v1.5.0, imported by the docs repository, which runs its ownsync-docs; - the geometric LOD study: simulation experiments with the runner and the evaluation metrics.
- For a person in Rhino 8: the toolbar and the 17 DOE icons as drawn, the viewport previews of the DOE generators, the canvas of Program JSON with its two new inputs, installing from the release files.
D-176 — The owner approves PR #1: the DOE geometry decisions D-160 to D-171 accepted¶
- Date: 2026-10-29 · Decided by: Cheng Xuan Li (merged PR #1 of the private repository on GitHub as
15f2e58, then "PR approved" and "Record and push") · Status: Accepted - What. The pull request of
feature/doe-geomintomain, whose test plan left one item open, the owner's reading of the provisional DOE decisions, is approved and merged; D-175 already records the merge and its release inv1.5.0. The approval is that reading: D-160 to D-171 change from Provisional to Accepted, with every choice the controller and the coordinating session made in them as written, and so do the statuses of ADR-025 (S1 to S10), ADR-026 (S8 and P5a's choices), the design note, and the nine DOE architecture pages. - Unchanged: the illustrative example loads of the DOE … Programs presets (D-171) stay illustrative; the owner's decisions O1 to O8 stay as the design note gives them. A later decision that overrules one of these choices supersedes it in a new entry.
- Not changed by this entry: code, tests, and the
v1.5.0tag, which stays at866da55.
D-177 — Sourced reference programs and explicit research comparison boundaries¶
- Date: 2026-10-28 · Decided by: Jimmy Wang ("Yes proceed I approve", following the proposed sourced-program, separated-energy and conditioning-preflight stage) · Status: Accepted; first-stage backend implemented on the feature branch, not merged or released
- What. Add the first source-provenance reference packet, a representability matrix and opt-in floor research/reporting APIs under ADR-028 and the stage plan. DOE 1.4_7.2 is the baseline and 1.3_5.0 remains selectable; office height variations must be explicit variants. Keep example defaults, production validation, simplifier behavior and existing sensible metric definitions unchanged.
- First-stage packet. The implementation starts with the actual new-2004 MidriseApartment Chicago/5A source to avoid inventing a detailed-office room/program allocation. Source hashes, use-specific schedules/loads, enabled controls, climate, expansion calendar and unsupported source features are explicit. This is not JFK weather selection or a claim of complete DOE building equivalence.
- Research boundary. Fixed-control experiments must check spatial conditioning/control identity and actual source assignment, not conditioned-area totals alone. Preserve separate production validation and unverified geometry/native/whole-building contracts. Report sensible, latent and total thermal loads, observed purchased energy, fixed-COP electricity and monetary cost separately; missing data remain unavailable. Default fixed-COP comparison is 3/3, not an equipment simulation.
- Limits. No DOE geometry-branch merge, duplicate generator, annual/batch simulation, push, PR, release or deployment is authorized by this stage. Shared-service/design-day limitations, detailed-office crosswalk and branch-wide glazing/Core dependencies stay explicit. Original-source versus departure-adjusted geometry remains separate; no historical glazing mismatch is hidden by widening tolerance.
- Numbering. D-160 to D-171 and ADR-025/026 are already used by the separately fetched DOE geometry branch, so this independent stage reserved D-172 and ADR-027 without adopting that branch. At the merge into
main(D-184), aftermain's D-172 to D-176 and ADR-027, these entries became D-177 to D-183 and ADR-028 to ADR-031; commit subjects on the branch keep the numbers of their time. - Verification. Release build: zero warnings/errors. Combined verification: 7,885 passed, 18 explicit real-engine/weather skips, zero failed. Eight new caller tests cover source programs through existing generation/IDF services, independent departure acknowledgements, writer-gate enforcement and full-year dated IDF schedule reconstruction for every represented source zone in both releases. Independent original-archive evaluation checked 54 zones, 150 loads and 227,760 schedule-hour values, with an altered-hour negative control rejected. A bounded independent Sol implementation review found no important or critical issues. No new annual simulation or measured energy accuracy follows from these checks.
- Calendar correction. Independent source inspection found ten explicit holidays and explicit daylight saving despite the weather-file flags being No. The implementation preserves the holidays and requires a separate acknowledgement of unsupported daylight-saving alignment; raw evidence coverage declares its subset. Shared elevator equipment remains explicitly unsupported.
- Native/manual and docs. The native Program JSON smoke launch encountered a Grasshopper duplicate-file conflict before scenarios. Native pass and manual viewport checks remain unestablished pending resolution; this is recorded separately from passing backend checks. Source usage, architecture links, ADR and roadmap updated; developer links checked. Public docs export/import and release follow only at the next approved tag.
D-178 — Opt-in source-program Grasshopper adapters and bounded native isolation¶
- Date: 2026-10-28 · Decided by: Jimmy Wang (authorized overnight continuation, source-program/research API usability and an isolated retry) · Status: Accepted implementation on
feature/reference-gh-stage2, not merged or released - What. Add DOE Reference Program as a thin adapter over the existing authenticated source catalog, with catalog-only discovery defaults, explicit original zone/WWR selection and independent geometry/DST acknowledgements. Append Require Floor Research Eligibility to Convert2IDF, index 11, default false. Existing GUIDs, input indices, illustrative defaults, production validation and source scientific behavior are preserved. See ADR-029 and the continuation plan.
- Reporting boundary. Comparison reporting stays backend-only because the existing graph has no parsed simulation-result reader/parameter path; this stage does not add a runner workflow. Source calendar, local-clock/DST, subset, multipliers and shared-service/design-day omissions remain visible.
- Verification. New selector tests witnessed 18 failures/4 passes before implementation, then 22 passes; complete ProgramJson suite 199 passes. Combined Release verification: 7,907 passed, 18 explicit installed-engine/weather skips, zero failed, zero build warnings/errors. A bounded independent Sol code review found no important/critical findings. Actual direct component instances and GH_Document solutions from the verified task build passed 62 native checks, including a baseline-version source-picker Office/corridor pair through existing generation and gated IDF, full-year gain/control/activity schedule reconstruction and floor-gate enforcement. This is narrower than normal startup registration or archive restoration.
- Native diagnosis. The laptop's discovery list contains installed BEMGen 1.1.0 and task build 1.4.0 with the same GHA filename and different hashes. Public API inspection showed the documented list-loading overload is not publicly available; it was not invoked through private reflection. Renaming only a task-owned GHA removed the filename dialog but still loaded 1.1.0, leading to missing Program JSON. This was retained as a failed isolation attempt. No installed plugin, persistent search path, global choice or security setting was changed; original Rhino/user documents were preserved. Normal discovery, viewport checks and current/synthesized previous-shape archive reopen remain open.
- Geometry checkpoint. Saved source-linked plans/data distinguish original DOE thermal-zone footprints from existing generated room layouts and new parameterized snapshots. Existing office/courtyard services are reused for probes; no DOE geometry branch merge or duplicate generator is adopted. Topology, reciprocal contacts, internal-window exclusion/exposure, orientation, source program/control allocation and vertical boundary/multiplier semantics require separate executable contracts. A floor control/mapping pass is not a spatial-gain or whole-building certificate.
- Close-out boundary. Version stays 1.4.0. Source docs and smoke inventory follow the adapter change; public export/import/release need a subsequent approved tag. No new annual/production-batch simulation, push, merge, publication or trust change is authorized by this decision.
D-179 — Spatial program research signatures and explicit air/boundary ledgers¶
- Date: 2026-10-28 · Decided by: Jimmy Wang (authorized overnight continuation and the accepted bounded planning ladder) · Status: Accepted on
feature/research-contract-stage3, not merged or released - What. Strengthen only the existing opt-in floor research gate with per-source normalized program signatures and configured hourly heat partitions. Compatible homogeneous splitting/merging remains possible; heterogeneous spatial redistribution fails with explicit source/target reasons. Production validation, aggregation, presets and central tolerances remain unchanged. See ADR-030, usage and the approved plan.
- Ledger boundary. Add explicit caller-provenanced source/derived air and boundary ledgers with role quantities that distinguish absent, unavailable and known zero, explicit mappings/departures and raw nullable volume differences. The API and conceptual fixtures are implemented; populated authenticated DOE-versus-derived ledgers are not established by those fixtures.
- Verification. Combined Release build and software verification: 7,961 passed, 18 explicit engine/weather skips, zero failed, zero build warnings/errors. Four real writer caller tests were observed failing before integration and passing afterward. Native results and final fixture verdicts remain in the external verification record, including retained unsuccessful attempts.
- Limits. Automatic people thermal partition, water/HVAC response, exact contact/window/orientation/construction identity and vertical equivalence remain unverified. No annual simulation, measured energy accuracy, merge, push, publication or installation change is implied. The software review found no important or critical source/test findings before this documentation entry; actual aggregate/native results remain in the external verification record.
D-180 — One authenticated MediumOffice evidence-ledger caller¶
- Date: 2026-10-28 · Decided by: Jimmy Wang (authorized bounded overnight source/derived ledger continuation) · Status: Implemented on
feature/mediumoffice-ledger-stage4, not merged or released - What. Authenticate the actual DOE 1.4_7.2 new2004 Chicago/5A archive, nested archive and IDF, and exact stored layout bytes at DOE branch
a527da5d0ffeb6de0d1c549dff2ab2568dde8738. Add a data-only extracted fixture and integration caller over existing Core ledger APIs; no production Core or generator changes. See plan and caller usage (scripts/research-ledgers/README.md). - Evidence and limits. Fifteen source occupied-role regions are supported by explicit People/thermostat references and three plenums by explicit ReturnPlenum references. Source geometric prism measurements are distinct from blank engine Volume fields. Sixty-five hybrid rooms retain measured cell enclosures separately from null physical occupied volumes and unavailable conditioning/boundary sets. Role placeholders and mapping gaps are explicit; no names/program-key classification or thermal allocation is inferred. Source and stored multipliers are retained without silent expansion. Basement-role absence is not measured zero. G1 is a declared representation convention, not a verified spatial crosswalk or 44.44% energy result. The internal source Building label's v1.3_5.0 text remains beside the authenticated v1.4_7.2 member identity; the original OSM bytes are not independently authenticated here.
- Verification. Seven initial extractor tests observed seven failures before implementation, then seven passes; final checks add nested/member byte-mutation tests for nine passes. Three integration tests exercise actual Core creation/comparison and retain unknowns, source couplings, mapping gaps and declared absorption. Bounded independent source review found no important or critical findings before this documentation entry. Corrected software-only Release verification: 7,964 passed, 18 explicit engine/weather skips, zero failed and zero build warnings/errors. Captured process exits and full logs are in the external verification record. The first aggregate mistakenly inherited
BEMGEN_ENERGYPLUS, enabling existing annual engine tests outside authorized scope; the verified task testhost and active children were stopped and all outputs/logs retained. The separate software-only retry explicitly cleared opt-ins. Those unintended runs establish no adopted scientific result. No push, merge, publication or installed-setting change.
D-181 — Fail-closed software-only verification after the inherited engine opt-in incident¶
- Date: 2026-10-28 · Decided by: Jimmy Wang (authorized process-local guard, fake-only regressions, generated data handoff and contract proposal) · Status: Implemented guard on
feature/software-only-verification-guard; default real runner unexecuted - What. Add the software-only launcher (
scripts/software-only/README.md) under the bounded plan. Clear process-local activation/filter/settings names and verify absence; accept only SoftwareOnly, exclude all 18 reviewed engine/installed-weather annotated methods, reject source/build/script fingerprint drift, and propagate integer failures/exceptions. No arbitrary filters or runner/settings arguments in the CLI, persistent environment edits, user-process actions or automatic policy refresh. - Evidence. Actual source activation variables are
BEMGEN_ENERGYPLUSandBEMGEN_WEATHER_3B; facts/theories and installed-weather reads explain the prior unfiltered gate. Incident evidence preserves only the first variable's inherited name, not its value or origin. End files do not establish successful annual completion. Keep all original logs, aborted outcomes and outputs; no energy results are adopted. - Verification boundary. Eleven harmless fake-runner checks pass; semantic RED, intermediate suffix failure and checkout-newline RED/GREEN are retained. No real build, .NET test assembly, engine or native UI is executed to test this guard. Independent static review found no important/critical issues in the trusted-checkout scope; it did not execute runners or independently reproduce the digest/results. This is an operational guard for the reviewed checkout, not a security sandbox or a fresh scientific/native verification.
- Separate artifacts. The exact-composition contract remains a proposal for coordinator review, with required invariants, negative fixtures and missing-evidence defaults; no new composer/preflight is implemented. The compact MediumOffice handoff contains generated geometry/ledger data only, not repository source or original models. No push, merge, publication or new production phase.
D-182 — Bounded opt-in exact-composition identity screening¶
- Date: 2026-10-28 · Decided by: Jimmy Wang (existing overnight research authorization; coordinator approved the bounded proposal) · Status: Accepted implementation on
feature/exact-composition-preflight, not merged or released - What. Add a default-off Core preflight over actual registered source/target floors under ADR-031 and the plan. Inspect complete identity mapping, geometry/contact/exposure/window/orientation and spatial program preservation separately; each check is Preserved, Failed or Unavailable. No new composer, placement-registration engine or production validation change.
- Evidence boundary. Caller ownership declarations establish only declared claim identity and retain evidence; unknown physical court/bay/stair ownership is never inferred. Full construction assignments are unavailable in these surface records. Supported geometry/program preservation does not become full exact-composition eligibility while required evidence is unavailable. Native and vertical/thermal accuracy claims remain outside scope.
- Guarded execution. Before implementation, inspect the narrow ledger caller, Integration initialization and dependencies, build properties and activation paths. Run exactly those three data-only tests through a reviewed narrow adapter with activations absent, positive selector AND all 18 mandatory exclusions and a matching source fingerprint: three passed, zero skipped, zero build warnings/errors. The default full-solution runner remains unexecuted. Source changes require an explicitly inspected policy refresh before any subsequent test run.
- Test-first. Six actual canonical courtyard generator identities at 0/37 degrees and independent mutations observed 23 assertion failures against a compiling unavailable-report scaffold before implementation. The first attempt's PowerShell native-stderr interruption is retained separately from the complete RED result. Final affected test/review outcomes remain in the external verification record.
- Verified checkpoint. The original 23 cases passed after implementation. An additional independent production-baseline status case observed RED, then the final 24 passed. Guarded affected software verification: Core 853, Generators 518, Integration 2,801; 4,172 passed, zero skipped/failed. A final test-only oracle confirms production conservation still passes during the spatial program swap and passed in the guarded 24-case rerun. Production source was unchanged after the affected run. One bounded independent static review found no important/critical findings before that last test-only assertion/final outcome wording. No fresh full-solution/native/energy verification is claimed.
- Limits. No engine execution, annual/production batch simulation, installed-setting change, user-process termination, push, merge or publication is authorized by this increment. Earlier incident artifacts remain preserved and are not adopted as scientific evidence.
D-183 — Combine current main, research checkpoints and the complete DOE geometry branch¶
- Date: 2026-10-28 · Decided by: Jimmy Wang (new branch and push; explicitly approved including the complete older DOE branch) · Status: Accepted scope; integrated and software-verified on the separate branch, no main update or release
- Inputs: current remote main
98aa569cdb16a797a336a62bf12f0cd9e3b57233, research chain throughfd9ac3b9c054b0c8d02bf1c47674853ff5f2a253, and DOE geometrya527da5d0ffeb6de0d1c549dff2ab2568dde8738. Main is already an ancestor of both tips. Preserve the research checkpoints once and retain both histories on a new integration branch; no main update or PR is authorized. The separate 5R1C branch is outside these selected inputs. - Integration compatibility: the DOE branch adds explicit roof skylights. The exact screen must compare their outlines and list order on registered ceiling hosts; aggregate glazing conservation cannot establish placement identity. Use existing central tolerances and leave production behavior unchanged. A positive baseline and five independent negative cases pin this boundary before the fix.
- Limits: configured thermal-program preservation does not authenticate source program-key classifications; no ownership/construction evidence is invented. Existing multiplier and SelfAdjacent scope limits remain. No annual/batch/production simulation, native launch, persistent setting change, force-push, tag or release is authorized by this integration.
- Verification: inspect the merged activation/build routes and explicitly refresh the guarded source inventory, run reviewed narrow software suites with fixed exclusions, independently review the final combined tree, then verify remote main ancestry and branch SHA before and after pushing only the new branch. Exact outcomes are recorded at close-out; prior branch-native and energy results are not new integration results.
-
Observed close-out: 11,092 selected software tests passed, zero failed, one pre-existing atlas A2 placeholder skipped; 11 guard regressions passed. Full solution build: zero warnings/errors. Independent source review accepted the 684-file executable/build fingerprint; no blocking findings. The default full-solution test runner and native/energy checks were not exercised. Exact branch publication and remote SHA confirmation are retained in the external integration record.
D-184 — The research integration branch merged into main: D-177 to D-183 and ADR-028 to ADR-031 renumbered after main¶
- Date: 2026-10-29 · Decided by: Cheng Xuan Li (approved the merge on 2026-10-29); the controller (the conflict resolutions and the renumbering) · Status: Accepted; merged through a pull request, not released
- What.
integration/main-bemgen-research-20261028(1ddaf10, Jimmy Wang's 18 commits of 2026-10-28) merged intomainat0b687e7(D-176) as3b03a71: the DOE reference catalog and packets, the floor research gate, spatial program signatures, the volume ledgers, the medium-office ledger fixture, the exact-composition screen, the comparison energy report, the raw zone thermal reader, and the software-only launcher (D-177 to D-183). The branch's basea527da5is the DOE geometry tip thatmainalready holds through PR #1. - Numbering. The branch wrote its entries as D-172 to D-178 and its ADRs as ADR-027 to ADR-030, the numbers
mainhad meanwhile given to the atlas release 2.0.0 contracts, the daylighting removal, version 1.5.0, and PR #1's approval (D-172 to D-176, ADR-027). Following D-160's convention, they followmain's sequence: D-172 to D-178 become D-177 to D-183, ADR-027 to ADR-030 become ADR-028 to ADR-031 (files renamed), and every cross-reference in the files the branch added or changed follows,main's own references unchanged. Commit subjects on the branch keep the numbers of their time. - Conflicts. This log keeps
main's entries first;Lod.ProgramJson.csprojembeds the reference packets besidemain's program JSON 5.4.0 schema; program-json.md keepsmain's 5.4.0 status line with the branch's catalog note, its "not atlas Program JSON 2.0.0" now 5.4.0.mainwins on the contracts (program JSON 5.4.0, constructions 4.3.0 and 3.1.0, mass contract 1.1.0), the daylighting writer's removal (D-174: the branch writes no daylighting; its DOE packet handles only daylight-saving time), and version 1.5.0: AGENTS.md's two branch paragraphs saying "version remains 1.4.0", which git placed in Stage workflow and Commands, become one Project state bullet. - Merge-induced fix.
main's water mains inlet (D-172) madeWaterTemperatures.Inletnullable, soResearchProgramSignatureno longer built (CS8604). A weather-file inlet is now the same input only as another weather-file inlet; a schedule against the water mains is a difference. Two tests (AWeatherFileWaterInletIsNotTheSameInputAsAScheduledInlet,WeatherFileWaterInletsOnBothSidesAreTheSameInput) failed with a null reference before the fix and pass after it. - Checks:
scripts/verify.ps1on the merge, withoutBEMGEN_ENERGYPLUSorBEMGEN_WEATHER_3B: VERIFY PASSED, 0 warnings: Core 1038, Generators 3051 (1 skipped), Integration 3629, Export 3116 (1 skipped), ConstructionJson 311, ProgramJson 239, Simulation 265 (14 skipped), IdfBatchRunner 103 (3 skipped), TestSuiteGenerator 119; 11,871 passed, 19 skipped, none failed.python scripts/docs-export/developer_pages.py --check: 106 pages checked. - Open: the software-only launcher's reviewed source digest (
scripts/software-only/policy.json, 684 files) does not covermain's code, soscripts/verify-software-only.ps1refuses the merged tree until a person inspects the activation routes and refreshes the policy (the merged tree still has the 18 engine-annotated tests it excludes); no Rhino smoke (reference-programsand the others), viewport, toolbar, or example.ghcheck was run for the merge; no EnergyPlus run.
D-185 — Tooling of the geometric LOD study: DOE prototype suites, pinned inputs and weather, a stage driver, and the deduplicating report¶
- Date: 2026-10-29 · Decided by: the owner (the study, its deterministic prototype schedules, and the §3 work of the run plan); the controller (the design of ADR-032) · Status: Provisional; built on
claude/lod-study-scripts, not merged or released - What. The test-suite generator imports every program for its test case's climate zone and an optional
program_context, takeswindow_to_wall_ratio: program(the JSON'sfenestration), checks a registry file'ssha256, reads relative inputs from--inputs-root, and records or requires the floor research gate (settings.floor_research_gate:off,report,require). Adoebuilding block runs the 15 DOE types through the 11 DOE generators at their records' dimensions, with an atlas program (or a floor-area mix) per program key and the record's glazing per key by default (scripts/TestSuiteGenerator/README.md).scripts/lod-study/adds the weather pins (pin_weather.py, six EnergyPlus TMY3 files,research/lod-study/weather.lock.json), the atlas input export with its manifest (export_inputs.py, 366 files for 22 prototype-vintages from atlasv2.0.0,96a2e928), the generated stage specs (make_specs.py,research/lod-study/specs/{smoke,pilot,main,ht,timing}.yaml), the driverrun.ps1(generate, run, merge, report,-Resume), andreport.py(deduplication by zone-geometry signature and annual sensible totals, and the §10 tables beside the conditioned-area change), documented inscripts/lod-study/README.md. - Why. Plan §3: climate-dependent program values fell back with
ProgramJsonClimateZoneNotGiven; the DOE generators were not in the suite registry; no weather was pinned; no spec or driver existed. - Checks.
scripts/verify.ps1passed with 0 warnings before each commit; TestSuiteGenerator tests 119 → 150.python -P -m unittest discover -s scripts/lod-study/tests: 7 passed.make_specs.py --checkpassed; all five specs plan. R1 smoke ran (plan §4). - Not done. No Rhino step is involved. The DOE suite tests that use the record's storeys and glazing were written before the code but first failed by not compiling; the report's unit tests were written after it.
D-186 — LOD study choices the build changed or found, for the owner¶
- Date: 2026-10-29 · Decided by: the controller (proposals; the owner decides) · Status: Provisional
- Vintages: 90.1-2007 and 90.1-2019, since atlas 2.0.0 has no 90.1-2004 (the plan proposed 90.1-2004).
- Zonings: 24 main zonings (PerimeterCore at 3.0 m and 4.57 m with every stacker) in place of the plan's 18, since a spec's zonings are a full factorial.
- Floor gate:
report, notrequire:requireblocks every zoning that merges zones of different programs, 16 of the medium office's 24 in R1. - Program keys: 90.1-2019 stand-alone retail
Retailis the atlas's Front_Retail and Core_Retail mixed by represented area (150.81 and 1600.48 m²); small-hotelGuestRoomisGuestRooms_Mixed; 90.1-2019 hospitalOfficeisOffice_Mixed; the 90.1-2007 small hotel takes the setdca6c5of the atlas's two sets with the same metadata. - Glazing: the DOE record's per key (
record), since the atlas 90.1 programs have nofenestration. Zone 7: registered as7A, the atlas's label. - R1 finding (blocking R3 in three climates). EnergyPlus warns once a year
Temperature out of range [-100. to 200.] (PsyPsatFnTemp)fromPsyTwbFnTdbWPbin the medium office reference with the 2B Tucson, 4A New York, and 7A International Falls files, and with Sterling exactly as in mock-1, at ordinary weather records; 2A, 3C, and 5A run clean. It is not a defect of the Sterling file. The single-zone stackers over many-zone floors warnFixViewFactors(and, for Single Zone Building, the floor-area warning). The runner fails these sites' verdict; accepting the warnings would change its expected-warning list, a validation decision left to the owner. Nothing was relaxed.
D-187 — Causes of the R1 EnergyPlus warnings and a proposed runner policy, for the owner¶
- Date: 2026-10-29 · Decided by: the owner ("find the cause first"); the controller (the diagnosis and the proposal) · Status: Accepted by the owner's decision of 2026-10-29 (D-189); implemented; the
FixViewFactorspart superseded by D-191 (the IDF writer merges coplanar pieces, the alternative named below) PsyPsatFnTemp(2B, 4A, 7A references, once a year). EnergyPlus's outdoor wet-bulb iteration (PsyTwbFnTdbWPb, the site'sSite Outdoor Air Wetbulb Temperature) at a time step whose outdoor wet-bulb is just below 0 °C. On the New York reference it stays at the same time step and input (03/12 11:50, −170.82 °C) with dehumidification control off, a lower minimum supply humidity ratio, no infiltration, no people, no tables, and no HVAC at all; the timestep moves it. The converged value is right (−0.4185 °C against an independent −0.4182 °C). Changing that record's humidity from 27 % to 28 % removes it and changes the summed ideal sensible loads by at most 1.1 × 10⁻⁴ kWh in an hour; no object of BEMGen's IDFs uses the outdoor wet-bulb. Not a BEMGen defect; no metric changes. Evidence: run plan §8.FixViewFactors(single-zone stackers over No Simplification and Semantic Merge). A merged zone keeps every room's floor and ceiling pieces (51 surfaces against 6 for the same floor simplified by Single Zone Per Floor) and an internal mass 2.4 times its surface area; EnergyPlus's approximate view factors then miss completeness by 0.005. Without the mass the warning goes; the geometry is closed and its areas equal the simplified zone's. Not a geometry defect. The fragmented zone's annual sensible cooling differs from the simplified zone's by about −0.45 % and heating by +1 to +2 % of 280 kWh, so they are two models. Correction to D-186: the floor-area warning of Single Zone Building is already expected (ADR-010); onlyFixViewFactorsfailed those sites.- Proposed runner policy (owner to decide). Add two expected-warning entries to
ExpectedWarnings, each matched on its text and its context line, with a count limit, and keep failing on any severe or fatal error and any other warning: (1)OutdoorWetBulbIteration:Temperature out of range [-100. to 200.] (PsyPsatFnTemp)with the routinePsyTwbFnTdbWPb, at most 3 per run, none during sizing; (2)MergedZoneViewFactors:FixViewFactors: View factors not completewith an enforced reciprocity tolerance of at most 0.01, at most one per zone. The alternative for (2) is a BEMGen change: stackers that coalesce coplanar floor and ceiling pieces of a merged zone, which changes those sites' results by about 0.5 % and needs its own decision. Until then the 2B, 4A, and 7A test cases and those four zonings fail the verdict.
D-188 — Machine-readable LOD recommendations for the atlas site¶
- Date: 2026-10-29 · Decided by: the owner (the recommendations are published on the atlas website); the controller (the format and the default threshold) · Status: Provisional; the threshold is the owner's choice
scripts/lod-study/report.pyalso writeslod-recommendations.json(schemascripts/lod-study/schemas/lod-recommendations-v1.schema.json): per test case, keyed by prototype, climate zone, vintage, and orientation and indexed by atlas program ID, the reference's benchmark values, every zoning's §10 metrics and conditioned-area change as the runner stored them, and the coarsest zonings (fewest modelled zones) meeting the threshold (run plan §9).- Default threshold (owner choice): for sensible heating and cooling, |annual error| ≤ 10 %, CV(RMSE) ≤ 30 %, |NMBE| ≤ 10 % (ASHRAE Guideline 14, hourly), |conditioned-area change| ≤ 1 %; a load under 1 kWh/m² a year is not judged.
--thresholdreplaces it and the file records it. The site integration is later atlas work. - Fix in the same slice: the report's zone-geometry signature read the vertex count as the first coordinate (field offset); signatures now hold the vertices themselves, with a test. The R1 duplicate groups are unchanged.
D-189 — The owner approves D-187: the two limited expected warnings, implemented; R2 next¶
- Date: 2026-10-29 · Decided by: Cheng Xuan Li ("Approve D-187: add the PsyPsatFnTemp (≤3/run, none in sizing) and FixViewFactors (≤0.01, ≤1/zone) expected-warning entries. Merge BEMGen PR #4 when verify passes, then run the R2 pilot.") · Status: Accepted; implemented on
claude/lod-study-scripts;MergedZoneViewFactorssuperseded by D-191 (removed) - What.
ExpectedWarnings.DefaultgainsOutdoorWetBulbIteration(PsyPsatFnTempwith the context lineRoutine=PsyTwbFnTdbWPb, at most 3 a run by the recurring summary's total, none during sizing) andMergedZoneViewFactors(FixViewFactors … zone="…"with an enforced reciprocity tolerance at mostToleranceSettings.ViewFactorReciprocity, 0.01, at most one per zone). A warning that misses its context line or its bound stays unexpected; a run over a limit fails; severe and fatal errors and every other warning still fail. The counts go intosite_status.message(expected warnings: OutdoorWetBulbIteration 1), so the central schema stays at version 7. - Proof. The R1 smoke suite run again: 24 of 24 sites succeed (the four
FixViewFactorssites withMergedZoneViewFactors 3or1), and the 20 sites that succeeded before have identical sensible heating, sensible cooling, total load, and CV(RMSE). The 4A diagnosis suite (New York JFK and Sterling, 48 sites): 48 of 48 succeed, both references withOutdoorWetBulbIteration 1. - Tests.
LimitedWarningTests(11, Lod.Simulation) andLimitedWarningRunTests(2, IdfBatchRunner), written first and failing (not compiling, then on the missing status count).
D-190 — R2 pilot run: two Convert2IDF fixes, the triage, and the small-hotel warning for the owner¶
- Date: 2026-10-29 · Decided by: the owner (run R2, D-189); the controller (the fixes and the triage) · Status: Accepted fixes; the small-hotel warning is Provisional, for the owner; closed by D-191 and D-192 (the writer merges coplanar pieces, R2 run again)
- Fixes, test-first. (1) Convert2IDF refused the hospital's reference:
L1/OR1_Flr_2/F3is two rectangles of one floor meeting at one vertex, which the union inIdfPolygons.Rebuildkept as one ring that touches itself; such a ring is now split at the vertex it repeats into its simple rings, their areas summing to the ring's within the central area tolerance (DoeReferenceConversionTests). (2) The restaurant, primary school, and small hotel references crashed EnergyPlus (Zero or negative surface area, exit code 0xC0000005) on wall-transfer or floor-distributed masses of about 10⁻⁷ m²: their sets budget exactly the capacity the record represents, so the shortfall was rounding.ConstructionMassAccounting.Plantakes the tolerances and treats a represented capacity equal to its budget withinToleranceSettings.RelativeAreaas no shortfall and no overshoot (ConstructionMassAccountingTests). Export snapshots are unchanged. - Results. 22 test cases; 528 sites planned, 440 written, 88
Invalidby the simplifiers' and stackers' documented limits (multiplied rooms in the large hotel and the hospital, ADR-026; the outpatient's narrow wings under Perimeter Core); 392 of 440 succeeded (89 %), all 372 succeeded test sites paired (largest deviation 2.9 × 10⁻¹⁰ W); 210 of 372 dropped as duplicates (110 groups); a zoning meets the default threshold in 16 of 20 cells. Per-prototype times and the recommended zonings are in the run plan §4. - For the owner. The R2 gate (≥ 99 % complete) is not met: both small-hotel references give one
FixViewFactorswarning onL3/CORRIDORFLR4, the unmerged 24-surface corridor of the top floor, which EnergyPlus prints without the enforced-reciprocity line even withDisplayExtraWarnings; D-189's entry needs that line to bound the tolerance, so the warning stays unexpected and the hotel's 46 test sites were not run. Deciding to accept it (for example a bareFixViewFactors, at most one per zone, in a reference as in a test site) or to look into the corridor's geometry is the owner's call; nothing was relaxed.
D-191 — The IDF writer merges a zone's coplanar surfaces, supersedes D-189's view-factor entry¶
- Date: 2026-10-29 · Decided by: Cheng Xuan Li ("Fix in BEMGen": the IDF writer merges a zone's coplanar floor, ceiling, and roof pieces); the controller (the rule, ADR-034) · Status: Accepted; the realisation Provisional, pending the owner's reading · Supersedes D-189 in part (the
MergedZoneViewFactorsentry) - What.
IdfSurfaceMergemerges, before the writer splits or pairs anything, a zone's floors, ceilings, and roofs of one kind, elevation, boundary, and adjacent zone that share an edge, pairwise while the union is one simple ring without holes of their summed area, interzone groups only together with their mirror across; and walls without windows in straight runs. Surfaces with windows or skylights, self-adjacent, with holes, or whose union would have an edge or a nearly straight vertex within EnergyPlus's 0.01 m stay apart. The header lists each merge, provenance recordsMERGED:, the infoSurfacesMergedcounts them. The convex split of outdoor surfaces (ADR-019) stays and now takes the cut leaving the fewest reflex vertices, then the shortest: the small hotel's T-shaped corridor roof is two pieces, not three. - Why. EnergyPlus's approximate view factors treat a zone's coplanar surfaces as unable to see each other; with a large internal mass the many pieces of the single-zone stackers (D-187) and the small hotel's three-piece corridor roof (D-190) missed completeness. In the small hotel reference (Buffalo 5A, 90.1-2019, run in
D:\bgdiag\d190), the two-piece roof removes the warning with no other warning (extra warnings on) and gives the same results as one polygon: annual sensible heating −1.18 % inL3/CorridorFlr4, −0.43 % for the building; cooling −0.97 % and +0.005 %. - Departure from the owner's words. A merged roof that is not convex is still cut into convex pieces, since a non-convex casting surface is a severe EnergyPlus error (ADR-019); in the corridor the two convex pieces give the results of one polygon.
- Runner.
ExpectedWarnings.MergedZoneViewFactorsandToleranceSettings.ViewFactorReciprocityare removed, with their tests;OutdoorWetBulbIterationstays. AFixViewFactorswarning fails a run again. - Tests.
IdfSurfaceMergeTests(13) and the T case ofIdfConvexTests, written first: the T failed on three pieces, the merge tests on a stub that merged nothing.LimitedWarningTests: the three view-factor tests replaced by one that aFixViewFactorswarning fails a run (written first, failing while the entry existed) and one that keeps the bound and key rules on a test entry. Two IDF snapshots changed on purpose (their merged floors, ceilings, and roofs and the header's merge list).scripts/verify.ps1passed with 0 warnings before each commit; after the second: Lod.Core 1040, Lod.Generators 3051 (1 skipped), Lod.Integration 3629, Lod.Export 3132 (1 skipped), Lod.ProgramJson 239, Lod.ConstructionJson 311, Lod.Simulation 275 (14 skipped; 276 before the runner change), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 150.
D-192 — R1 and R2 run again with the coplanar merge: R2's gate met, D-190's small-hotel question closed¶
- Date: 2026-10-29 · Decided by: the owner (D-190: "Fix in BEMGen"); the controller (the runs and the comparison) · Status: Accepted · Closes the open question of D-190
- R1 (
run.ps1 -Stage smoke -Parallel 20, runrun_20261029-050006): 24 of 24 sites succeed, all paired; noFixViewFactors; 11 of 23 test sites dropped as duplicates (5 before), since the single-zone stackers over No Simplification now write the zone Single Zone per Floor writes. - R2 (
run.ps1 -Stage pilot -Parallel 20, suitelod-pilot-20261029-050143, runrun_20261029-050215): 440 of 440 written sites succeed (89 % before), the ≥ 99 % gate is met; 418 of 418 test sites paired (largest internal-load deviation 2.9 × 10⁻¹⁰ W); noFixViewFactorsor other unexpected warning in any site'seplusout.err. 79 duplicate groups, 275 of 418 dropped; a zoning meets the default threshold in 18 of 22 cells (the 16 of D-190 and both small-hotel cells). - Against the previous R2 (
D:/bemgen-results/lod-study/pilot-before-d191, the 392 sites both completed): written surfaces 39 420 → 33 128 (−16 %); references change by at most 0.14 kWh (heating) and 0.36 kWh (cooling) a year; where a load is at least 1 kWh/m² a year, annual sensible heating moves by at most 2.2 % and cooling by at most 1.2 %, the largest in Perimeter Core with a single-zone stacker. EnergyPlus times are not comparable (a shared machine); the run took 9 min 37 s. Details in the run plan §4.
D-193 — R3 main and R4 heat-transfer stages run; the small hotel's IWHT-off failures for the owner¶
- Date: 2026-10-29 · Decided by: the owner (run the study, D-185, D-191); the controller (the runs and the triage) · Status: Accepted for the runs; the R4 failures Provisional, for the owner
- Build. Both stages ran with BEMGen
2ed856c(main after PR #6, beforev1.6.0), atlas inputs of release 2.0.0 (96a2e928), EnergyPlus 26.2.0. - R3 (
run.ps1 -Stage main -Parallel 20, suitelod-main-20261029-051941, runrun_20261029-052718, 1 h 47 min): 264 test cases, 6 336 sites planned, 5 280 written, 1 056Invalidby documented limits (multiplied rooms, ADR-026, 864; the outpatient's narrow wings, 192); 5 280 of 5 280 succeed, 5 016 of 5 016 test sites paired (largest deviation 5.5 × 10⁻¹⁰ W); noFixViewFactors, the only warnings the expected floor-area (384 sites) andOutdoorWetBulbIteration(2 640 sites, within its limit). Deduplication: 925 groups, 3 323 of 5 016 dropped (66 %). A zoning meets the default threshold in 218 of 264 cells: 24 of 24 for the hospital, large hotel, medium office, outpatient, small hotel, stand-alone retail, and strip mall, 20 for the primary school, 16 for the supermarket, 12 for the full-service restaurant, 2 for the warehouse. The coarsest zonings meeting it are most often Conditioned Merge + Single Zone Building (84 cells), Semantic Merge + Stack (57), and Conditioned Merge + Stacked Floor Zone Multiplier (40); the medium office's are Perimeter Core with Stack or Stacked Floor Zone Multiplier. - R4 (
run.ps1 -Stage ht -Parallel 20, suitelod-ht-20261029-071714, runrun_20261029-071919, 33 min): 22 test cases × 96 zonings, 2 112 planned, 1 760 written, 352Invalid(same limits); 1 736 of 1 760 succeed (98.6 %), every reference among them, 1 714 of 1 714 test sites paired; noFixViewFactors. 213 duplicate groups, 1 433 of 1 714 dropped; 18 of 22 cells have a recommendation, the cells of R2. - R4 failures (24, for the owner). All are the small hotel's (12 in 3C, 12 in 5A): the zonings that keep the unconditioned elevator core or mechanical room as its own zone (No Simplification, Semantic Merge, Conditioned Merge with Stack or Stacked Floor Zone Multiplier) with IWHT off. EnergyPlus stops in warm-up (
CalcHeatBalanceInsideSurf … very far out of bounds): the core's program carries the source's elevator equipment, 2 133 W/m² (32 kW in 15 m²) with no lost fraction, and with adiabatic interior walls the zone sheds it only through one 1.8 m exterior wall and the ground. Not a BEMGen defect (the program and the option are written as given, GLOBAL.md scientific rule 3); nothing was changed. The owner decides whether IWHT-off settings exclude such zonings or the study treats such a zone differently. - Not run: R5, the serial timing pass, which needs an idle machine. EnergyPlus times per prototype are in the run plan §4 and are not runtime results (20 runs at a time on a machine shared with other work).
D-194 — Schedule Draw: the atlas generator as an external command, drawn per zone, the requirement's open items settled (ADR-033)¶
- Date: 2026-10-29 · Decided by: the owner (the goal: the atlas's stochastic schedules as an optional add-on beside the deterministic ones, by calling the atlas generator as an external command; the requirement of D-133, D-134); the controller (the settled items, ADR-033) · Status: Provisional, pending the owner's reading
- What.
Lod.ProgramJson.ScheduleDrawdraws program JSON 5.4.0 instances withatlas_schedules3.0.0 (request and bundle schema 3.0.0): one request per draw, one run of a configurable command (defaultpython -m atlas_schedules, files in and out, timeout, captured standard error, no shell, no network on BEMGen's side), the bundle checked against the pins (a pre-release such as3.0.0.dev0accepted withScheduleDrawPreRelease), and one program JSON per instance with the drawn roles asannual2007 schedules andmetadata.schedule_drawprovenance. The program setting is release 2.0.0'sgenerator.json; the generator setting is BEMGen'sschedule_draw_setting1.0.0. - Settled. Drawn: occupancy, lighting, electric and gas equipment, hot water (flow and target), activity, heating and cooling setpoints, narrowed by the setting; ventilation is not a generator schedule. Where: an external command. Calendar: the generator's 2007, never remapped (a suite with schedules must use 2007). Seeds: the test case's seed is the master seed; each zone its own operating group keyed
<plan index>.<zone ID>, its seed the generator'satlas_conditional_seeded_v1of the master seed and key, computed and recorded by BEMGen. Magnitudes: the program's value must equal the generator's amplitude, and amplitude × Σ values equals the bundle's annual total withinToleranceSettings.RelativeLoad. - Refused (open). Apartment-unit recipes (ResStock end uses program JSON does not hold) and composition parents (another slot vocabulary),
ScheduleDrawKindNotSupported. - Why. The generator is validated against native answer keys (atlas ADR 0035); a C# port would need its own validation. Drawing merged zones would break the conservation of hourly scheduled loads (GLOBAL scientific rules 1 and 4).
D-195 — Test suites: the optional schedules: section, each seed a test case, one draw per test case on the plan's zones¶
- Date: 2026-10-29 · Decided by: the owner (requirements 2 to 6 of D-134); the controller (the spec form and records, ADR-033 decision 7) · Status: Provisional, pending the owner's reading
- What.
schedules: {generator: atlas_schedules, setting, setting_sha256?, recipes: [generator.json, ...], seeds: [...]}; absent, every suite is unchanged (IDs, tables, folder). Each seed (0 to 2^53 − 1) is a test case of every combination of the other axes, its ID holding the setting's and program settings' SHA-256 and the seed. Per test case, one generator run on the zones of its generated plans before any zoning; each zone takes its drawn program, and the simplifiers and stackers aggregate the drawn zone programs, so merged zones never get a draw. The command and its folder are machine settings:--schedule-command/--schedule-folder, elseBEMGEN_SCHEDULE_COMMAND/BEMGEN_ATLAS_GENERATOR. Records:draws/<test case ID>.request.jsonand.provenance.txt, theschedule_*columns oftest_cases,schedule_seedin the long table, and the tableschedule_draws(each zone's key, plan, zone, program, operating group, seed policy, seed). A calendar year other than 2007 and a mixed slot are plan problems. - Why. Requirement 3 (seeded sampling only here, the other axes full factorial), 4 (all sites of a test case share its drawn zones), 5 (per-zone seeds), 6 (one run per test case). A floor that stands for several storeys shares its plan's draws, since a multiplier stands for identical floors.
D-196 — Schedule Draw built: the core service, the Grasshopper component, the suite section, and the real-draw proof¶
- Date: 2026-10-29 · Decided by: the controller (build of D-194, D-195) · Status: Accepted build; the Rhino check is open
- What. Schedule Draw (
SDraw, panel 1 Program, GUID740cafea-7afe-47f4-8723-b24b6ec3c836, iconschedule_draw), a thin adaptor with a Run toggle whose Drawn JSON goes into Program JSON; architecture page, design note, the requirement page marked designed and implemented. Fixtures:tests/Lod.ProgramJson.Tests/Fixtures/draw/(the DOE Ref 1980–2004 medium office'sgenerator.jsonfrom atlas1bc25767, a setting, the request BEMGen writes for two instances and seed 42, and the bundle the generator answered with). The existing icons are unchanged byte for byte. - Real draw.
python -m atlas_schedules(Python 3.14, generator3.0.0.dev0, atlas1bc25767) on the recorded request, the DOE Ref 1980–2004 medium office as instances0.KS(200 m²) and0.DI(300 m²) with their hot-water services, seed 42: the command took 1.1 s, gave the bundle SHA-25690ff6a71…40e2twice (byte-identical runs), andRealDrawTests(withBEMGEN_ATLAS_GENERATOR=D:/aa-gen) passed, the whole draw with its read-backs in 15.9 s, its bundle equal to the recorded one byte for byte. The two instances of one program draw different hours, as the shifted seeds intend: annual lighting 38,212 Wh/m² for0.KSagainst 69,772 Wh/m² for0.DI, equipment 67,037 against 55,566 Wh/m². - Checks.
scripts/verify.ps1: VERIFY PASSED, 0 warnings, 11,984 passed and 20 skipped of 12,004 (Lod.ProgramJson.Tests 239 → 292 with 53 schedule-draw tests, one of them the opt-in real draw, skipped; TestSuiteGenerator.Tests 150 → 163). Aftermainwith D-191 and D-192 was merged in: VERIFY PASSED, 0 warnings, 11,997 passed and 20 skipped of 12,017. The core service was drafted before its tests, not test-first; its tests change one input at a time (a lighting value, an annual total, a fraction, a version, a seed, an owner) and check each is refused with its code, and the first run caught two of the tests' own mistakes (a fraction mutation that the binding check caught first, a stand-in script's parameter binding); the suite tests were written first and failed by not compiling (SuiteScheduleDraw,TestCase.ScheduleSeed,SuitePlan.Schedulesmissing), and the sha256 pin case first failed on YAML reading an unquoted hash as a number (the test now quotes it, as the README says). - Not done. The Rhino 8 check of the component (checklist); an example
.gh; the software-only launcher's reviewed-source policy, which a person must refresh for the changed sources (and should listBEMGEN_ATLAS_GENERATOR); apartment units, composition parents, and mixed slots (ADR-033 Consequences).
D-197 — Version 1.6.0: Schedule Draw with the released generator, atlas release 2.1.0, the reference programs, and the LOD study tooling with the coplanar merge¶
- Date: 2026-10-29 · Decided by: Cheng Xuan Li (stage B3 of the public-release plan, with blanket authority to merge, push, tag, and publish the releases once verify and the gates are green); the controller (the preparation and the routine choices below) · Status: Accepted
- Version. 1.6.0, prepared on
release/v1.6.0frommainat5883da6, a minor version as D-125 and D-175 reason: D-064 waives backward compatibility, and no component or parameter GUID changed or moved sincev1.5.0(every one of its 101 GUIDs is still in the same source file ofsrc/Lod.Grasshopper; 2 are new: Schedule Draw740cafea-7afe-47f4-8723-b24b6ec3c836and DOE Reference Program9f3c8f73-4d64-4c7c-a2a2-8eb071dc7c12). It adds components and appends one input (Convert2IDF's Require Floor Research Eligibility, index 11); nothing is removed from a component. The existing icons are byte-identical tov1.5.0's. - What it merges since
v1.5.0(45 commits onmain): - Research contracts and reference programs (D-177 to D-184, ADR-028 to ADR-031, PR #3): DOE Reference Program (the authenticated DOE new2004 MidriseApartment Chicago/5A programs of DOE 1.3_5.0 and 1.4_7.2), Convert2IDF's opt-in floor research gate, spatial program signatures, volume ledgers, the exact-composition screen, the comparison energy report, and the software-only launcher.
- LOD study tooling (D-185 to D-190, ADR-032, PR #4): DOE prototype suites with pinned atlas inputs and weather, the stage driver and the deduplicating report with
lod-recommendations.json, the runner's limited expected warningOutdoorWetBulbIteration, and two Convert2IDF fixes (a self-touching floor ring split at its repeated vertex; a mass shortfall withinToleranceSettings.RelativeAreatreated as none). - Coplanar surface merge (D-191, D-192, ADR-034, PR #6): the IDF writer merges a zone's coplanar floors, ceilings, and roofs and windowless wall runs;
MergedZoneViewFactorsremoved. - Schedule Draw (D-194 to D-196, ADR-033, PR #5): the component, the core service, and the test-suite
schedules:section. - Made in this preparation.
- Atlas release 2.1.0 and the generator 3.0.0 pinned (D-198): the embedded program JSON schema is release 2.1.0's copy, the recorded bundle recorded again with the released wheel.
- The version in
Directory.Build.props; the smoke specschedule-draw(registration, Run false, two error scenarios, and, withBEMGEN_SCHEDULE_COMMAND, the real draw through Program JSON to Validate and Convert2IDF, repeatability, another seed); the exampleschedule-draw.gh(routine choice: a Stream Filter gated by the Run toggle passes the program's own JSON while Run is false, so the saved definition solves without a generator and the examples check stays one Validate, True); the 21 example definitions rebuilt with the 1.6.0 plugin (BEMGEN_EXAMPLES_WRITE=1). reference-programs.py(D-178), never run on this harness before (D-184), stopped withImportError: No module named Lod.Core.Common: IronPython had no reference to the loadedLod.Coreassembly. The spec now adds it from the loaded assemblies (asdoe-presets.pydoes); no check changed, and it then passed its 72 checks.- The release-1.6.0 checklist for a person in Rhino, appended to grasshopper-smoke-test.md.
- Checks on the release candidate:
- Examples. All 21 rebuilt with the 1.6.0 plugin; each reopened and solved with one Validate True and no error; one warning, Program JSON's
ProgramJsonNotInstantiatedof the DOE Ref medium office's two hot-water zone gain records inschedule-draw.gh(the same with Run false or true). WithBEMGEN_SCHEDULE_COMMANDset toD:\bemgen-venv-schedules\Scripts\atlas-schedules.exe,schedule-draw.ghwith Run true solved with no error, Validate True, and the drawn lighting schedule in the IDF. - Definitions saved with 1.5.0. The 20 example files as committed before the rebuild (saved with the 1.5.0 plugin) reopened with the 1.6.0 plugin (
1.6.0+5673d81): every archive read without a warning or error, and all 20 solved with one Validate True and no runtime warning or error (60 checks passed). - Rhino smoke. All 27 specs of
scripts/rhino-smokein Rhino 8.25.25314.11001 on the final plugin code (f5f70a0), withBEMGEN_SCHEDULE_COMMANDset: 1519 Validate True and none False, 1002 spec checks passed and none failed, no missing component or missing expected error, no exception; runtime errors only in the error scenarios and, in the DOE specs, on the component placed without a program map, as in their stage runs.reference-programsfailed in the full run on the import above and passed when run again alone after the fix (its counts are those of the second run). Theschedule-drawspec drew with generator 3.0.0 and noScheduleDrawPreRelease; nothing wasSKIPPED. Timings are not meaningful: an EnergyPlus batch of another agent (about 14 to 17 processes) ran throughout. scripts/verify.ps1on the candidate's tree: VERIFY PASSED, 0 warnings: Core 1040, Generators 3051 (1 skipped), Integration 3629, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 299 (1 skipped), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163. One earlier run on the second commit's tree failedAGeneratorThatRunsTooLongIsStopped(the 2 s timeout stopped the stand-in command only after 1 min 47 s on the loaded machine, against its 50 s bound); the run again passed. The test was not changed; it is load-sensitive.- Fresh-clone gate. A clean clone of
release/v1.6.0atbe90af9(D:\BEMGen-gate\v1.6.0) passedscripts/verify.ps1with 0 warnings: Core 1040, Generators 3051 (1 skipped), Integration 3629, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 299 (1 skipped), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163. The commit that records this changes only this entry. - Sync-docs. Applied: README (version, the 1.6.0 paragraph), AGENTS.md (the Done line, the stage bullets' release state, the LOD study bullet), the statuses of the Schedule Draw and LOD study plans, the roadmap's progress log,
examples/README.md(schedule-draw.gh), the smoke README (schedule-draw, the examples' Run-true check), ADR-033 and the Schedule Draw architecture page (the released generator), the program and construction JSON pages and the user-facing texts (release 2.1.0), the fixture README.python scripts/docs-export/developer_pages.py --check: 112 pages checked, the one known redaction. The public site described 1.5.0 (reference-source.jsonat866da55); the docs export of the tagged commit, its import in the docs repository, and that repository's ownsync-docsfollow the tag. - Release notes.
- New: Schedule Draw in 1 Program: stochastic 8,760-hour schedules for a program JSON from the atlas generator, an optional add-on whose Drawn JSON goes into Program JSON. It needs Python 3.11 or later and
atlas-schedules3.0.0 installed from the wheel of the atlas releaseatlas-schedules-v3.0.0(in a virtual environment), run as an external command (Command: the environment'sScripts\atlas-schedules.exe) only while Run is true; drawn schedules use the calendar year 2007; apartment-unit and composition recipes are refused. DOE Reference Program in 1 Program. Program JSON and Construction JSON read atlas release 2.1.0 exports (2.0.0 exports remain valid). - What IDFs change: a zone's coplanar floors, ceilings, and roofs and windowless wall runs are merged (the header lists the merges); results move slightly (D-192: at most about 2.2 % of annual sensible heating and 1.2 % of cooling where a load is at least 1 kWh/m² a year, in the R2 pilot).
- What opens differently: definitions saved with 1.5.0 open with their saved values; Convert2IDF shows Require Floor Research Eligibility, false. The examples are rebuilt with 1.6.0 and
schedule-draw.ghis new. - Not yet performed by a person: the release-1.6.0 checklist in Rhino 8.
- What follows: the tag
v1.6.0on the merge commit of the pull request, the releases onEnvironmentalSystemsLab/BEMGen(notes) and on the docs repository (the files ofscripts/package-release.ps1built from the tagged commit), and the docs export ofv1.6.0imported by the docs repository. - For a person in Rhino 8: the release-1.6.0 checklist (installing from the release files, the two new icons,
schedule-draw.ghwith and without Run, the viewport previews).
D-198 — Atlas release 2.1.0 and the released generator 3.0.0: the 2.1.0 schema copy embedded, the recorded bundle recorded again¶
- Date: 2026-10-29 · Decided by: Cheng Xuan Li (stage B3 of the public-release plan: pin atlas release 2.1.0 and
atlas-schedules3.0.0 for version 1.6.0, contracts unchanged unless a check proves otherwise); the controller (the two changes below, each shown necessary by a check) · Status: Accepted - Atlas release 2.1.0. A delta on 2.0.0 (release,
3f62e19a): the open licence, the eight apartment folders'generator.json, and its manifest; contracts unchanged (program JSON 5.4.0, constructions 4.3.0 / 3.1.0, mass 1.1.0). Exported atv2.1.0(python -m scripts.atlas_exports,PYTHONHASHSEED=0), the 90.1-2019 and DOE Ref 1980–2004 medium offices differ from the 2.0.0 fixtures only insource.release(atlas-release-2.1.0), and the medium office's detailed and unified 3B construction sets are equal to the fixtures as JSON. The atlas widened the 5.4.0 schema in place:source.releaseadmitsatlas-release-2.1.0(two enum lines, otherwise byte-identical; the construction schemas are byte-identical). BEMGen's embedded copy refused every 2.1.0 program export (ProgramJsonSchemaViolationat/source/release): the new testAnExportOfAtlasReleaseTwoPointOneReads(seven fixtures with that release) failed so before the change. The embedded schema and its fixture copy are now release 2.1.0's;AReleaseTheSchemaDoesNotNameIsRefusedkeeps the enum closed. The programs and constructions stay release 2.0.0 exports:export_fixtures.pygains--schema-atlas, and its run with the atlas atf617b30band the schema fromv2.1.0rewrote every other fixture byte for byte (only the schema and the programs'manifest.json, which recordsprogram_schema_atlas_commit, changed). User-facing texts that named only release 2.0.0 (Program JSON's and Schedule Draw's descriptions, the reader's and Construction JSON's refusal messages) name 2.1.0 too. 2.0.0 exports remain valid. - Generator 3.0.0. The wheel
atlas_schedules-3.0.0-py3-none-any.whlofatlas-schedules-v3.0.0(SHA-256f082bb12…229cb, as the release lists it), installed with pip intoD:\bemgen-venv-schedules(Python 3.14.7; numpy 2.5.4, jsonschema 4.26.0), reportsgenerator_version3.0.0.RealDrawTests(BEMGEN_ATLAS_GENERATOR=D:\bemgen-venv-schedules,BEMGEN_ATLAS_COMMAND=D:\bemgen-venv-schedules\Scripts\atlas-schedules.exe) first failed on the bundle SHA-256 (a94624b0…1838against the recorded90ff6a71…40e2): the two bundles differ only ingenerator_version(3.0.0against3.0.0.dev0), every schedule value equal.Fixtures/draw/bundle.jsonis recorded again with the released generator (two runs byte-identical); the tests that expected3.0.0.dev0expect 3.0.0, the pre-release warning test now draws a bundle mutated to3.0.0.dev0, andRealDrawTestsalso checks the version 3.0.0 and that noScheduleDrawPreReleaseis given. It then passed with the console script and withpython.exe -m atlas_schedules(generator 3.0.0, 18.9 s and 12.0 s on a loaded machine). ADR-033 records the pin and the install; the component's default command is unchanged. - Checks:
scripts/verify.ps1on the commit's tree (without the opt-in variables): VERIFY PASSED, 0 warnings: Core 1040, Generators 3051 (1 skipped), Integration 3629, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 299 (1 skipped; 292 before), Simulation 275 (14 skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163.
D-199 — The public site of 1.6.0: exported after the tag, with D-176's link to the private pull request as plain text¶
- Date: 2026-10-29 · Decided by: the controller (a routine choice of stage B3, under the owner's authority to publish, D-197) · Status: Accepted
- What. The import of the docs export of
v1.6.0(e0ccc48) into the docs repository reported one public-content problem: D-176 links to PR #1 at a path inside the private BEMGen repository, which the public site must not hold (its AGENTS.md rule 5; generated pages are fixed here, never there). D-176's link is now the plain text "PR #1 of the private repository"; its decision and every other word are unchanged. The site is exported from this commit onmain, which changes only this log after the tag, soreference-source.jsonnames version 1.6.0 at this commit rather than at the tag; the plugin, the examples, and the release files are those ofv1.6.0. - Why not a moved tag: a pushed tag is never moved (the docs repository's rule 2 and D-004's spirit); the documents of the tag differ from the site's only in this link and this entry.
- The tag checked. The gate clone
D:\BEMGen-gate\v1.6.0, checked out at the tagv1.6.0(e0ccc48, tree equal to D-197's gate commitd0c991b), passedscripts/verify.ps1again with 0 warnings and D-197's counts. The release files were built byscripts/package-release.ps1on the clean tagged checkout (SHA-256:bemgen-1.6.0-examples.zip9c6d85c1…f904,bemgen-1.6.0-rh8_19-win.yak0b744147…88d1,bemgen-1.6.0-rh8-win.zip247d6d90…6614).
D-200 — R5 serial timing pass run on an idle machine; its coefficient-of-variation gate not met, for the owner¶
- Date: 2026-10-29 · Decided by: the owner (stage C3 of the public-release plan: run R5 under decision 14 of the run plan, on an otherwise idle machine); the controller (the waits, the guard, the analysis, and the routine choices below) · Status: Accepted for the run; the gate verdict Provisional, for the owner
- Build. BEMGen
b29931c(main afterv1.6.0), atlas inputs of release 2.0.0 (96a2e928; the R3/R4 export underD:\bemgen-lod-inputsreused, each file checked by its pinned SHA-256 when the suite was written), EnergyPlus 26.2.0 (4bd7a1f26f), i7-14700F (28 logical processors). Suitelod-timing-20261029-080232, runrun_20261029-083427,run.ps1 -Stage timing(the runner with--parallel 1 --timing-repeats 3 --timing-test-cases 12; the protocol unchanged). - Machine state. Before the run,
dotnet build-server shutdown; a 30-minute wait (08:04 to 08:34) never saw the machine idle: another project's 2-worker fit (D:\smart-meter-calibration,county_library.py fit --workers 2) held about 12 %. The run started at 08:34; at 08:35 the same project startedcounty_calibrate.py stage1 --generations 8000 --chains 24 --workers 14, and total load was mostly 25 to 60 % until about 13:20. Nothing of that project was stopped. The serial main phase (08:34 to 09:29) ran beside it: 440 of 440 written sites succeed, 418 of 418 test sites pair (largest deviation 2.9 × 10⁻¹⁰ W), deduplication and recommendations as R2's; its serial times insitesare loaded measurements (processor time a median 45 % above the timing pass's, up to 185 %), superseded evidence, not results. A guard then stopped this run's runner and its EnergyPlus (only these) at 09:29:51 as the first timing run began, before any timing row was written. Polling every 10 minutes (later every 2) for a 5-minute window with average under 5 %, maximum under 10 %, and no EnergyPlus, Rhino, or MSBuild, the machine was idle at 13:38 (4.2 %, 6.0 %), andrun.ps1 -Resumeran the whole timing pass 13:38 to 14:31 with the guard armed (it would have stopped the runner on foreign load again; it did not trigger). Load in 30-s samples during the pass: mean 5.1 % (one EnergyPlus is 3.6 %), at most 11.9 % at 13:39–13:41 with two short foreign scripts. - Counts. 12 test cases (no medium office, no primary school, by the SHA-256 order), 218 sites, 654 of 654 timing runs completed.
- Gate (coefficient of variation of the processor time per site < 5 %): not met. 192 of 218 sites pass, 26 miss, the largest 12.9 % (median 2.2 %, P90 5.3 %); peak committed memory varies by at most 0.55 %. Not a cold start: without repeat 1, 31 miss. 16 misses are among the 34 sites of at least 4 s: from about 14:21, late in repeat 3, every run took 13 to 24 % more processor time than in repeats 1 and 2 at unchanged total load and with no foreign process (over repeats 1 and 2, 2 of the 34 miss); the cause is not established (one candidate: the scheduler moving the single EnergyPlus thread between the 14700F's performance and efficiency cores). The other 10 misses are runs of 0.8 to 3.3 s, where the 15.6 ms tick of the processor-time count is about 2 % of the value. Whether to run R5 again (for example with the process pinned to a performance core, or with more repeats), to judge the gate on longer sites, or to accept the medians is the owner's; no tolerance or gate was changed.
- Results. Medians relative to the reference: over all 206 timed test sites 0.504 processor time, 0.521 wall clock, 0.985 peak committed memory; Single Zone Building 0.424, 0.422, 0.980 (50 sites; outpatient 0.064, small hotel 0.057, strip mall 0.256, full-service restaurant 0.787). Per prototype and per zoning in the run plan §4; full tables in
D:/bemgen-results/lod-study/timing/timing-analysis/timing-summary.mdandtiming-sites.csv, written bytiming_analysis.pybeside them (SHA-25658605220…e072, read-only on the mergedcentral.duckdb; a routine choice: kept with the results, not in the repository, since it only tabulates the runner'ssite_timing).report.py's outputs are in…/timing/report/. - Not done. No Rhino step is involved. The repeat-3 slowdown was not diagnosed further.
D-201 — R4's small-hotel failures: such zonings are not applicable in their cells; no BEMGen change¶
- Date: 2026-10-29 · Decided by: the controller, under the owner's blanket authority for the public-release stages (stage C4) · Status: Accepted; amended by D-211 (the heat-transfer rule) and D-224 (the rerun leaves no failed zoning) · Closes the open question of D-193
- Decision. The 24 R4 sites that failed (the small hotel's zonings that keep the unconditioned elevator core, the source's 32 kW elevator load in 15 m², or a mechanical room as its own zone, with IWHT off; EnergyPlus diverges in warm-up, D-193) need no BEMGen change: the program's load and the option are written as given (GLOBAL.md scientific rule 3). The study treats such zonings as not applicable in that cell: the cell's recommendation comes from the zonings that ran (as
report.pyalready does: a failed site is listed and never recommended), the failures are listed in the study report with the zone EnergyPlus names, and the methodology names this as a limit of adiabatic interior walls around an isolated high-gain unconditioned zone (study report §4.3 (research/lod-study/report/2026-10-29-lod-study-report.md), methodology §M9 (research/lod-study/report/2026-10-29-lod-study-methodology.md)). - Why. With IWHT on the same zones run in R2 and R3; the divergence is a property of the boundary condition with that load, not of the zoning or the writer. Nothing was relaxed.
D-202 — R5's timing gate: costs reported as medians of site ratios; the repeat check of the long sites run once¶
- Date: 2026-10-29 · Decided by: the controller, under the owner's blanket authority for the public-release stages (stage C4) · Status: Accepted · Closes the open question of D-200
- Decision. 192 of 218 R5 sites meet the < 5 % processor-time CV gate; the misses are explained (10 short runs where the 15.6 ms clock step is about 2 % of the value; 16 long sites slowed 13 to 24 % late in repeat 3 for a reason not established, D-200). The study reports costs as medians of site ratios (test site over reference, each the median of its three repeats), states the per-site CV distribution (median 2.2 %, P90 5.3 %, largest 12.9 %) and the misses, and claims no per-site precision below that. The gate and the tolerances are unchanged. The medium office and the primary school were not among the 12 timed test cases; that is named as a limit.
- Repeat check, run once. The runner cannot select sites of a suite, but its IDF-folder mode times exactly the IDFs given: the 34 R5 sites of at least 4 s (6 test cases with their references) were copied from the R5 suite to
D:\bgdiag\r6-retime\idfand run with--weather D:\bemgen-weather\tmy3 --timing-repeats 3(main phase 14 at a time, then the serial timing pass), BEMGend32fa2a(only documents changed since R5'sb29931c), runrun_20261029-144634. The machine was idle before it (5-minute average 1.7 %, at most 4.5 %, nosmart-meter-calibrationor other Python process); during the timing pass (14:50 to 15:32 local) total load averaged 4.8 %, at most 8.1 %, in 30-s samples. Outcome: 102 of 102 timing runs complete; no site at or above 5 % (largest 4.8 %, median 2.05 % against R5's 3.73 % for the same sites; R5 had 16 misses among them); repeat 3 over the mean of repeats 1 and 2 has median 1.000 (largest 1.073), so R5's late slowdown did not recur. The absolute processor times are a median 15 % above R5's, and the 28 test sites' ratios to their references moved by a median of −4.5 % (−17 % to +5 %). The misses did not persist, but a session's level moves; per-site ratios are reproducible to about ±15 % between sessions, which the report states (study report §5.5 (research/lod-study/report/2026-10-29-lod-study-report.md)).
D-203 — R6: the study report, the methodology, and the consolidated recommendations (schema 1.1.0)¶
- Date: 2026-10-29 · Decided by: the controller, under the owner's blanket authority for the public-release stages (stage C4); the routine choices below by the R6 builder · Status: Accepted; the documents are for the owner's review
- What was written.
scripts/lod-study/study_tables.py(withtests/test_study_tables.py) reads the R3, R4, and R5 stage folders read-only and writes (1) the study report (research/lod-study/report/2026-10-29-lod-study-report.md): purpose, method, the R3 recommendations per prototype × climate zone × vintage × orientation (11 tables, 264 rows: recommended zonings, stored errors, benchmark loads, the closest zoning of the 46 cells without one), the R4 sub-study by heat-transfer setting and its 24 failures with the zone EnergyPlus names, the R5 costs by prototype, zoning, and recommended zoning, the repeat check of D-202, deduplication rates, and the limits; (2) the methodology (research/lod-study/report/2026-10-29-lod-study-methodology.md), §M1 to §M10, every number with its source; (3)research/lod-study/results/:lod-recommendations.json(R3's 264 entries, 218 with a recommendation, unchanged, plus the serialcostof the 12 timed references and 206 timed test sites and atimingblock),lod-recommendations-ht.json(R4, 22 entries, 18 with a recommendation, separate), andprovenance.json(BEMGen builds2ed856cfor R3/R4,b29931cfor R5,d32fa2afor the repeat check; suites and runs; atlas inputs 2.0.096a2e928; EnergyPlus 26.2.04bd7a1f26f; the threshold; the gate verdicts; the SHA-256 of sources and files). The tables are generated betweentable:markers;--checkverifies them and the files. - Schema 1.1.0.
lod-recommendations-v1.schema.jsonaccepts version 1.0.0 or 1.1.0; 1.1.0 adds only optional members (timing, andcoston a reference and a zoning), recorded in its$comment. A test validates the committed file and a 1.0.0 document. report.pyfix. A site that did not complete has no row in the runner'ssites, so its zoning was null in the recommendations; it is now taken fromsuite_sites(fill_suite_zoning, test-first: the two tests failed on the missing function). R4's report was written again intoD:/bgdiag/r6-ht-report(the stage folder stays read-only); it differs from the first drop only in the 24 failed zonings' zoning (checked entry by entry;duplicates.csvandmetrics.csvbyte-identical).- Routine choices. Costs are matched to R3's zonings by site name (test case and zoning code); the 218 timed IDFs of R5 equal R3's apart from their comment lines (build stamps), checked file by file. The consolidated JSON is written compact (7.8 MB, against 11.1 MB indented); R4's file is
report.py's output as written (3.6 MB). These are the published, derived recommendation files the run plan's §9 names, committed as the study's results (no IDF, database, or hourly output is committed). In R4 a recommendation "with IWHT or FHT off" is often the deduplicated twin of the both-on zoning (deduplication keeps "off" first in zoning order); the report says so rather than changing the rule. P90 of the CV is computed as type 7 (5.26 %); D-200's 5.25 % used a lower-rank estimate. - Checks.
python -P -m unittest discover -s scripts/lod-study/tests: 46 passed.study_tables.py --check:TABLES CURRENT.scripts/verify.ps1: see the change description. - Not done. No Rhino step is involved. The atlas site's import of the consolidated drop is atlas work (run plan §9). The cause of R5's late slowdown is still not established; it did not recur in the repeat check.
D-204 — Version 1.7.0: atlas release 2.2.0 and Schedule Draw on the released generator 4.0.0¶
- Date: 2026-10-30 · Decided by: Cheng Xuan Li (move BEMGen to atlas release 2.2.0 and generator 4.0.0 and release 1.7.0, with blanket authority to merge, push, tag, and publish the releases once verify and the gates are green, as for 1.6.0, D-197); the controller (the preparation and the routine choices below) · Status: Accepted
- Version. 1.7.0, prepared on
release/v1.7.0frommainat38ff521, a minor version as D-125, D-175, and D-197 reason: D-064 waives backward compatibility, and no component or parameter GUID changed or moved sincev1.6.0(each of its 103 GUIDs is still in the same source file ofsrc/Lod.Grasshopper; none is new). No component, input, or output is added or removed; Schedule Draw's input descriptions change with what they take (D-205). The icons are byte-identical tov1.6.0's. - What it holds since
v1.6.0: the LOD study runs and report onmain(D-193, D-200 to D-203, PRs #9 to #11; tools and documents, no plugin change), and this preparation: - Atlas release 2.2.0 and generator 4.0.0 (D-205, ADR-035, design note): the embedded program JSON 5.4.0 schema with the release pattern, so 2.0.0, 2.1.0, 2.2.0, and later labels read; Schedule Draw and the suite's
schedules:on request and bundle schema 4.0.0, release program IDs, the export's view, the release folder'sgeneration.json, setting 2.0.0, and the bundle'sreleasein the provenance; the recorded draws recorded again with the 4.0.0 wheel. - The version in
Directory.Build.props; the 21 example definitions rebuilt with the 1.7.0 plugin (BEMGEN_EXAMPLES_WRITE=1),schedule-draw.ghwith the release 2.2.0generation.jsonand setting 2.0.0 in its panels (routine choice: the same layout and Stream Filter gate as in 1.6.0); the smoke specschedule-drawchecks 4.0.0 and the release in the provenance; the release-1.7.0 checklist for a person in Rhino, appended to grasshopper-smoke-test.md. - Checks on the release candidate:
- Definitions saved with 1.6.0. The 21 example files as committed before the rebuild (saved with the 1.6.0 plugin) reopened with the 1.7.0 plugin (
1.7.0+edf06a9): every archive read without a warning or error, and all 21 solved with one Validate True and no runtime error (63 checks passed); the one warning, Program JSON'sProgramJsonNotInstantiatedof the two hot-water zone gain records inschedule-draw.gh, is 1.6.0's. Its Run true was not tried with the 1.6.0 panels: their setting 1.0.0 is refused by design (D-205; an item of the person-in-Rhino checklist). - Examples. All 21 rebuilt with the 1.7.0 plugin; each reopened and solved with one Validate True and no error; the same one warning. With
BEMGEN_SCHEDULE_COMMANDset toD:\bemgen-venv-schedules\Scripts\atlas-schedules.exe(generator 4.0.0),schedule-draw.ghwith Run true solved with no error, Validate True, and the drawn lighting schedule in the IDF (66 checks passed). - Rhino smoke. All 27 specs of
scripts/rhino-smokein Rhino 8.25.25314.11001 on the candidate's plugin code (9cad338), withBEMGEN_SCHEDULE_COMMANDset: 1519 Validate True and none False, 1003 spec checks passed and none failed (1.6.0: 1002; the new one checks the release in the draw's provenance), no missing component or missing expected error, no exception, nothingSKIPPED; runtime errors only in the error scenarios and, in the DOE specs, on the component placed without a program map, as in 1.6.0. Theschedule-drawspec drew with generator 4.0.0 on atlas release 2.2.0 (manifest67e73e55…7ece4) and noScheduleDrawPreRelease, the same text twice and another for seed 43. Another project's Python workers (about ten processes) ran throughout; no EnergyPlus batch was started. scripts/verify.ps1on the candidate's tree: VERIFY PASSED, 0 warnings: Core 1040, Generators 3051 (1 skipped), Integration 3629, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 332 (2 skipped), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163.- Fresh-clone gate. A clean clone of
release/v1.7.0at93c9f96from GitHub (D:\BEMGen-gate\v1.7.0) passedscripts/verify.ps1with 0 warnings and the same counts: Core 1040, Generators 3051 (1 skipped), Integration 3629, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 332 (2 skipped), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163. The commit that records this changes only this entry. - Sync-docs. Applied: README (version, the 1.7.0 sentence), AGENTS.md (the Done line, the Schedule Draw bullet), the roadmap's progress log, the design note's status,
examples/README.md(schedule-draw.gh), the smoke README and the Schedule Draw checklist (generator 4.0.0 and the new files), ADR-033 (amended by ADR-035) and the Schedule Draw architecture page, the program JSON, construction JSON, and pipeline pages and the schedule-generator requirement (release 2.2.0, generator 4.0.0), the fixture README, the test-suite README and spec schema.python scripts/docs-export/developer_pages.py --check: 114 pages checked, the one known redaction. The public site describes 1.6.0 (reference-source.jsonat4f1b806); the docs export of the tagged commit, its import in the docs repository, and that repository's ownsync-docsfollow the tag. - Release notes.
- Changed: Schedule Draw uses the atlas schedule generator
atlas-schedules4.0.0, installed from the wheel of the atlas releaseatlas-schedules-v4.0.0(check it with that release'sSHA256SUMS.txt); the wheel brings atlas release 2.2.0 with it. Its Generator JSON input now takes thegeneration.jsonof the program's folder in atlas release 2.2.0 (atlas-release-2.2.0.zipof the atlas releasev2.2.0,v2.2.0/<family>/<template>/<type>/generation.json) in place of the earliergenerator.json, and its setting is version 2.0.0 (generator 4.0.0, bundle schema 4.0.0, noview: the program JSON's own version, filled or source, chooses the values drawn from). The draw records the atlas release and its manifest checksum. - New: Program JSON and Construction JSON read exports of atlas release 2.2.0 (the contracts did not change; 2.0.0 and 2.1.0 exports remain valid), and later releases of the same program JSON 5.4.0 contract are read without a new version of BEMGen.
- What a user with stored 1.6.0 draws changes: a drawn program JSON from 1.6.0 still reads. To draw again, replace the
generator.jsonpanel with the release 2.2.0generation.jsonand the setting with a 2.0.0 one (a 1.0.0 setting and agenerator.jsonare refused with what to give instead); BEMGen then writes the request for generator 4.0.0 itself; a suite's stored 3.0.0 request (draws/<test case ID>.request.json) run by hand converts once withatlas-schedules translate. For the recorded DOE Ref medium-office draw the schedules are value-identical to 1.6.0's; the atlas reports the same for 263 of its 266 sampled generator 3.0.0 bundles, the other 3 being retired hotel corridor compositions, which Schedule Draw does not draw; the atlas's 90.1 apartment-building common-area recipes (fixed) now carry the release's heat fractions, so their draws can differ. - What opens differently: definitions saved with 1.6.0 open with their saved values; the examples are rebuilt with 1.7.0.
- Not yet performed by a person: the release-1.7.0 checklist in Rhino 8.
- What follows: the merge (PR #12), the tag
v1.7.0on the merge commit, the releases onEnvironmentalSystemsLab/BEMGen(notes) and on the docs repository (the files ofscripts/package-release.ps1built from the tagged commit, withSHA256SUMS.txt, marked latest), and the docs export ofv1.7.0imported by the docs repository (D-206). - For a person in Rhino 8: the release-1.7.0 checklist (installing from the release files,
schedule-draw.ghwith and without Run on 4.0.0, a 1.6.0-savedschedule-draw.gh, the viewport previews).
D-205 — Atlas release 2.2.0 and the released generator 4.0.0: the 5.4.0 schema with its release pattern, Schedule Draw on release IDs and the export's view (ADR-035), the recorded draws recorded again¶
- Date: 2026-10-30 · Decided by: Cheng Xuan Li (version 1.7.0: pin atlas release 2.2.0 and
atlas-schedules4.0.0, as the atlas's ADR 0051 §8 asks of BEMGen, under the blanket authority of D-204); the controller (the design of ADR-035 and the routine choices below) · Status: Accepted - Atlas release 2.2.0. A delta on 2.1.0 (release, atlas
2b3a6870,atlas-release-2.2.0.zipchecked with itsSHA256SUMS.txt:204675cb…f343; itsv2.2.0/manifest.jsonhas the SHA-25667e73e55…7ece4the atlas names): each generated folder'sgeneration.jsonanddefinition-map.jsonadded, everygenerator.jsonremoved; program IDs unchanged. The seven program fixtures exported at2b3a6870(python -m scripts.atlas_exports --program <ID> --version <version>,PYTHONHASHSEED=0, in agit archiveof that commit outside the atlas checkout) are equal to the release 2.0.0 fixtures as JSON apart fromsource.release(atlas-release-2.2.0), all seven. The atlas changed the 5.4.0 schema in place once more:source.releaseis the pattern^atlas-release-\d+\.\d+\.\d+$in place of the enum of four labels (two places, otherwise byte-identical). BEMGen's embedded copy refused every 2.2.0 export (ProgramJsonSchemaViolationat/source/release): the new testsAnExportOfAtlasReleaseTwoPointTwoReads(seven fixtures) andALaterReleaseLabelReadsfailed so before the change. The embedded schema and its fixture copy are now2b3a6870's (SHA-25608844cbb…503e; the manifest'sprogram_schema_atlas_commitnames that commit);AReleaseLabelOfAnotherShapeIsRefused(five labels, among thematlas-release-2.2andatlas-release-2.2.0-rc1) replacesAReleaseTheSchemaDoesNotNameIsRefused, whose label 2.2.0 the contract now admits; the 2.1.0 test stays. The construction schemas (unified 3.1.0, detailed 4.3.0, mass 1.1.0, ground 1.0.0) are byte-identical at2b3a6870and have no release enum; nothing else needed. User-facing texts that named releases 2.0.0 and 2.1.0 name 2.2.0 too. - Generator 4.0.0 (
atlas-schedules-v4.0.0;sha256sum -c SHA256SUMS.txt: the wheelatlas_schedules-4.0.0-py3-none-any.whl8302c0ab…7ccd3and the sdist OK), installed withpip install --upgrade <wheel>intoD:\bemgen-venv-schedules(Python 3.14.7);atlas-schedules --versionprintsatlas-schedules 4.0.0 (request schema 4.0.0); reads atlas release 2.2.0 from …\atlas_schedules\data\atlas-release (generator view). Its seed policy is 3.0.0's (commercial_group_seed(42, "zone")and(42, "Z1")give the values the existing test pins). ADR-035 amends ADR-033: the pins (generator 4.0.0, request and bundle schema 4.0.0, data release 2.2.0 by its manifest SHA-256; the pre-release logic kept), the program setting is the release folder'sgeneration.json, the request names the export'sidand itsversionasvalues, shared services come from the export's hot-water demands that name a service of the files, the methodscomstock_hoursandfixedonly, a slot's load among several by the bound magnitude, the bundle'sreleaseand owners'valueschecked and recorded, and the setting 2.0.0 withoutview. - How an export maps to the request. The release program ID is the export's
id(equal tosource.program_id; the same in exports of 2.0.0, 2.1.0, and 2.2.0).valuesis the export'sversion: a filled export asks forfilledand a source export forsource; where the program's generation block has no view of that name, the request asks forsource, the generator's own default for such a block, with the informationScheduleDrawValuesSource. This is the case of the DOE Ref programs, whose blocks carry onlysource: generator 4.0.0 refusesvalues: filledfor them (unsupported_input at /values: … has no filled view, observed), and under 3.0.0 their filled exports were drawn with the source recipe too. The magnitude check stays the guard. - Tests first. The draw's tests were written before the code and failed by not compiling (
BundleRelease,ScheduleDrawResult.Release,DrawnInstance.Values,ScheduleDrawCodes.ValuesSource,ScheduleDrawSetting.PinnedRelease…,GeneratorRecipes.ServiceConfigurations, the newGeneratorRecipe); after the code one test failed on its own miscount (it expected oneScheduleDrawValuesSourcenote; the draw has two instances, so two, one per instance, which the test now checks by subject). The review (D-111) found no blocking problem; its findings were applied: the load choice among several now compares withinToleranceSettings.RelativeLoadas the magnitude check does, and three tests pin it (another magnitude before the bound load, two loads of the bound magnitude refused asScheduleDrawAmbiguous, a hot-water load without a value needing no service); a control character in a comment, stale comments, and the Schedule Draw checklist's file names were fixed. The reviewer also scanned the 734comstock_hoursprograms of release 2.2.0 in both views and found BEMGen's shared services equal to the atlas assembler's required services for every one. - The recorded draws.
Fixtures/draw/: the DOE Ref 1980–2004 and 90.1-2019 medium offices'generation.json(byte for byte from the release zip, equal to2b3a6870's),setting.json2.0.0,request.jsonas BEMGen now writes it (valuessource), andbundle.jsonfrom the wheel (fbdf6a5e…acfe; three runs, the console script twice andpython -m atlas_schedulesonce, byte-identical). Its schedules and owners equal the generator 3.0.0 bundle's (a94624b0…1838, D-198) apart from the owners' newvalues; the versions and thereleaseblock are new. A second recorded draw, the 90.1-2019 medium office's source export as one instance of 250 m² (b1620ee2…ff19, two runs identical), covers a source view, its configuration, its hot-water load, and a second lighting load without a value.generator.doe-ref-1980-2004.medium-office.jsonis gone (in git history). - The gated test.
RealDrawTests(BEMGEN_ATLAS_GENERATOR=D:\bemgen-venv-schedules) passed withBEMGEN_ATLAS_COMMAND=D:\bemgen-venv-schedules\Scripts\atlas-schedules.exeand again withD:\bemgen-venv-schedules\Scripts\python.exe -m atlas_schedules: both tests (the recorded DOE Ref draw drawn twice, the 90.1 source draw), generator 4.0.0, noScheduleDrawPreRelease, release 2.2.0 with the pinned manifest, both bundles equal to the recorded ones byte for byte and the second run equal to the first (4.85 s and 4.45 s for one draw). - What a user changes. Schedule Draw's inputs, outputs, defaults, and GUID are unchanged; its descriptions name 4.0.0,
generation.json, and setting 2.0.0. A setting 1.0.0 is refused with what to change, and agenerator.jsonwith the file to give instead. A suite'sschedules.recipeslistsgeneration.jsonfiles. A drawn program JSON stored by 1.6.0 still reads; to draw it again, give the 2.2.0generation.jsonand a 2.0.0 setting. - Checks:
scripts/verify.ps1before each commit, VERIFY PASSED with 0 warnings: Core 1040, Generators 3051 (1 skipped), Integration 3629, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 312 (1 skipped) after the schema, 329 (2 skipped, the two opt-in real draws) after the draw, and 332 (2 skipped) with the review's tests, Simulation 275 (14 skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163. - Not done. The software-only launcher's reviewed-source policy, which a person refreshes for changed sources (as D-196 left it).
D-206 — The public site and the release files of 1.7.0¶
- Date: 2026-10-30 · Decided by: the controller (a routine step of D-204, under the owner's authority to publish) · Status: Accepted
- What. The docs export of the tag
v1.7.0(d58ac75, the merge commit of PR #12;scripts/docs-export/export.ps1in the gate clone checked out at the tag, Rhino 8.25.25314.11001: 103 icons, 63 screenshots, 114 developer pages, 0 gaps, the one known redaction) was imported into the docs repository with itsimport_reference.py(reference-source.json: 1.7.0 atd58ac75); unlike 1.6.0 (D-199), the import reported no public-content problem, so the site is the tag's documents. That repository's hand-written pages were brought to 1.7.0 by itssync-docs(what's new, getting started's file names, the program JSON releases,schedule-draw.gh's panels);mkdocs build --strictpassed without a warning and its 18 importer tests passed; its commitsdb4995dandd449072were pushed to itsmain, and the Pages workflow deployedd449072(success; the site names 1.7.0). - The tag checked. The gate clone
D:\BEMGen-gate\v1.7.0, checked out atv1.7.0(tree equal to D-204's gate commit93c9f96apart from the decision log), passedscripts/verify.ps1again with 0 warnings and D-204's counts.scripts/package-release.ps1built the release files on that clean tagged checkout (SHA-256:bemgen-1.7.0-examples.zip3ce45a9a…0755,bemgen-1.7.0-rh8_19-win.yak7e224b91…0cd6,bemgen-1.7.0-rh8-win.zip733ca25f…038d2f). - Releases.
EnvironmentalSystemsLab/BEMGenv1.7.0with the notes of D-204;Energy-Atlas/BEMGen-docsv1.7.0with the same notes, the three files andSHA256SUMS.txt(checked withsha256sum -cbefore the upload), marked latest.
D-207 — The JSON editor window of D-128 merged into main; the program checksum of LF text on the release 2.x fixtures¶
- Date: 2026-10-30 · Decided by: Cheng Xuan Li (the atlas program picker note, phase 1, with every recommendation of its §6 accepted on 2026-10-30 and the authority to merge once
scripts/verify.ps1and the headless Rhino check pass; no tag, version, or release: 1.8.0 follows phase 3); the controller (the resolution and the routine choices below) · Status: Accepted - What.
feature/program-popup-window(D-128, 15 commits from3e46108, 489 behindmain) is merged intomainas it was approved: the strip Edit JSON… on Program JSON (ProgramJsonAttributes), the window (ProgramJsonDialog, its instruction now naming program JSON 5.4.0),SetJsonTextwith one undo event,ProgramJsonTextCheck, and the SHA-256 of the text with LF line endings inProgramJsonReader. Built onfeature/program-editor-window, fromdocs/atlas-program-picker(the note lands with it), in the worktreeD:\BEMGen\.claude\worktrees\program-editor-window. A merge commit, not a rebase: the branch's 15 reviewed commits keep their text, and the resolution is one commit to read. No GUID, input, or output changed; no component is new. - The nine conflicts. AGENTS.md and the roadmap keep
main's text (the roadmap's two JSON editor rows now say merged). The decision log keepsmain's entries and gains D-128 in its numeric place between D-127 and D-130, as written on the branch, with one line saying that it was added here.program-json.md, the smoke-test document, and the rhino-smoke README keepmain's text and gain the branch's editor section, checklist, and spec row, written for the 5.4.0 exports. In the code,ProgramJsonComponentkeepsmain's 2.2.0 component and gains the branch'sCreateAttributes,SetJsonText,CheckJsonText, and JSON description sentence;ProgramJsonDocumentkeepsmain's record with the branch'sSha256comment;MediumOfficeTestskeepsmain's tests with the branch's two checksum tests. - Moved to program JSON 5.4.0. The branch's tests and smoke spec used the 2.0.0 Medium Office and its raw export, which
mainno longer reads; they now usemedium-office-90-1-2019.filled.json, and the rejected example is the same text as the retired program JSON 2.0.0 (ProgramJsonVersionNotSupported). - A defect of the merge, fixed. On
mainthe reader resolves loads by Climate Zone and Context, which the branch's check did not pass: OK refused the IECC dwelling (ProgramJsonContextRequired) on a component whose Context gives its foundation, while the solve accepted it. The check now reads the component's Climate Zone (one text) and Context (key=value items, the solve's own parse) as it reads Year and Holidays, else none. The new smoke scenarioclimate zone and contextfailed 2 of its 5 checks before the fix and passes after it; it is the only test of the component's part, sinceLod.Grasshopperhas no unit tests.TheCheckReadsOnTheGivenConstructionContextcoversProgramJsonTextCheckwith the options and passed before the fix too. - The checksum's effect. The note (§4.1) expected no recorded checksum to change, because the 2.0.0 fixtures of D-128 were LF. The release 2.x fixtures are not:
scripts/atlas-fixtures/export_fixtures.pywrites the atlas exporter's output as it comes, on Windows with CR LF line endings, andFixtures/atlas/manifest.jsonpins those bytes. So Source'sJson.Sha256for them is now that of the same text with LF: the Medium Office'sf121f346…(the file's) becomes1331af85…in the import, IDF, and preset-snapshot tests, and Schedule Draw'ssource_program_sha256of the DOE Ref fixture becomes65c19c07…(the note also said that nothing else readsJson.Sha256; the draw's provenance does). The expected values were computed outside BEMGen, bysed 's/\r$//' <file> | sha256sum. No fixture file changed, and the manifest still pins every file's bytes. For a user the effect is D-128's: a definition that stored CR LF text gets a new Source once, the checksum of the same text with LF. The other checksums of a draw's provenance (setting_sha256,recipe_sha256) stay those of the given bytes, and so do the test-suite generator's file checksums (the names ofinputs/<sha256>.jsonand the input rows ofsuite.duckdb, ADR-020): they identify files, whileJson.Sha256andsource_program_sha256identify the program text, so for a program file with CR LF line endings the two differ. ADR-033 decision 10 is amended to say so. - How it was built. The merge (
e6c49f5); the climate-zone and context tests written first (20bc6e7, the spec failing as above), then the fix (fd17fba); this entry. A review by a second agent (D-111): approved with changes, all applied: the window's instruction still asked for program JSON 2.0.0 and now names 5.4.0; ADR-033 decision 10 and this entry now say which checksums are of the LF text and which of the bytes (the draw's setting and recipes, the suite's input files); the reader's comment, this entry's section reference and its wording on what tests the fix, the note's as-built line, and the roadmap's counts were corrected. The reviewer recomputed the changed checksums and found no fixture or snapshot change beyond them. - Checks:
scripts/verify.ps1VERIFY PASSED with 0 warnings: Core 1040, Generators 3051 (1 skipped), Integration 3629, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 346 (2 skipped; 332 onmain, +14: the branch's 13 and the context test), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163.- The headless Rhino check: all 28 specs of
scripts/rhino-smokein Rhino 8.25.25314.11001 onfd17fba(before the review's text fixes), withBEMGEN_SCHEDULE_COMMANDset to the generator 4.0.0 console script: 1519 Validate True and none False, 1048 spec checks passed and none failed (1.7.0: 1003; the 45 ofprogram-json-editorare new), no exception, missing component, or missing expected error, nothingSKIPPED; runtime errors only in the error scenarios and on the DOE components placed without a program map, as in 1.7.0. - Open. For a person in Rhino 8: the editor window checklist (the strip in both canvas themes, the double-click, the window, paste, the error list, Cancel, Esc, undo and redo, a reopened definition, the context case). Phase 2 (D-208) follows.
D-208 — Get from Atlas on Program JSON: the program of the page's address, downloaded from the atlas site and checked; the clipboard removed¶
- Date: 2026-10-30 · Decided by: Cheng Xuan Li (the atlas program picker note, phase 2, with every recommendation of §6 accepted on 2026-10-30: the network after a click in a window, never in a solve, from the atlas site only; the site root fixed; the filled version by default, the source version a choice; Windows only; the authority to merge once
scripts/verify.ps1and the headless Rhino check pass; no tag, version, or release: 1.8.0 follows phase 3); the controller (the realisation and the routine choices below) · Status: Accepted · Amends: D-126 and ADR-018, as to the network; D-129 (its four open items are settled here) · Builds on: D-128, D-129, D-207 - What.
feature/program-popup-web(D-129) is merged intomain(a merge commit; D-129 is added to the log in its numeric place), then changed as the note's §4.2 sets out, onfeature/atlas-program-sitein the worktreeD:\BEMGen\.claude\worktrees\atlas-program-site. The strip Get from Atlas… opensProgramJsonAtlasDialogon the site's catalogue; the web view goes nowhere outsidehttps://energy-atlas.github.io/archetype-atlas/, and the status line names an address it refused; Use this program, enabled on a program's page, downloads the filled export (or the source one, by a choice in the window), checks it, runs the editor's check, and stores it withSetJsonText.ClipboardWatcher, Use clipboard now, and the copy instructions are gone; pasting stays possible through Edit JSON…. No GUID, input, or output changed; no component is new; the JSON input's and the component's descriptions name the strip and say that the solve reads no file or network. Lod.ProgramJson.Site(no Rhino dependency, the transport injected; program-json.md). Its public API, which phase 3b reuses:AtlasSite(IAtlasSiteTransport transport, Func<DateTimeOffset>? clock = null);static Uri Root,Catalogue,IndexAddress;static TimeSpan IndexCacheTime(10 minutes),DefaultTimeout(30 s);static bool IsOnSite(Uri address);Task<Result<AtlasSiteIndex>> ReadIndexAsync(CancellationToken cancellationToken = default);Task<Result<AtlasProgramDownload>> DownloadAsync(string programId, AtlasProgramValues values, CancellationToken cancellationToken = default).AtlasSiteIndex:static Result<AtlasSiteIndex> Parse(byte[] utf8),string Release,string ReleaseLabel,IReadOnlyDictionary<string, AtlasSiteProgram> Programs,string? ProgramForPage(Uri address);AtlasSiteProgram(string Id, string Page, string? Filled, string? Source, string? FilledReason)withstring? StorePath(AtlasProgramValues values);enum AtlasProgramValues { Filled, Source };AtlasProgramDownload(string ProgramId, AtlasProgramValues Values, string Release, string StorePath, string Json).IAtlasSiteTransport.GetAsync(Uri address, CancellationToken cancellationToken)givingTask<Result<byte[]>>;HttpAtlasSiteTransport(TimeSpan timeout)(disposable);FileAtlasSiteTransport(string folder);AtlasSiteDiagnosticCodes.AtlasProgramPicker(AtlasSite site, Func<string, ProgramJsonTextCheck> check):static string? Refusal(Uri? address),OpenAsync,PageAsync(Uri address, …)givingAtlasPageStatus(bool IndexRead, string? ProgramId, bool FilledPublished, string? FilledReason, string Message), andPickAsync(Uri address, AtlasProgramValues values, …)givingAtlasProgramPick(bool Accepted, string? Json, AtlasProgramDownload? Download, IReadOnlyList<Diagnostic> Diagnostics, string Message).- Routine choices beyond the note (the controller's):
- The site's index has
filled: nullwith afilled_reasonfor 1166 of its 3051 programs (the IECC programs and the code-prototype dwellings, which the atlas exports as source only). A filled download of one is refused asAtlasSiteVersionNotPublishedwith the site's reason, never replaced silently by the source version; each program's page selects Filled in the window, a source-only one Source, and the status line says why. - The download also checks the export's
versionagainst the version asked for (AtlasSiteProgramMismatch), besides itsidandsource.release, and the index refuses two programs on one page and paths outside the root. - Decompression stops at 64 MiB (
ProgramJsonLimits.MaxInputBytes), so a store file cannot expand without bound; the HTTP body has the same bound, and the transport follows no redirect. - The window's logic lives in
AtlasProgramPicker(Lod.ProgramJson) and the two component methodsAtlasPageStatusAsyncandPickFromAtlasAsync, so the tests and the smoke check run it without the window;FileAtlasSiteTransportserves recorded files for them. The editor's options (calendar, climate zone, context) are read when Use this program is clicked, on the window's thread. ProgramForPagecompares the address's path as the server reads it, not decoded and with case; a link that opens a new window opens in the same view when it is on the site and is refused otherwise; a cancelled read is the resultAtlasSiteCancelled, not an exception.- One
AtlasSitewith one HTTP transport per Rhino session (ProgramJsonComponent.SessionSite), created when an atlas window first opens, so the index cache is shared by every Program JSON. - Fixtures (
tests/Lod.ProgramJson.Tests/Fixtures/site/,-text), recorded from the live site on 2026-10-30 by the newscripts/atlas-fixtures/record_site_fixtures.py, which checks every store file against its name;recorded.jsonpins them: a trimmedsite-manifest.json(release 2.2.0, manifest67e73e55…7ece4,partial: false; two programs, one construction set, one zoning page), and three store files:90-1-2007.medium-office-detailed.source.medium-office-open-office.648259filled (dcf01d11…3e07a) and source (e94691dc…33e02), andiecc-2006.full-service-restaurant.source.dining.371b01source (fe6e4898…04b51; its filled isnull). - Tests first.
AtlasSiteTests(the index, the partial index, malformed keys, pages and store paths, a page's program for six forms of its address and none for eight others, the every-page round trip of the fixture with its three downloads, the tampered hash, another program's file, the other version's file, another release, not gzip, not program JSON, the 64 MiB bound, the cache time and a failed read not kept, transport failures, only site addresses asked for, the HTTP transport offline) andAtlasProgramPickerTests(refusals, the page status, the pick filled and source, the refusals of a pick, a rejected check, a failed download, the file transport) were committed failing to compile; the smoke scenarioget from atlasfailed before the window change (an exception on the missing method, and the JSON description). One live test,LiveSiteTests, runs only withBEMGEN_ATLAS_SITE_LIVE=1. - How it was built. The merge of the web branch (
06e740a), the fixtures, the tests,AtlasSite, the picker and the file transport, the smoke scenario, the window, these documents. A review by a second agent (D-111), which fetched the live index and found every ID, page, and store path of its 3051 programs accepted by the parser and the fixtures' entries equal to the live ones: changes asked, all made (d2eab26, the tests first, and08cfb0b): links withtarget="_blank"(every page's footer has one) would have opened WebView2's own window, which goes anywhere, soOpenNewWindowis handled; the version choice stayed on Source after a source-only page, so it is set on every program's page fromAtlasPageStatus.FilledPublished(new); the window's handlers now stop when it has closed, and a second click during a download is ignored; the file transport's traversal test now targets a file that exists, and the transport no longer throws on the root without its slash or on a NUL;ProgramForPageno longer decodes the path; a cancelled index read is a result; the 64 MiB in the message comes fromProgramJsonLimits; the HTTP transport's message covers an answer that cannot be read. The test of a refused loopback port, which timed out under load in the first verify (Windows retries a refused connection), is replaced by two loopback servers: one closes the connection (AtlasSiteUnreachable), one never answers (AtlasSiteTimedOutafter 1 s). - Checks:
scripts/verify.ps1VERIFY PASSED with 0 warnings (on62080da): Core 1040, Generators 3051 (1 skipped), Integration 3629, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 423 (3 skipped: the two real draws andLiveSiteTests; 346 after D-207, +77), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163. The first verify, before the review, failed one test (the refused loopback port, timed out under load), which the review's changes replaced.LiveSiteTestswithBEMGEN_ATLAS_SITE_LIVE=1on 2026-10-30: passed (live release 2.2.0, 3051 programs; both recorded store paths of the medium office served and checked).- The headless Rhino check: all 28 specs of
scripts/rhino-smokein Rhino 8.25.25314.11001 on62080da, withBEMGEN_SCHEDULE_COMMANDset to the generator 4.0.0 console script: 1519 Validate True and none False, 1065 spec checks passed and none failed (D-207: 1048;program-json-editor62, of which the 14 ofget from atlasand 3 of the atlas strip are new), no exception, missing component, or missing expected error, nothingSKIPPED. The scenarioget from atlasfailed before the window change, as above. A re-review of the fixes approved them; its one low note (the opening status line may give the catalogue's message without the program count) changed the checklist item to ask only for the release. - Docs. ADR-018 amended (the solve reads no file or network; the window may after a click, from the site only);
program-json.md(the atlas window section in place of the experimental one); the rhino-smoke README; the atlas window checklist for a person; AGENTS.md (theLod.ProgramJsonrow, the gated live test); the fixture README. - Afterwards. The tag
archive/program-popup-webatorigin/feature/program-popup-web(c338f31), pushed; the branchesfeature/program-popup-windowandfeature/program-popup-webdeleted onoriginand locally. - Open. For a person in Rhino 8: the atlas window checklist (the web view, links leaving the site, the version choice, offline). Phase 3 (D-209) follows; 1.8.0 (D-210) after it.
D-209 — Atlas program maps on the DOE Programs components, and Generator JSON from the atlas site (ADR-036)¶
- Date: 2026-10-30 · Decided by: Cheng Xuan Li (the atlas program picker note, phase 3b, with every recommendation of §6 accepted on 2026-10-30: the strip on each DOE Programs component, filled by default, all five commercial families with their coverage, keys without a program refused with Presets output with those positions empty, Generator JSON from the site with the same strip on Schedule Draw, Windows only; the authority to merge once
scripts/verify.ps1and the headless Rhino check pass; released in 1.8.0, D-210); the controller (the design of ADR-036 and the routine choices below) · Status: Accepted · Amends: ADR-018 and ADR-035 (where Generator JSON may come from) · Builds on: D-207, D-208; the atlas's ADR 0052 (live since 2026-10-30) - What. Built on
feature/atlas-program-mapsin the worktreeD:\BEMGen\.claude\worktrees\atlas-program-maps, frommainat6ff8802. Get from Atlas… at the bottom of each of the fifteen DOE … Programs components opens a window that lists the five commercial families holding the prototype's folder with their coverage; OK downloads every key's program, verified, and stores one text in the component with one undo event; Clear returns to the illustrative presets. The solve imports the stored text offline with Program JSON's import on four new optional inputs (Climate Zone, Year, Holidays, Context), keeps the DOE reference building's window ratios, and gives a new output JSON after the existing three. Schedule Draw gains the same strip, which stores thegeneration.jsonof the folder of the program in its Program JSON. No GUID changed; no component is new; the icons are unchanged. Lod.ProgramJson.Site, additively (program-json.md):AtlasSiteProgramgainsFolder,Mode,SpaceType,BuildingType(init properties, so D-208's constructor stands);AtlasSiteIndexgainsFolders(AtlasSiteFolder,AtlasStoreFile),ReleaseManifestSha256,FoldersFault, andMatchKeys;AtlasSite.DownloadGenerationAsync(AtlasGenerationDownload);AtlasProgramMap(bemgen-atlas-program-map1.0.0),AtlasProgramMapPicker,AtlasProgramMapImporter,AtlasGenerationPicker; the codesAtlasSiteFolderNotInIndex,AtlasSiteGenerationNotPublished,AtlasProgramMapInvalid,AtlasProgramMapMismatch,AtlasProgramMapKeysWithoutProgram.Lod.Generators:DoeAtlasFolders(DoeAtlasFamily) andDoeProgramPresets.KeyRatios(DoeKeyRatios).Lod.Grasshopper:AtlasStripAttributes,DoeAtlasMapDialog,ScheduleDrawAtlasDialog, andProgramJsonInputs(the context, calendar, climate-zone, and context readers of Program JSON, moved unchanged so both components share them).- Matching (the controller's decision, as the brief asked). A key matches the one program of its prototype's folder whose
modeissourceand whosespace_typeis the key, exactly; programs of modedepartmentandgeneralare never candidates, being the atlas's merges of source programs (*_Mixed), not a standards space type of the prototype.DoeAtlasProgramMapTestspins, against the recorded index of the 75 folders, that no department or general program has a key's space type, so the exclusion drops no candidate today, and fails if one ever does. Observed gaps (the same on the live index, checked by the reviewer): DOE Ref pre-1980 and DOE Ref 1980–2004 153 of 153, none; 90.1-2007 151 of 153, the secondary school'sGym - audienceand the small hotel'sGuestRoom; 90.1-2013 and 90.1-2019 149 of 153, those two and the hospital'sOfficeand the stand-alone retail'sRetail; no key ambiguous in any family. They are the expected gaps exactly. - Routine choices beyond the note (the controller's):
- The five families only; the IECC templates, which also hold most of the prototypes' folders (110 of 153 keys resolve, source only, no office or supermarket folder), are not offered, since the note reconciled only the five (ADR-036 decision 4).
- OK stores a family with gaps (the keys left empty in the text, named in the window), so that the solve can output Presets with those positions empty for DOE Program Map; a family with no key matched, a failed download, or a rejected check stores nothing.
- The map is the component's own state (
Write/Read,RecordUndoEvent), not a hidden or visible input; the strip names the stored family and version. - Each key's preset is typed by its space type (
DoeSpaceTypes.Of), so no key takes the Service fallback of the illustrative map; the window ratios are the illustrative map's (KeyRatios), so the stacked window areas are unchanged. - A stored map is imported only when it is of the component's prototype, keys (in key order), one of its families with that family's folder and name, and each text is the program and version it is stored as.
- A fault in the atlas ADR 0052 fields of the index drops the folders, not the programs, so Program JSON's window keeps working (ADR-036 decision 2).
- Schedule Draw's strip finds the folder by the program JSON's
idin the index. None of the*-detailedoffice folders has ageneration.jsonon the site (90 of the 255 folders have one), so the programs of the DOE small, medium, and large office are refused with a message that says so (AtlasSiteGenerationNotPublished), an atlas limitation not worked around. - Fixtures (recorded 2026-10-30 from the live site by
scripts/atlas-fixtures/record_site_fixtures.py, extended):Fixtures/site/recorded again additively (its index gains the four program fields and the two programs' folders; store files byte-identical;recorded.json1.1.0); newFixtures/site-doe/, the 75 folders whole with their 1086 programs, the filled exports of the 90.1-2007 medium office's eleven keys, the filled and source exports of the 90.1-2019 quick-service restaurant's two keys and its folder'sgeneration.json(6893f941…7ef7), and the filled exports of the 90.1-2019 stand-alone retail's three keys with a program.scripts/rhino-smoke/fixtures/doe-programs-1.7.0.gh, the fifteen DOE Programs components wired into their generators and saved by the 1.7.0 plugin (1.7.0+d58ac75, byfixtures/doe-programs-1.7.0.pyrun with-SpecFileon av1.7.0worktree). - Tests first.
AtlasSiteFolderTests,AtlasProgramMapTests,AtlasGenerationPickerTests(ProgramJson),DoeAtlasFoldersTests(Generators), andDoeAtlasProgramMapTests(Integration: the pinned gaps, and an atlas map of the medium office through its generator, Semantic Merge, Stack Floors, and validation with the reference glazing) were committed failing to compile; one test's own input was inconsistent (a program's folder removed while its folder still listed it) and was corrected. The smoke specdoe-atlas-mapsfailed onmain's plugin (8 of 11 checks, the strip and the methods missing) before the components changed. The live checksLiveSiteTestsgainTheLiveSiteGivesTheRecordedDoeFolders. - Review (D-111). A second agent approved with two should-fix items and five nits, all applied (tests first, observed failing to compile): a fault in the folders no longer makes the whole index unreadable (
FoldersFault); the coverage text of a source-only program is now tested; ADR-036 says the decoded size; the window keeps the stored version; the window's options are read on the calling thread; the importer checks the map's family and folder; the generation pointer is escaped. The reviewer parsed the live index (3051 programs, 255 folders, 90 generation files), downloaded and checked all 90 generation files against the new checks, and found thesite-doefixture equal to the live data. A re-review of the fixes approved them. - Checks:
scripts/verify.ps1VERIFY PASSED with 0 warnings (ona93e96c): Core 1040, Generators 3087 (1 skipped; 3051 after D-208, +36), Integration 3642 (+13), Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 499 (3 skipped: the two real draws andLiveSiteTests; 423 after D-208, +76), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163. On the tip, with the live factTheLiveSiteGivesTheRecordedDoeFoldersadded, the same with ProgramJson 499 (4 skipped).LiveSiteTestswithBEMGEN_ATLAS_SITE_LIVE=1on 2026-10-30: both passed (live release 2.2.0, 3051 programs, 255 folders, 90 with a generation file; the recorded 75 folders equal to the live ones).RealDrawTests(BEMGEN_ATLAS_GENERATOR=D:\bemgen-venv-schedules, generator 4.0.0) with the console script and withpython.exe -m atlas_schedules: both tests passed, the bundles equal to the recorded ones byte for byte. No recorded bundle changed: D-207's LF checksum issource_program_sha256of BEMGen's provenance, which no request or bundle holds.- The headless Rhino check: all 29 specs of
scripts/rhino-smokein Rhino 8.25.25314.11001 ona93e96c(and before the review's fixes on231f335, the same counts), withBEMGEN_SCHEDULE_COMMANDset to the generator 4.0.0 console script: 1519 Validate True and none False, 1265 spec checks passed and none failed (D-208: 1065; the 200 ofdoe-atlas-mapsare new), no exception, missing component, or missing expected error, nothingSKIPPED. Theexamplesspec reopened the 21 committed examples, saved with 1.7.0, with no archive warning and every Validate True;doe-atlas-mapsreopenedfixtures/doe-programs-1.7.0.ghwith its fifteen components solving as new ones and its fifteen Validate True. - Docs. ADR-036 (new); ADR-018 and ADR-035 amended;
doe-components.md(the inputs, the JSON output, the atlas maps),program-json.md,schedule-draw.md; the atlas program map checklist; the rhino-smoke README; the fixture README; AGENTS.md (theLod.ProgramJsonrow); the note's status and as-built line; the roadmap. - Open. For a person in Rhino 8: the atlas program map checklist (the strips in both themes, the windows, OK, Clear, undo, offline reopening). The programs of the DOE offices' keys cannot be drawn until the atlas publishes
generation.jsonfor its*-detailedfolders (an atlas limitation). The software-only launcher's reviewed-source policy, which the owner refreshes. Release 1.8.0 follows (D-210).
D-210 — Version 1.8.0: the atlas site in BEMGen — the JSON editor window, Get from Atlas on Program JSON, and atlas program maps¶
- Date: 2026-10-30 · Decided by: Cheng Xuan Li (the atlas program picker note, §6: phases 1 to 3 released together as 1.8.0, with the authority to merge, tag
v1.8.0, and publish the releases and the public site once verify and the gates pass); the controller (the preparation and the routine choices below) · Status: Accepted - Version. 1.8.0, prepared on
release/v1.8.0frommainat3e3e9c8(the merge of PR #16), a minor version as D-125, D-175, D-197, and D-204 reason: D-064 waives backward compatibility, and no component or parameter GUID changed or moved sincev1.7.0(the 103 GUIDs ofsrc/Lod.Grasshopperare the same; none is new). The icons are byte-identical tov1.7.0's. - What it holds since
v1.7.0: the JSON editor window and the LF checksum (D-207, PR #14), Get from Atlas… on Program JSON (D-208, PR #15), atlas program maps on the fifteen DOE Programs components and Generator JSON from the site (D-209, ADR-036, PR #16); the version inDirectory.Build.props; the 21 examples rebuilt with the 1.8.0 plugin (BEMGEN_EXAMPLES_WRITE=1) and one new,doe-atlas-map.gh(routine choice: the DOE medium office at its defaults with DOE Medium Office Programs storing the filled 90.1-2007 map, picked at build time from the recorded siteFixtures/site-doe/, so it solves with no network; theexamplesspec now checks that its map is stored and used); the release-1.8.0 checklist for a person, appended to grasshopper-smoke-test.md. - Checks on the release candidate:
- Definitions saved with 1.7.0. The 21 committed examples (saved with 1.7.0) and
scripts/rhino-smoke/fixtures/doe-programs-1.7.0.ghreopened with the plugin ofa93e96c: every archive read without a warning or error, all 21 examples with one Validate True, the DOE definition's fifteen Validate True and its components solving as new ones (D-209's smoke run). - Examples. All 22 rebuilt and reopened: each Validate True, no runtime error; the one warning, Program JSON's
ProgramJsonNotInstantiatedinschedule-draw.gh, is 1.7.0's;schedule-draw.ghwith Run true and generator 4.0.0 drew (the drawn lighting schedule in the IDF);doe-atlas-map.ghsolved offline with its stored map (70 checks passed). - Rhino smoke. All 29 specs of
scripts/rhino-smokein Rhino 8.25.25314.11001 on the candidate4100bcb, withBEMGEN_SCHEDULE_COMMANDset to the generator 4.0.0 console script: 1520 Validate True and none False (1519 and the new example's), 1269 spec checks passed and none failed (D-209: 1265; the four new are the example's), no exception, missing component, or missing expected error, nothingSKIPPED. scripts/verify.ps1on4100bcb: VERIFY PASSED, 0 warnings: Core 1040, Generators 3087 (1 skipped), Integration 3642, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 499 (4 skipped: the two real draws and the two live site facts), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163.- Gated tests.
RealDrawTestswithBEMGEN_ATLAS_GENERATOR=D:\bemgen-venv-schedules(generator 4.0.0), with the console script and withpython.exe -m atlas_schedules: both tests passed, both bundles equal to the recorded ones byte for byte, so no recorded bundle was recorded again: D-207's LF checksum changessource_program_sha256of BEMGen's provenance, which no request or bundle holds.LiveSiteTestswithBEMGEN_ATLAS_SITE_LIVE=1: both passed (live release 2.2.0, 3051 programs, 255 folders). - Fresh-clone gate. A clean clone of
release/v1.8.0at16aa133from GitHub (D:\BEMGen-gate\v1.8.0) passedscripts/verify.ps1with 0 warnings and the same counts: Core 1040, Generators 3087 (1 skipped), Integration 3642, Export 3132 (1 skipped), ConstructionJson 311, ProgramJson 499 (4 skipped), Simulation 275 (14 opt-in skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 163. The commit that records this changes only this entry. - Sync-docs. Applied: README (version, the 1.8.0 sentence), AGENTS.md (the Done line), the roadmap's progress log and its JSON editor row, the design note's status (Done),
examples/README.md(doe-atlas-map.gh), the rhino-smoke README (examples), the release checklist. No component description cites a decision;python scripts/docs-export/developer_pages.py --check: 117 pages checked, the one known redaction. The public site describes 1.7.0 (reference-source.jsonatd58ac75); the docs export of the tag, its import, and the docs repository's ownsync-docsfollow the tag. - Release notes.
- New: Get from Atlas… on Program JSON: a window shows the Energy Archetype Atlas site (https://energy-atlas.github.io/archetype-atlas/); on a program's page, Use this program downloads its program JSON (filled by default, or source), checks it against the site's index, and stores it. Edit JSON… edits or pastes the text in a window.
- New: Get from Atlas… on each DOE … Programs component: choose one of five atlas families (DOE Ref pre-1980, DOE Ref 1980–2004, 90.1-2007, 90.1-2013, 90.1-2019), see how many of the prototype's program keys it covers, and store every key's atlas program in place of the illustrative presets; the window ratios stay the DOE reference building's. New optional inputs Climate Zone, Year, Holidays, Context, used with an atlas map, and a new output JSON, each key's program text. A key the family has no program for (the 90.1 families split or rename up to four space types) is refused by name, and Presets has that position empty for DOE Program Map. Clear returns to the illustrative presets.
- New: Get from Atlas… on Schedule Draw stores the
generation.jsonof the folder of the program in Program JSON. The atlas has none for the detailed office folders, so the DOE small, medium, and large office's programs cannot yet be drawn; the window says so. - Changed: the program checksum in Source is that of the text with LF line endings, so a definition that stored text with CR LF gets a new Source once.
- The network is used only after a click in one of these windows; every solve reads only the definition, so stored programs solve offline. Windows only (WebView2).
- What opens differently: definitions saved with 1.7.0 open with their saved values; the DOE Programs components get the four inputs unset and the JSON output, and solve as before. The examples are rebuilt with 1.8.0, and
doe-atlas-map.ghis new. - Not yet performed by a person: the release-1.8.0 checklist in Rhino 8.
- What follows: the merge, the tag
v1.8.0on the merge commit, the releases onEnvironmentalSystemsLab/BEMGen(notes) and on the docs repository (the files ofscripts/package-release.ps1built from the tagged commit, withSHA256SUMS.txt, marked latest), and the docs export ofv1.8.0imported by the docs repository. - For a person in Rhino 8: the release-1.8.0 checklist, with the editor, atlas window, and atlas program map checklists.
- Published (recorded after the tag; the facts of what followed, in the place D-206 took for 1.7.0):
- The tag.
v1.8.0, annotated (tag object497cedf), ond8e9751, the merge commit of PR #17 (the release branch; PR #16 merged D-209 as3e3e9c8). The gate cloneD:\BEMGen-gate\v1.8.0, checked out at the tag (its tree equal to the gate commit6267910), passedscripts/verify.ps1again with 0 warnings and the counts above. - Release files, built by
scripts/package-release.ps1on that clean tagged checkout and checked withsha256sum -c SHA256SUMS.txtbefore the upload:bemgen-1.8.0-rh8-win.zip(1,594,140 bytes,13f9dcb3…b4ee6),bemgen-1.8.0-rh8_19-win.yak(1,602,600 bytes,b60887ed…16646),bemgen-1.8.0-examples.zip(342,636 bytes,ef708fb8…825f2). - Releases.
EnvironmentalSystemsLab/BEMGenv1.8.0with the release notes above;Energy-Atlas/BEMGen-docsv1.8.0with the same notes, the three files andSHA256SUMS.txt, marked latest. - The public site. The docs export of the tag (
scripts/docs-export/export.ps1in the gate clone, Rhino 8.25.25314.11001: 103 icons, 66 screenshots, 117 developer pages, 0 gaps, the one known redaction) imported into the docs repository with itsimport_reference.py(reference-source.json: 1.8.0 atd8e9751); no link to a private path and no new redaction. Its hand-written pages brought to 1.8.0 by itssync-docs(what's new, getting started's file names, the user index, the typologies' DOE presets and examples, the workflow's strips);mkdocs build --strictpassed without a warning and its 18 importer tests passed; its commitsc520ba8and968fc4fpushed to itsmain, and the Pages workflow deployed968fc4f(success; the live site's What's new has 1.8.0).
D-211 — The heat-transfer options seal only boundaries between two conditioned zones; amends D-201¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the owner's rule of 2026-11-01 on the review's F3: "IWHT off and FHT off make a surface adiabatic only when both zones it separates are conditioned; a surface of a zone without HVAC keeps its link"); the controller (where it is implemented, the tests) · Status: Accepted · Amends: D-069 (what an option that is off writes) and D-201 (R4's small-hotel failures now get a BEMGen change) · Design note §1
- Decision.
HeatTransferOptions.Exported(surface, isConditioned)writes an interzone surface adiabatic only when its option is off and both of its zones are conditioned (ZoneProgram.IsConditioned, a thermostat, so ideal loads in the IDF); a surface of a zone without HVAC on either side keeps its link whatever the options.HeatTransferOptions.For(building)gives the function for a building's zones. Both converters use it (IdfGeometryfor Convert2IDF, Convert2BEM's Boundaries), so the options keep one meaning (D-069). The site record'sIWHT=/FHT=values are unchanged; what they write for a zone without HVAC is the change. - Why. The 24 R4 failures (D-193) are one zone, the small hotel's 15.05 m² ground-floor elevator core (
L0/ElevatorCoreFlr1,L0/Mechanical-1, orL0/Unconditioned-1by simplifier), with no HVAC and 32.1 kW of elevator equipment; IWHT off left it about 5 W/K to outside and EnergyPlus diverged in warm-up. The owner tested the rule on the failing IDF (lod-ht-20261029-071714, site469d7e03): a full year, no severe error. Sealing a zone that nothing conditions is not a simplification of interzone exchange but a different building; between two conditioned zones the ideal loads hold each side, which is what the option was for (D-069). - Tests first (observed failing before the change, for the reason expected):
HeatTransferOptionsTests(a surface of a zone without HVAC on either side keeps its link; compile failure on the old one-argumentExported);IdfHeatTransferRuleTestson the R4 case rebuilt from the DOE small hotel (ElevatorCoreHotel: every key's illustrative preset,Elec/MechRoomwith no HVAC and 2133 W/m² of equipment, nothing lost, 0.12 to 0.5): the core is its own unconditioned zone with three interior walls, with both options off its four interior surfaces are writtenSurfacewith partners that point back (old code:Adiabatic), no pair joins two conditioned zones, and with both options on the core is written as with both off; the opt-inHeatTransferRuleEndToEndTests(BEMGEN_ENERGYPLUS, EnergyPlus 26.2.0, Chicago O'Hare TMY3): with the old rule EnergyPlus stopped (CalcHeatBalanceInsideSurf, 69 395 °C onL0/ELEVATORCOREFLR1/W1, 1 severe, 1 fatal); with the rule the year completes with 0 severe and 0 fatal. The input stays extreme (32.1 kW in 15 m², the atlas's to correct, review F3a): with the Example envelope the core passes 200 °C on some hours, and EnergyPlus's psychrometric range warningPsyPsatFnTemp(15 431 times, at most 206.6 °C) is the one warning that test allows beyond the default expected ones. - Changed outputs. Two IDF snapshots changed, only in the boundaries of the unconditioned stair's surfaces (
AdiabatictoSurfacewith the partner):idf-minimal-linear(12 lines) andidf-SemanticMerge-StackedFloorZoneMultiplier-off(36 lines).IdfChecksnow checks with both options off that no pair joins two conditioned zones (it checked that nothing was paired);IdfLinearMatrixTestslikewise. - Docs.
convert2bem.md(the inputs, the boundary table, the section on heat transfer),convert2idf.md(the inputs, the boundary table),pipeline.md, the Convert2IDF and Convert2BEM option descriptions (text only, no GUID changed), the person's Convert2BEM checklist items ingrasshopper-smoke-test.md. - Follows. Stage LOD1 reruns R4's small-hotel cells on this rule and re-reports them (D-201's "not applicable" cells, the study report §4.3 and methodology §M9, are LOD1's to restate).
D-219 — LOD study R6 tables from durable sources; the lost R5 repeat check archived as published¶
- Date: 2026-11-01 · Decided by: the owner (review of 2026-10-31, issue E1, accepted 2026-11-01: durable locations, record the lost evidence honestly, no timing now while other agents load the machine); the LOD1 builder (the routine choices below) · Status: Accepted · design note
- Finding.
study_tables.py --checkreadD:/bgdiag/r6-retimeandD:/bgdiag/r6-ht-report, both deleted; the repeat check of D-202 (runrun_20261029-144634) has no copy anywhere, so its figures cannot be re-derived (review S5-01). - Decision.
study_tables.pyreads only durable sources under--stages(defaultD:/bemgen-results/lod-study): the stage folders (read-only),r6/{main,ht,timing}-reportwritten by this commit'sreport.py(commands inscripts/lod-study/README.md), and the repeat check fromrecheck/when a run is there, else the archiveresearch/lod-study/archive/r5-repeat-check-20261029.json: thetiming.recheckblock and the per-site table as published at137db0e, with the SHA-256 of the three files they were taken from. The published block carriesevidence: "archived",rederivable: false; the report §5.5, the methodology §M8 and §M9, andprovenance.jsonsay the check cannot be re-derived.scripts/lod-study/recheck.pyprepares the re-time (the 34 IDFs andsites.csv) and runs nothing. - Open. Timing the repeat check again needs an otherwise idle machine (D-200); not run.
D-220 — LOD recommendations bounded by the reference's zone count; every planned zoning listed (schema 1.2.0)¶
- Date: 2026-11-01 · Decided by: the owner (issues E2 and E5, accepted 2026-11-01: recommend only zonings with at most the reference's zone count and flag cells where only finer ones meet; emit the excluded zonings with a reason); the LOD1 builder (the field names and the reason codes) · Status: Accepted · Amends D-188's coarsest rule
- Decision.
report.coarsest: the candidates are the zonings meeting the threshold withzone_countat most the reference's; the fewest-zone candidates are recommended; a cell where only finer zonings meet it has none andfiner_than_reference: true. The study asks how coarse a zoning can be; a finer split answers another question. R3: 214 of 264 cells have a recommendation (218 before); the 4 flagged cells are the full-service restaurant 4A (both vintages and orientations), where only Perimeter Core 4.57 m + Stack (5 zones against 2) met it. The closest zoning of a cell without one is taken from the candidates only. - Excluded zonings.
report.loadadds thesuite_sitesrows the generator refused (Invalid), so every entry lists every planned zoning (23 in R3, 95 in R4) withsucceeded: false,status: "Invalid", and areason(Invalid: MultipliedRooms, 864 in R3, orInvalid: PlanTooNarrow, 192); a failed run hasstatusandreasonRunFailed. A zoning taken from the suite (refused or failed) has its simplifier spelled as the runner spells the written ones (PerimeterCore PerimeterDepth=3, wheresuite_sitesholdsPerimeterCoreand the depth apart), so one zoning has one spelling. The R4 tables count refused zonings apart from failed runs. - Schema 1.2.0 (additive,
lod-recommendations-v1.schema.jsonaccepts 1.0.0 to 1.2.0):suites,runs,deduplication,judged_loads,sensitivity;judgedandfiner_than_referenceon an entry;statusandreasonon a zoning; a 1.2.0 document must carry them (the schema'sallOf). For the atlas import (ADR 0050 §4, stage REL-A): showInvalidzonings as not generated, not failed, and show the flag; its sidecar's "the 24 main zonings each" now holds.
D-221 — Deduplication keeps the both-on twin¶
- Date: 2026-11-01 · Decided by: the owner (issue E3, accepted 2026-11-01); the LOD1 builder (the order after the heat-transfer setting) · Status: Accepted · Amends plan decision 8's kept site (D-185)
- Decision.
report.sort_key(the kept order and the listing order): the heat-transfer setting closest to the reference's first (IWHT and FHT on; IWHT on, FHT off; IWHT off, FHT on; both off), then the simplifier by name without its options, join pieces (unset, off, on), the perimeter depth as a number, the stacker by name, and the simplifier's full text. The first drop sorted the stringified fields, so "False" came before "True" and depths compared as text. R3's kept sites are unchanged (all its zonings have both options on; 0duplicate_ofchanged); in R4 the kept twin changes in 1 578 sites and the cells with an off setting in a recommendation fall from 15 to 7 of 18, each a tie with a both-on zoning, not a twin.
D-222 — Cost medians: twins dropped, prototypes weighted equally¶
- Date: 2026-11-01 · Decided by: the owner (F4, accepted 2026-11-01) · Status: Accepted · Amends D-202's medians
- Decision. The R5 cost medians drop the timed test sites R3 marks
duplicate_of(137 of 206, leaving 69 distinct models); a prototype's figure is the median over its distinct test sites; a zoning's and the overall figure are the medians of the prototype medians (each timed prototype one vote). The publishedtimingblock gainsduplicates_dropped,overall, andper_prototypeon eachby_zoningrow; the report §5.3 adds a zoning × prototype table. Overall processor-time ratio: 0.659 over nine prototypes (D-200's 0.504 pooled every timed site); per prototype from 0.065 (outpatient) to 1.65 (full-service restaurant, whose distinct zonings are a single zone and two Perimeter Core splits finer than its reference).
D-223 — LOD study hygiene: tests in the gate, LF output, self-provenance, a pinned EnergyPlus, sensitivity and unjudged loads stated¶
- Date: 2026-11-01 · Decided by: the owner (issues E4, E6, E7, E8, accepted 2026-11-01); the LOD1 builder (the routine choices below) · Status: Accepted
- Tests and gate (E4).
tests/test_report_main.pyrunsreport.mainend to end on a central database and suite the test writes (dedup and the kept twin, the reference never dropped, the area change, the refused zoning, the bounded recommendation, the source, LF output, the schema; it fails on the code before this change in 6 of its 11 tests and is skipped withoutduckdb).scripts/verify.ps1runspython -P -m unittest discover -s scripts/lod-study/testsandmake_specs.py --check, andstudy_tables.py --checkwhenBEMGEN_LOD_STAGESnames the stage folders, and fails whenpythonis not on the path;report.pyimportsduckdbonly where it reads a database, so without it only the end-to-end tests are skipped (routine choice: an opt-in like the EnergyPlus tests, since the stage folders are on the owner's machine). - Sensitivity and unjudged loads (E6). The consolidated file carries
judged_loads(heating not judged in 108 of 264 cells: 2A 40, 2B 32, 3C 33, 4A 3; cooling in 4) andjudgedper entry, andsensitivity: the recommendations recomputed from the stored metrics by the same rule with the three error limits × 0.5, 0.8, 1, 1.2, 2 (area limit and judging floor unchanged): 194, 206, 214, 216, 223 cells with a recommendation, 178, 232, 264, 250, 209 equal to the published. The report §3.4 and the methodology §M7 and §M9 state both. The threshold stays the owner's (D-188). - Provenance (E7).
report.pywrites every file with LF;sourceissuites <names>; runs <names>;study_tables.pyderives the R3 and R4 gate strings from the status counts and refuses a stage whose EnergyPlus is not the pinstudy.jsonnow carries (energyplus: 26.2.0,4bd7a1f26f);run.ps1refuses uncommitted changes, records the commit and the EnergyPlus version instate.json(a resumed stage must be at the same commit), and refuses another EnergyPlus build. The published HT file changes its SHA-256 (it was written with CR LF);provenance.jsonis version 1.1.0 of its format (addsenergyplus_pin, the R5 report source, and per-sourcesites_csv_sha256). - Second person (E8).
scripts/lod-study/README.mdgains "Running a stage on another machine" (software andduckdb==1.5.5, a clean short checkout, every root parameter, the weather download, the inputs, the expected smoke counts) and the R3, R4, and R5 re-report commands; the first R1 paragraph is dated and marked superseded; the input count is 367.
D-212 — Skylights take the construction set's skylight glazing; without one, the vertical glazing with a warning¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the owner's rule of 2026-10-31 on the review's A1, S4-01: "set has skylight glazing → skylights use it; model has skylights but the set has no skylight glazing → warn and fall back to the vertical glazing; no skylights and no skylight glazing → no message"; "don't list Skylight as unused when skylights exist"); the controller (the design below) · Status: Accepted · Amends: D-162 (skylights were written with the window construction) · Design note §2
- What was wrong.
IdfGeometrywrote every skylight with the envelope's window construction, andUnifiedReadernever read the set'sSkylight, listing it as unused "(no geometry in BEMGen)". For 90.1-2019 5A that wrote U 2.04 / SHGC 0.38 in place of U 2.84 / SHGC 0.40; for 3B U 2.38 / SHGC 0.25 in place of U 3.12 / SHGC 0.30. - Decision.
UnifiedReaderreadsSkylightas it readsExteriorWindowintoConstructionSet.Skylight(WindowConstruction, not applicable unless given) andSkylightIncidentSolarMultiplier. Its problems are not import errors (only a skylit building uses it): an assembly BEMGen cannot write, or an unsupported solar modifier, is read as blocked with the reason. The import's role lines gainSkylight: ….EnvelopeValues.Skylight/EnvelopePreset.Skylight(null: the skylights take the window;SkylightGlazingis the one written), checked and named with the window.ConstructionSet.Resolve(…, hasSkylights): with skylights, a provided skylight glazing is the preset's, with the role lineskylight: <assembly> (<assembly>), and leaves the unused list; a not-applicable one is the warningSkylightGlazingFromWindow(new) and the window glazing, with the role lineskylight: <window> (the window's glazing; the set: NotApplicable, <reason>), the window then being needed as if the building had windows; a blocked one isConstructionBlocked. Without skylights nothing is said andSkylightstays unused. A set from an envelope preset gives its skylights the preset's window, as before, with no message (the rule is about sets).IdfWriterpasses wall windows and skylights apart, writes the skylight glazing's material and construction, gives skylights theSkylightassembly's incident solar multiplier and wall windows the window's (a skylight on the window glazing takes the window's);IdfGeometrywrites skylights withSkylightGlazing. Convert2BEM's Window Constructions name the skylight glazing for skylights (an envelope preset's window, so unchanged).- Tests first (failing to compile before the change: no
Skylight, nohasSkylights, noSkylightGlazingFromWindow):ConstructionSetTests(the three owner cases, the unused list either way, a blocked glazing, a building with only skylights, an envelope preset),ImporterTests(3B's skylight U 3.12304483761242 / SHGC 0.3 / VT 0.81 and its role line; a set without one; an unsupported solar modifier read as blocked while the set imports),IdfSkylightGlazingTests(the medium office 3B set on a skylit linear plan writes every skylight with90-1-2019.simple-glazing.e99390and its glazing object, wall windows with the window, the role line,Skylightout of the header's and provenance's unused lists; the hospital set warns and writes the window; no skylights and no skylight glazing give no message; the medium office set without skylights listsSkylightunused and writes no skylight glazing). - Changed outputs. No snapshot changed (no snapshot has skylights with a construction set). The import's role lines have one more line.
- Docs.
construction-json.md(theSkylightrow),convert2idf.md(skylights), the Construction JSON input and Unused descriptions (text only; the schema version there corrected to 3.1.0, B7).
D-213 — No simulation with an unresolved climate: the zone is resolved at import, and Convert2IDF refuses a program whose climate-dependent values were not¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the owner's rule of 2026-11-01 on the review's F5a: "never simulate with an unresolved climate; refuse at Convert2IDF when a program has climate-dependent values and no resolved climate zone; take the zone from the Climate Zone input, or else from the .stat file next to the EPW"); the controller (where the resolution happens, below, as the owner asked to design and record) · Status: Accepted · Amends: D-172 (an import without a zone took the general values with a warning, and nothing stopped the conversion) · Design note §3
- Where the resolution happens (the controller's design). At import, as before: Program JSON and the DOE Programs components take the zone from their Climate Zone input. It cannot move to Convert2IDF: a by-climate window ratio has already shaped the generated windows, and a merged zone's loads have already been aggregated from its sources, so a zone chosen at conversion could no longer be applied consistently. So the
.statfile cannot resolve the zone at conversion; it names the zone to give and checks a zone given. This is the one point where the build departs from the letter of the brief ("or else from the .stat file"), for the reason above; reported to the owner. - Decision.
DocumentBuilderlists what differs by climate zone and was not resolved (ProgramJsonDocument.UnresolvedClimate), and the warningProgramJsonClimateZoneNotGivennow says Convert2IDF refuses it. The preset's provenance recordsClimateZone.Unresolved(Lod.Core.Programs.ClimateResolution.UnresolvedParameter) with the program ID and what differs; the keyClimateZonekeeps the zone or "none given".ClimateResolution.Unresolved(building)andZones(building)walk the building's provenance (its own and each floor's, every input), which carries each preset's provenance through plans, mixes, merges, and aggregators.IdfWriterrefuses a building with an unresolved program with the errorClimateZoneUnresolved(no document; Override does not apply), naming each program and what differs, and the zone to give when the weather's.statstates one.EpwStat.ClimateZonereads the ASHRAE zone a.statfile states (- Climate type "5A" (ASHRAE Standard 196-2006 Climate Zone)**; older files give only Köppen, so none).Convert2Idf.Runreads the.statnext to the weather file intoIdfWeather.StatClimateZone/StatFileName. With a zone stated, the header recordsWeather climate zone: <zone> (ASHRAE, from <file>), and programs resolved for another zone get the warningClimateZoneDiffersFromWeather.- The Climate Zone descriptions of Program JSON and the DOE Programs components say so (text only).
- Tests first (failing to compile before the change: no
ClimateResolution,UnresolvedClimate,EpwStat, the two codes):ClimateResolutionTests(ProgramJson: the IECC small office and the 90.1-2019 retail core without a zone record their unresolved climate; with a zone, the zone and nothing unresolved; the medium office, without by-climate values, nothing);IdfClimateResolutionTests(Export: the retail core on the office plate without a zone is refused, naming the program and the daylighting; with 5B it converts; the refusal names the.stat's zone; 6A against a.statof 5B warns and the header records 5B; 5B against 5B does not;EpwStaton the ASHRAE lines of 196-2006 and 169-2013, a zone without a letter, and a Köppen-only file;Convert2Idf.Runreadssite.statnext tosite.epw). - Changed outputs. None of the existing tests imported a by-climate program without a zone and then converted it (all suites passed unchanged); no snapshot changed. The test-suite generator always gives the spec's climate zone (D-133), so suites are unaffected.
- Docs.
convert2idf.md(Weather, Diagnostics),program-json.md(the code, the climate zone, the Climate Zone input).
D-214 — Hot water delivered at most at the source outlet when the atlas caps it¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's A3, S4-02, accepted: "honour the policy or warn and list it in Left Out"); the controller (honour it, as below) · Status: Accepted · Design note §4
- Decision. A per-program water heating demand with
delivery_policy.methodcap_at_source_outlet(the atlas's ADR 0018) gives its draw the outlet of its last heating stage (ProgramJsonLoad.DeliveryCap). The import caps the draw's target temperature hour by hour at that outlet, as a new schedule<target> capped at <outlet> C(the requested schedule itself when no hour exceeds it). For EnergyPlus's stand-aloneWaterUse:Equipmentthis gives the heat of writing the outlet as the hot water supply temperature: above the outlet the whole draw is hot water at the outlet, below it the mix meets the target. So no core type or IDF field changed. Overrides (the preset's Source) records the requested target, its maximum, and the hours it is unmet, the contract's "report unmet target". For the 90.1 small-hotel laundry (82.2 °C requested, 60 °C outlet), the draws' heat drops from about 1.44 times the delivered heat (at 10 °C mains) to the delivered heat. This was not run in EnergyPlus here. - Tests first (
FieldsReadPastTests, failing to compile before the change): the kitchen fixture given a cap at 50 °C delivers at most 50 °C with the same flow and an Overrides line with the unmet hours; a cap at 99 °C changes nothing and records 0 hours.
D-215 — Fuels BEMGen does not meter and water-heater stages are listed in Left Out¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's A4, S4-03, accepted: "map non-gas fuels to OtherEquipment with its fuel, or warn; add Left Out lines for heater fuel/efficiency"); the controller (warn, not map: BEMGen's loads have no fuel, and
OtherEquipmentby fuel would be a new load kind through every aggregator) · Status: Accepted - Decision. A load whose
fuelis not the one its object meters (gas_equipmentother than natural gas,electric_equipmentother than electricity) is written as given with the warningProgramJsonFuelNotAppliedand a Left Out line (three ResStock programs have propane gas equipment). Each per-program water heating demand gets a Left Out line naming its stages' heater, fuel, conversion efficiency, and outlet, which are not applied: BEMGen reports the hot water's thermal demand. - Tests first: propane gas equipment and natural-gas electric equipment warn and are listed; the kitchen's own fuels give no message; the kitchen's water heater line names natural gas and 0.8051284672.
ImportTests.ReportsTheUnusedDesignDayRulesOncePerSchedulenow counts 9 Left Out lines for the medium office, its 7 design-day lines plus the water heater (this entry) and the daylighting controls (D-217), and pins both new lines.
D-216 — The DOE Programs components gain Left Out; the source calendar's daylight saving and holidays are listed only when they apply¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's B3, S4-04, S4-05, accepted) · Status: Accepted · Amends: D-209 (the components' outputs)
- Decision. The fifteen DOE Programs components gain a fifth output Left Out (tree, one branch per key, empty for a key without a program; nothing without an atlas map). The output is appended, and no GUID changed. Program JSON lists
source_calendar.daylight_savingwhen it is recorded (the IDF runs on standard time). It lists the holiday line only when the source has holiday rules and the calendar given has no holidays. - Tests first: the DOE Ref medium office lists its daylight saving (2nd Sunday in March); without holidays the holiday line and the binding warning (D-217) appear, with 01-01, 07-04, 12-25 neither does. The headless Rhino spec
doe-atlas-mapswas updated for five outputs and not run (Rhino is the owner's).
D-217 — Messages and descriptions corrected; unknown releases remarked; an unmet holiday binding warned¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's B7, B8, S4-06, S4-08, S4-10, S4-11, accepted) · Status: Accepted
- Decision. Messages and descriptions, text only with no GUID changed:
- the people messages name the real inputs (Activity, People Radiant, People Sensible);
- the inlet message says the filled version gives the mains correlation "where the atlas publishes" one;
- a newer program JSON schema is told to update BEMGen, an older one to export again;
- Construction JSON names schema 3.1.0 and the skylight glazing (D-212);
- Program JSON says daylighting controls are carried and never written, lists them in Left Out, and its Left Out description names what it now lists.
A source.release other than atlas releases 2.0.0 to 2.2.0 (ProgramJsonDocument.KnownReleases) is the remark ProgramJsonReleaseNotKnown. A stored atlas map whose release manifest SHA-256 is not one BEMGen knows (AtlasSiteIndex.KnownReleaseManifests: 2.2.0 67e73e55…7ece4) is the remark AtlasSiteReleaseNotKnown. Of required_bindings, holiday_calendar is checked: unmet when the source calendar has holiday rules and no holidays are given, the warning ProgramJsonBindingNotMet. The others are met by construction or checked elsewhere: floor area, people, water properties, calendar_year (the calendar always has a year), dwelling_unit_count (ProgramChecks.PerDwellingLoads), shared_service_host (listed in Left Out), and weather_file (Convert2IDF's weather and mains objects). load_host is not checkable: BEMGen puts every load of a program in its zone.
- Tests first: FieldsReadPastTests (the version advice, the unknown release, the holiday binding with and without holidays, a program without source holidays) and AtlasProgramMapTests.AMapOfAnUnknownReleaseManifestIsImportedWithARemark.
D-218 — DOE atlas maps: a crosswalk of the 90.1 split keys to their department mixes, after their members were checked; Retail stays refused¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's F2, S2-12, accepted: "map small-hotel GuestRoom, hospital Office and stand-alone retail Retail to GuestRooms_Mixed, Office_Mixed and Sales_Mixed after confirming their members; keep Gym - audience refused"); the controller (the members' check, below) · Status: Accepted for GuestRoom and Office; Retail for the owner (settled by D-246: the core and front keyed apart) · Amends: D-209 and ADR-036 decision 3 · Design note §5
- Members (atlas release records,
data/atlas-releases/v2.0.0/commercial/<template>/<type>/programs/department/, IDs kept through 2.2.0; checkout1266d075): - small hotel
GuestRooms_Mixed, in 90.1-2007, 2013, and 2019:GuestRoom123Occ0.4535,GuestRoom123Vac0.2347,GuestRoom4Occ0.1931,GuestRoom4Vac0.1188. These are the four split guest rooms, so confirmed. 90.1-2007 has the same gap and the same mix, so it is mapped too; the owner's rule names no template. - hospital
Office_Mixed, in 90.1-2013 and 2019:HospitalOfficeFlr10.1087,HospitalOfficeFlr50.8913. Confirmed. - stand-alone retail
Sales_Mixed, in 90.1-2013 and 2019:Front_Retail0.0793,Core_Retail0.8414,Point_of_Sale0.0793. Not confirmed:Point_of_Saleis a key of its own in the map, so mappingRetailto this mix would count the point-of-sale program twice.Retailstays refused. For the owner: an atlas mix ofFront_RetailandCore_Retailalone (weights 0.0861 and 0.9139 renormalised) would close the gap. - Decision.
DoeAtlasFolders.Crosswalk(type, template)givesGuestRoom → GuestRooms_Mixed(small hotel, 90.1-2007 to 2019) andOffice → Office_Mixed(hospital, 90.1-2013, 2019).AtlasMapFamily.Crosswalkcarries it, andAtlasSiteIndex.MatchKeys(folder, keys, crosswalk)matches a crosswalk key without its own source program to the folder's onedepartmentprogram of that space type (AtlasKeyMatch.Mix). The window's coverage names the mix ("GuestRoom takes GuestRooms_Mixed, the atlas's area-weighted mix of its split spaces"). The DOE Programs components pass each family's crosswalk.Gym - audiencestays refused. - Tests first (failing to compile before the change):
DoeAtlasProgramMapTests.TheCrosswalkMatchesTheSplitKeysToTheirDepartmentMixAndLeavesTheOtherGaps(Integration, the recorded index of 75 folders: with each family's crosswalk exactly those keys take the mix, of modedepartmentin the prototype's folder, every other key's match is unchanged, and the gaps left areRetailandGym - audience);AtlasProgramMapTests.ACrosswalkKeyIsCoveredByItsDepartmentMixAndNamed. The pinned source-only gaps of D-209 are unchanged. No recorded store file holds a mix's export, so a crosswalk map was not downloaded in a test. That is a person's check in Rhino (the BG1 checklist).
D-224 — R4's small-hotel cells run again with D-211's heat-transfer rule: no failed run left¶
- Date: 2026-11-01 · Decided by: the owner (the review's F3, the BG1 rule of D-211, and the instruction to rerun R4's small-hotel cells at BEMGen
d4eb868, pinned for the whole rerun); the LOD1 builder (the routine choices below) · Status: Accepted · Amends D-201 (the "not applicable" cells) and D-193's R4 failures - Scope check first. The whole R4 suite written again at
d4eb868(lod-ht-20261101-040524, 1 760 sites) has IDFs equal to the first run's apart from comment lines in every cell but the small hotel's: there 30 of 192 differ (12 IWHT off and FHT on, 12 both off, 6 IWHT on and FHT off), the zonings with a surface of a zone without HVAC. So only the small hotel's two cells need running. - Run. A full R4 rerun at 20 and then 4 runs at a time stopped twice on
OutOfMemoryExceptionin the runner (the machine's commit memory was nearly exhausted by other work; 204 sites done;D:/bemgen-results/r4-d211/run_20261101-041001, abandoned). The small-hotel cells then ran as their own stage:ht-smallhotelinstudy.jsonandspecs/ht-smallhotel.yaml(SHA-25688a37fa9…c0620), suitelod-ht-smallhotel-20261101-042906(the same test case IDs, site codes, and IDFs as the full rewrite), the generator and runner built in a clean checkout ofd4eb868(D:/bg-r4), the runner with--parallel 2, runrun_20261101-043133(build stamp1.8.0+d4eb868…), atlas 2.0.0 inputs, EnergyPlus 26.2.04bd7a1f26f; merged and reported byreport.pyintoD:/bemgen-results/lod-study/ht-smallhotel/(itsstate.jsonwritten by hand in run.ps1's format). Routine choice: run by hand rather than byrun.ps1, which atd4eb868has no such stage (added in this change). - Outcome. 192 of 192 sites succeed; 190 of 190 test sites pair (largest deviation 8.0 × 10⁻¹¹ W); 40 duplicate groups drop 128. The 24 sites that failed in D-193 all run. Small hotel 5A: Semantic Merge + Stack (43 of 67 zones), unchanged; its 8-zone Conditioned Merge zonings with IWHT off are now twins of the same zonings with both options on, which miss the threshold, so the review's S5-11 question is closed. Small hotel 3C: Conditioned Merge + Stack and + Stacked Floor Zone Multiplier (8 zones), plus the tie Conditioned Merge + Stack with FHT off (a twin in the first run, a model of its own under D-211). Both references and every both-on zoning keep the first run's load metrics (only the parallel run's
cpu_time_ratio, not a result, differs). - Published.
study_tables.py --ht-rerun(default<stages>/ht-smallhotel/report) replaces the first R4 run's two small-hotel entries by the rerun's (combine_ht, tested);suites,runs,source, andby_programname both;provenance.jsonrecords the rerun stage, the superseded first-run cells, and the IDF equality of the other cells. R4 as published: 1 760 of 1 760 written sites succeed, 18 of 22 cells have a recommendation, 8 with an off setting. The study report §4 and §4.3, the methodology §M4 and §M9 item 1, andlod-recommendations-ht.jsonare regenerated; R3's file is unchanged. - For REL-A. Import the new
lod-recommendations-ht.jsonandprovenance.json(R3'slod-recommendations.jsonis byte-identical to PR #20's): the small hotel's R4 cells have no failed zoning any more, so the site's "24 not applicable" text and the D-201 caution go.
D-233 — Opt-in code-rule daylighting: the program says whether and how, BEMGen's geometry says where; amends D-174 (ADR-037)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the owner accepted the review's F5 (b) on 2026-11-01 with its method "all as proposed": code-rule daylighting on BEMGen's geometry, opt-in; D-174's "never" becomes the default; the LOD study keeps it off); the controller (the realisation below) · Status: Accepted (IECC amended by D-248) · Amends: D-174 (no daylighting object in any IDF becomes the default) and ADR-027 decision 4 · ADR-037, design note §2–§4
- Decision.
IdfOptions.WriteDaylighting, Convert2IDF's appended input Daylighting (index 12,Dayl, Boolean, default false; the component's GUID unchanged, a Boolean input needs no new parameter type), and the test-suite spec'ssettings.daylighting(default false) write, for each zone whose program has daylighting controls and lights, the reference pointsCodeDaylightingplaces (Lod.Core.Conversion) and oneDaylighting:Controlswith the program's settings. Off, nothing is written. - Where (the code rule). The 90.1 primary and secondary sidelit areas of each window (as wide as the window plus 2 ft each side in 90.1-2010, half the head height in 2013 to 2019; one and two head heights deep) and the toplit areas of each skylight (grown by 0.7 × its height above the floor), united, netted (primary less toplit, secondary less both), on the zone's own floor (its parts united per storey, so a merged zone takes its own geometry); the edition's control requirements and sensor choice (
space_daylighting_control_required?,space_daylighting_fractions_and_windowsof openstudio-standardsc8c1b9eb), the office whole-building non-geometry fractions, rounding to three decimals and the cap below 1; sensor 1 at 0.66 and sensor 2 at 1.33 head heights in from the window's centre, or under the skylight's centre, at 0.8 m (the owner; openstudio-standards uses 0.762 m), moved to the interior point of its area when outside the zone's floor. - The rule's inputs.
DaylightingCodeRuleonDaylightingControls.Rule(part ofSameSettings, kept by a merge with its lead source's settings): the program's template (90.1-2010; 2013, 2016, 2019 share one rule; any other template has none), its openstudio-standards building and space type from thespace_types <building> / <space>evidence of Program JSON, thestandards_rule's non-geometry fractions (applied only for OfficeWholeBuildingspace types, as openstudio-standards does), and the import's climate zone.daylighting_appropriate: falsegives no rule. Program JSON readsstandards_rule(ProgramJsonStandardsRule) and recordsDaylightingRulein the preset's source; its Left Out line says when the controls are written. - Diagnostics and header.
DaylightingNotWritten(info, option off),DaylightingWritten(info, option on),DaylightingNoCodeRule(warning). The header's daylighting lines say off (the default) or on, and on, each zone's edition, areas, and points or why it has none; provenanceDAYLIGHTING: …. A building whose programs carry no controls is written byte for byte as before, either way. - Checks.
ExpectedWarnings.PartialDaylightingControlis not inDefault;ExpectedWarnings.ForIdfadds it only for an IDF holding aDaylighting:Controlsobject (eplus-check, the batch runner).IdfChecksfails any daylighting object unless a test passesdaylighting: true, which checks the controls' zones, points, and shares instead. - Tests first (written before their code; observed failing for the expected reason:
CodeDaylightingTests, not compiling without the core types, andDoeSuiteTests,/settings/daylightingrefused by the schema; the others reference members that did not exist yet and were first run once those were written):CodeDaylightingTests(31),DaylightingAndMainsAggregationTests.AMergeKeepsTheCodeRuleOfItsControls,ReleaseTwoTests(4),IdfCodeDaylightingTests(6),RunVerdictTests.TheDaylightingWarningIsExpectedOnlyForAnIdfWithDaylightingControls,DoeSuiteTests.DaylightingIsWrittenOnlyWhenTheSpecAsksForIt, and the opt-inCodeDaylightingEndToEndTests(BEMGEN_ENERGYPLUS, EnergyPlus 26.2.04bd7a1f26f, Chicago O'Hare TMY3): the medium office on the office plate ran the year off and on with 0 severe and 0 fatal errors, the on run's 3 warnings allPartialDaylightingControl, interior lights 66 750.5 kWh off and 53 133.0 kWh on, equipment equal. - Changed outputs. The program preset snapshot
medium-office-presetgains itsDaylightingRuleline; no IDF snapshot changed; the header line and info of a building with controls and the option off are reworded (IdfProgramJsonTests). - Docs.
convert2idf.md(inputs, provenance, the daylighting section),program-json.md,program-presets.md§7, ADR-027 (decision 4), ADR-037, the eplus-check and test-suite generator READMEs, the smoke checklist and theprogram-jsonsmoke spec.
D-234 — Occupancy sensors and time switches: in the schedules and power densities; an occupancy-driven cap left to the atlas¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (F5 (b): "PNNL models them as reductions of the lighting schedules or power densities, which the programs already carry, so nothing is added (to confirm per template before the build)"); the controller (the check below) · Status: Accepted (the cap, which the atlas now records, applied on request by D-256) · Design note §6
- Found. In the PNNL 90.1 medium office IDFs (Denver), the lighting schedule's occupied value falls from 0.9 (2004) to 0.885 × 0.9 (2010) and 0.8368 × 0.9 (2013 to 2019): the control credit is in the schedule, and the atlas's 90.1-2019 medium office carries openstudio-standards'
OfficeMedium BLDG_LIGHT_SCH_2013(0.776 at occupied weekday hours). The 90.1-2019 IDF listsLPD_Ctrl = 0.02and an office LPD of 6.664 W/m², the atlas program openstudio-standards' 6.889 W/m²; the difference is the atlas's to explain. Time switches appear only as schedules. - A gap. The 90.1-2016 and 2019 PNNL models of the office, restaurant, retail, strip mall, and warehouse types (not 2010 or 2013) add one EMS program per zone, read in the medium office: when the zone's occupant count is 0 and its lighting exceeds 0.02 W/ft², the lighting is set to 0.02 W/ft². Neither the atlas programs nor BEMGen carry it.
- Decision. BEMGen adds nothing: the cap changes the program's lighting, which is the atlas's to record (GLOBAL.md scientific rule 3). Reported to the owner for the atlas.
D-235 — The prototype check of code-rule daylighting on four DOE prototypes¶
- Date: 2026-11-01 · Decided by: the controller (the check F5 (b) asks for; a record, not a rule) · Status: Accepted · Design note §5
- Check. The suite generator's DOE prototypes (90.1-2019, 5A, No Simplification, Stack, the option on) with the LOD study's pinned atlas inputs, the medium office on its whole-building program; against the PNNL 90.1-2019 Buffalo IDFs and
table.htmzone summaries. Controlled floor fraction, BEMGen / PNNL: medium office 0.438 / 0.408, primary school 0.748 / 0.748, stand-alone retail 0.066 / 0.698, warehouse 0.052 / 1.000; controlled lighting fraction: 0.183 / 0.213, 0.408 / 0.460, 0.012 / 0.387, 0.058 / 0.492. The four IDFs ran a year in EnergyPlus 26.2.0 (eplus-check, Buffalo TMY3): all passed, 0 severe. - Why they differ. BEMGen's DOE geometry is the DOE reference layout and glazing: the medium office's 65 rooms with centred windows, where the code's power thresholds leave some small rooms' areas uncontrolled; no skylights in the reference retail and warehouse records, where the 90.1-2019 prototypes' controls are toplit. The rule matches where the geometry does (the school's zones, the office's perimeter share).
- Not changed. The geometry's glazing; a program or spec that gives a key the prototype's skylight ratio gives it toplit areas.
D-236 — Code-rule daylighting review fixes: the rule out of the merge settings, host parts for windows and skylights, the toplit-and-primary case described¶
- Date: 2026-11-01 · Decided by: the controller (an independent review of PR #23 relayed by the coordinator; fixes within D-233's method) · Status: Accepted · Refines: D-233 · Design note §2
- The rule is not a merge setting. D-233 had put
Equals(Rule, other.Rule)intoDaylightingControls.SameSettings, so a merge of two sources with the same written settings and different code rules (space types) raisedDaylightingSettingsDifferwith the option off, its text showing identical settings.SameSettingsis again the written settings only; the merged zone still takes its lead source's rule (WithFraction), and a different rule is the infoDaylightingRuleDiffers. - Host parts. A window's storey is that of the zone part hosting its wall (the wall's outline under the window, the part's height holding the sill), a skylight's that of the part whose top is the roof and whose footprint holds its centre; the earlier rules (the highest storey holding the sill; the highest storey below the roof) remain the fallback. Before, a part spanning another part's floor elevation lost its windows and skylights to the other part's storey.
- Toplit and primary without secondary. 90.1-2013 to 2019 with these two controls required have no branch in openstudio-standards' sensor table, so no sensor; BEMGen keeps that parity, and the header now says
none (required by … for the toplit and primary area(s), but openstudio-standards places no sensor for this combination: …)in place of "not required". - Smaller. Program JSON warns (
ProgramJsonSpaceTypeAmbiguous) when its evidence names several openstudio-standards space types (none of the 249 release programs checked does);IdfCheckstests reference points inside the zone's footprint, not its bounding box; the 90.1-2010 aperture test is pinned with a skylight transmittance (inCodeDaylighting; the writer's pass-through of the skylight glazing's transmittance has no test of its own). - Tests first. Run against the code of PR #23 (
42267ce), the new tests failed for the expected reasons:ControlsThatDifferOnlyByTheirCodeRuleMergeWithoutAWarning(the warning raised; its info assertion removed for that run, the code not existing yet), the updatedAMergeKeepsTheCodeRuleOfItsControls,ASkylightBelongsToThePartWhoseTopIsTheRoofEvenAboveAnotherPartsFloor,AWindowBelongsToThePartHostingItsWallEvenAboveAnotherPartsFloor,ToplitAndPrimaryWithoutSecondaryControlGetNoSensorAsInOpenStudioStandards;NinetyOneTwentyTenTakesTheSkylightTransmittanceIntoItsApertureTestpassed there too (a pin).SeveralSpaceTypesInTheEvidenceWarnAndTheFirstIsTakenwas first run with its code. - The off path against 1.8.0. The LOD study's main spec cut to orientation 0 was written by the suite generator with 1.8.0 (
137db0e), main before BG3 (b1c363c), PR #23 (42267ce), and this fix: at climate zone 5A (11 prototypes, both vintages, 528 sites) the sites' conversion warnings are identical in all four, and the IDF objects identical except 96 files of 1.8.0 (BG1's skylight glazing, D-212); the IDF headers differ only by D-233's daylighting line andDaylightingRulesource parameter. With the suite generator patched (outside the repository) to record the simplifier's and stacker's warnings too, the large hotel, outpatient, and small hotel (both vintages, all six climate zones, 864 sites) give identical warnings in all four versions (264MassOverBudget, 108DaylightingSettingsDiffer): in these specs every merge whose sources' rules differ also differs in its written settings, so the regression of D-233 did not show in them; the fix holds it for any merge.
D-225 — Atlas site downloads: at most four at a time, one retry of a 429 or 5xx, a rate-limit message, and no exception in a window¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's B4, S4-09, accepted: "cap concurrency at 4, retry 429/5xx once with backoff, say 'rate-limited, wait a few minutes'; catch-all in async handlers"); the controller (the backoff) · Status: Accepted · Design note §1
- Decision. One
AtlasSitefetches at mostAtlasSite.MaxConcurrentDownloads(4) store files at once; the session's one site (ProgramJsonComponent.SessionSite) shares the gate across every window.HttpAtlasSiteTransportasks once more after HTTP 429 or 5xx, after 2 s or the server'sRetry-Afterwhen longer, at most 10 s; a 429 after that isAtlasSiteRateLimited(GitHub Pages limits this network's downloads; wait a few minutes), a 5xx after it says the site answered it twice; a 404 is not asked again. Everyasync voidhandler of the atlas windows catches any exception other than cancellation into the status line (AtlasWindows.Unexpected). - Tests first (
AtlasSiteNetworkTests, observed failing to compile): loopback servers answering 429 then 200, 429 twice, 503 then 200, 503 twice, 404; a cancelled backoff; a recorded site that counts store requests in flight, whose DOE map of eleven keys reaches four and no more. The window handlers are a person's check.
D-226 — Program and construction checksums of the atlas's canonical JSON; the store file of an atlas pick in Source¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's B5, S4-07, S1-13, S3-08, accepted: "hash a canonical form in both readers; record the store path"); the controller (the realisation) · Status: Accepted · Supersedes: D-207's checksum of the text with LF line endings (D-128) · Amends: ADR-018, ADR-033 decision 10 · Design note §2
- Decision.
CanonicalJsonwrites a JSON value as the atlas'ssite_store.canonicaldoes (atlas ADR 0057): Python'sjson.dumps(sort_keys=True, separators=(',', ':'), ensure_ascii=False)with members sorted by code point, only quote, backslash, and control characters escaped (\u00xxlower case), integers as written (-0as0), other numbers as Python'sreprof the double, no trailing newline; a lone surrogate has no canonical form.ProgramJsonReader(Json.Sha256, and so Schedule Draw'ssource_program_sha256) andConstructionJsonImporter(Sha256and the set'sSHA-256line) hash it, so the site's store text, the release's pretty-printed file, a CR LF panel, and a re-indented or reordered copy have one checksum, the site'sjson_sha256. The draw'ssetting_sha256andrecipe_sha256and the test-suite generator's input names stay checksums of bytes (they identify files). - The store file.
AtlasStoreOrigin(store path, release, the canonical checksum of the text as downloaded) is recorded asJson.Storein a program's Source and asatlas store …in a construction set's source only while the text read has that checksum. Program JSON and Construction JSON keep the last pick's origin in the definition; the DOE maps keep each key's checksum in the map (formatbemgen-atlas-program-map1.1.0, whose keys gainjson_sha256; a 1.0.0 map from 1.8.0 still reads and records no store file), and the atlas mixes each member's (D-228). - Changed outputs. The 90.1-2019 medium office fixture's
Json.Sha256isf792abc0…(was1331af85…) inImportTests,MediumOfficeTests,IdfProgramJsonTests, and the snapshotmedium-office-preset.txt; the DOE Ref 1980–2004 medium office'ssource_program_sha256is943259b0…(was65c19c07…). Each expected value was computed outside BEMGen with atlasdbf47bce'sscripts/site_store.py; the canonical-text vectors with Python 3.14'sjson.dumps. No fixture file changed. - Tests first:
CanonicalJsonTests(the vectors, Python'srepr, a lone surrogate, the export file's and the site text's checksums),CanonicalChecksumTests(the construction fixture, CR LF and compact layouts, the detailed document, the store line),AtlasStoreOriginTests, and the map'sJson.Store; the reader tests failed before the readers changed (the vectors passed with the new class).OtherWhitespaceStillChangesTheSha256, which asserted D-207's behaviour, becameOtherWhitespaceAndTheMembersOrderDoNotChangeTheSha256, withAChangedValueChangesTheSha256beside it.
D-227 — The site index schema 1.0.0: kind and major version, generation checksums, mix compositions, and construction sets¶
- Date: 2026-11-01 · Decided by: the controller (needed by B1 and B6; the atlas's ADR 0058, live on the 2.2.0 site since 2026-11-01) · Status: Accepted · Amends: ADR-036 decision 2 (the folders' fault, now with separate faults for the compositions and the construction sets) · Design note §3
- Decision.
AtlasSiteIndexreadskindatlas_siteandschema_version1.x (another major is refused with "update BEMGen"); a folder'sgenerationneeds itsjson_sha256, whichDownloadGenerationAsyncchecks after decompression;foldersis required (schema 1.0.0); each folder'scompositionsare read strictly (each member asourceprogram of the folder listed once, an area of at least 0, a weight in [0, 1], the weights summing to 1 and equal to the area shares withinToleranceSettings.AbsoluteFraction, each general and department mix of the folder with its composition and its own mode);construction_setswith each context'sdetailed,unified, and the reasons beside their nulls. A fault in the compositions drops them only (CompositionsFault), one in the sets drops the sets only (ConstructionSetsFault). - Fixtures recorded again on 2026-11-01 by
scripts/atlas-fixtures/record_site_fixtures.py(recorded.json1.2.0):Fixtures/site/andFixtures/site-doe/gain the schema's keys and the compositions, their store files byte-identical; newtests/Lod.ConstructionJson.Tests/Fixtures/site/, four construction sets whole and three unified documents (D-229). The live index (release 2.2.0, schema 1.0.0) reads with no fault: 446 compositions, 291 sets, 6250 contexts, 1112 of them one document for both representations (LiveSiteTests,BEMGEN_ATLAS_SITE_LIVE=1). - Tests:
AtlasSiteCompositionTests(eleven composition faults, three kind or version refusals, a later minor read, the generation checksum, four construction-set faults, a null with its reason). Written before the reader, but not observed failing: they were first run with it.AnIndexWithoutTheFieldsOfAdrZeroZeroFiveTwoStillReadsbecame…ReadsItsProgramsWithAFaultOfTheFolders, since schema 1.0.0 requires the folders.
D-228 — Get from Atlas on Mix Programs: a folder's general and department mixes or a free selection, area-share weights, stored and mixed offline¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's B1, part 2, the owner's issue O-1, accepted: "pick a template × building type folder, list its source programs with default weights = the prototype's represented floor area, let the user untick or edit weights, store members and weights in the component; Presets/Weights become optional overrides"); the controller (the design below) · Status: Accepted · Amends: D-124 and ADR-017 (the component's inputs and outputs) · Design note §3
- Decision. Mix Programs (GUID unchanged, moved to its own file
MixProgramsComponent.cs) gets Get from Atlas….MixAtlasDialog(logic inAtlasProgramMixPicker) lists the folders with source programs by template and building type; a folder's starting selections are its general mixes, then its department mixes, members ticked with their composition weights, and a free selection of every source program, unticked, with its area share of the folder's general mixes where it is a member of one, else no weight. The user unticks and reweights; OK downloads the ticked members (at most four at once), checks each on the component's Climate Zone, Year, Holidays, Context, and mixes them with the stored weights; only an accepted mix is stored, as one textbemgen-atlas-program-mix1.0.0 (per member its program, space type, store path, represented area, weight, text, and the text's canonical checksum), with one undo event (format 1.0.0 is new in this stage, so its requiredjson_sha256breaks no stored text); Clear removes it. The starting selection and its areas are taken from the index, not from the window. - The solve.
AtlasProgramMixImporterimports each member offline with Program JSON's import (an any-program preset named by its space type,Json.Storein its Source), checks each text is its program and version, and mixes withProgramMix.Mix(D-124). A wire into Presets takes precedence over the stored mix, even when empty (a warning), and then needs Weights; with the stored mix and no wire, Weights when given replaces the stored weights, one per member. Appended inputs Climate Zone, Year, Holidays, Context; appended outputs JSON and Left Out. Presets and Weights are optional, also on a definition saved before (set after reading, since Grasshopper saves the flag). - Why BEMGen mixes the members itself instead of reading the atlas's
General_Mixed: unticking, reweighting, and every member's program, checksum, and store file in the provenance (GLOBAL scientific rule 2). The atlas's own mixes stay available through Program JSON; the two can differ where the atlas's mixing rule differs from D-124's. - Tests:
AtlasProgramMixTests(folders, starts, the stored general mix with the members' texts byte for byte, unticking and reweighting, a free selection, six refused selections with no store request, a member not downloadable, a compositions fault, a refused solve, a rejected check, the text round trip and seven malformed texts, the 11-member medium office mixed byProgramMixwith each member'sJson.Store, given weights, a member of another program, an unknown manifest, an edited member text, a start altered by the caller). They were written after the picker and importer, so they were not observed failing first; of the two added in the review, the project failed to compile onJsonSha256(the edited-member test), and the start test was not observed failing for its own reason; two of the first run failed on the test's own count (the folder has twelve source programs, the plenum outside the general mix) and were corrected. The component and the window are a person's check; the smoke specmix-atlasis written, not run.
D-229 — Get from Atlas on Construction JSON: a set's unified document by context; the residential source-model documents not read¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's B6, S4-16, S1-19, accepted: "add the atlas strip to Construction JSON (set + climate context); decide whether to read residential source-model sets"); the controller (the decision on the source-model sets, below) · Status: Accepted · Amends: ADR-021 (where a set may come from) · Design note §4
- Decision. Construction JSON (GUID unchanged) gets Get from Atlas…:
ConstructionAtlasDialog(logic inAtlasConstructionPicker,Lod.ConstructionJson) lists the site's construction sets by the template their IDs begin with, and a set's contexts; Use this set downloads the context's unified document withAtlasSite.DownloadConstructionAsync(the gzip SHA-256 against the store path, kindbuilding_level_equivalent,detailed_context_id<set>.<context>), imports it withConstructionJsonImporter.Import, and stores it as the JSON input's value with one undo event, its store file kept for Source (D-226). - The source-model sets are not read (the issue's second half). A context whose
unifiednames itsdetailedfile (atlas AT2, ADR 0058: the 1,112 contexts of the IECC residential and manufactured-housing sets) is onesource_model_construction1.0.0 document, the source dwelling's constructions on its own surfaces; BEMGen converts only whole-building unified sets (D-142), and reading it would need a mapping from the source's surfaces to BEMGen's roles, a design of its own. The window marks those contexts and refuses them before any download (AtlasSiteConstructionNotUnified). A ResStock set's unified document is single-dwelling and is refused by the import (ConstructionJsonScopeNotSupported, D-142); a context without a unified document with itsunified_reason(AtlasSiteConstructionNotPublished); an IECC commercial set is stored with the import's design-flow warning and refuses conversion as before (D-172). - Tests first (
AtlasConstructionPickerTests, observed failing to compile): the four recorded sets and their contexts, the 90.1 office's 5-a stored with its store line, the IECC office's warning, the dwelling refused by the import, the source-model set refused with no store request, a set or context not in the index, a null with its reason, another context's file, a file not recorded, a fault of the sets. The window is a person's check; theconstruction-jsonsmoke scenarioatlasis written, not run.
D-230 — Stage BG2: the review, the checks, and what refreshes at atlas release 2.3.0¶
- Date: 2026-11-01 · Decided by: the controller (the fix plan's merge authority: merge after
scripts/verify.ps1and CI; no tag) · Status: Accepted · Design note - Built on
fix/bg2-atlas-pickersinD:/bg-bg2, frommainatbdc9eef. No GUID, icon, or component count changed; Mix Programs and Construction JSON gained strips, Mix Programs four inputs and two outputs, appended. - Review (D-111). A second agent reviewed the six slices read-only, checked the canonical form against Python and the compositions and construction sets against the live index, and asked for changes, all made (
4306082; the test project failed to compile on the new checksum member first, the start test was not observed failing for its own reason, the Grasshopper ones are a person's check): an old Mix Programs definition kept Presets and Weights required (Grasshopper savesOptional), soReadnow sets them optional with their new descriptions (a person's check); the DOE maps' and mixes'Json.Storewas granted against the checksum of the very text read, so each key and member now keeps its checksum as downloaded (map format 1.1.0); a wired but empty Presets now takes precedence with a warning; the mix's starting selection and areas are taken from the index;Origin()no longer throws; the person's checklist andprogram-presets.mdupdated. A re-review approved the fixes. Left as they are:AtlasProgramMap.ToJsonwrites 1.1.0 and is for fresh maps only (a parsed 1.0.0 map written again would not read back; nothing does that); a download without a canonical form would store a checksum that does not read back (unreachable after the strict parse); the mixed preset's own Source names its members and weights (Input.k) and nests their Sources with the atlas IDs and store files, but not the folder and starting mix (ProgramMixhas no parameter for them; the component's remark names them). - At atlas release 2.3.0 (stage REL-B):
KnownReleaseManifestsandProgramJsonDocument.KnownReleases; the three recorded sites and their pinned store paths, checksums, and counts, recorded again byrecord_site_fixtures.py; the embedded construction schemas,UnifiedVersion, andDownloadConstructionAsync's identity check if AT1 changes the unified contract; a site index schema 2.x would be refused until BEMGen follows it. - Checks:
scripts/verify.ps1onb5fb638: VERIFY PASSED, 0 warnings: Core 1090, Generators 3087 (1 skipped), Integration 3647, Export 3157 (1 skipped), ConstructionJson 329 (314 onmain, +15), ProgramJson 613 (5 skipped: the two real draws and the three live site facts; 522 onmain, +91), Simulation 276 (16 skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 164, Python 107.LiveSiteTestswithBEMGEN_ATLAS_SITE_LIVE=1: 3 passed (live 2.2.0, schema 1.0.0).developer_pages.py --check: 122 pages, the one known redaction. Rhino not run: the specsmix-atlas,construction-json(scenarioatlas), andprogram-json-editorwere changed or written and not run, and the BG2 checklist is for a person.
D-239 — Atlas release 2.3.0 contracts: program JSON 6.0.0, unified 4.0.0, detailed 5.0.0 (ADR-038)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the 2026-10-31 review's fix plan, stage REL-B, approved "Yes, all as proposed" on 2026-11-01: move BEMGen to atlas release 2.3.0 with no backward-compatibility shims); the REL-B builder (the realisation and the routine choices below) · Status: Accepted · Supersedes in part: D-172 and ADR-027 (the contract versions), D-205 (the 5.4.0 schema copy), D-217 (the known releases) · ADR-038 · Design note
- Decision. BEMGen reads the contracts of atlas release 2.3.0 (atlas PR #48, merge
75fec30d, its ADR 0054 and ADR 0059): program JSON 6.0.0 (ProgramJsonDocument.SchemaVersion), unifiedbuilding_level_equivalent4.0.0 (ConstructionJsonImporter.UnifiedVersion), and detailedconstruction_set5.0.0 (DetailedVersion), and only those. The embedded schemas are the atlas's at75fec30dbyte for byte, with their$commentrevision markers:program-json-v6(revision 2, withlighting_controls.daylighting_origin),construction-equivalent-v4,construction-json-v5, and the mass-accounting 1.1 and foundation-ground 1 schemas, whose bytes changed only by their markers.ProgramJsonDocument.KnownReleasesisatlas-release-2.3.0andAtlasSiteIndex.KnownReleaseManifestsrelease 2.3.0's manifestafe7e2ef…fb86c(2.0.0 to 2.2.0 are no longer listed: their exports are the contracts BEMGen no longer reads). Source-model constructions (2.0.0 in this release) stay unread (D-229). - The new fields. The evidence rows are read in their one shape (
ProgramJsonEvidence:Id,SourceProject,SourceRevision,SourceLocator,SourceFileSha256,SourceUrl,OriginalValueJson,OriginalUnit,Transformation,ExtractionDate,InterpretationNote; a unit or checksum map is kept as JSON text), and the daylighting code rule (D-233) reads the space type from the locator after#. A dwelling'sfenestration.variantsare selected by Context as its loads are (ProgramJsonFenestration.Variantnames the variant in Source); a context key the options lack, or not exactly one variant applying, is the warningProgramJsonFenestrationContextRequiredand the JSON's fenestration is not used.status,reason,nulls, anddaylighting_originare read past. A unified document from the site must name the index's release (AtlasSiteReleaseMismatch), D-230's identity check. The accepted absences are no longermetadata.resolutionsbutmetadata.absent_demandsin 6.0.0 (each withstatusfilledand itsfilling);ProgramJsonDocument.AbsentDemandsreads them and Assumptions lists them after the resolutions, andDefaultPolicyin Source counts both (2 resolutions; 2 absent demandsfor the medium office), so the provenance of a zero load the filled version drops stays (the review of this stage, item 1). A unified set's Source names the document'srelease(item 2). - Fixtures.
scripts/atlas-fixtures/export_fixtures.pyexports release 2.3.0 (--release 2.3.0) from an atlas clone at75fec30d: eight programs (the seven before by their 2.3.0 IDs and the IECC 2021 single-family dwelling, whose window ratios arevariants) and 22 construction pairs inFixtures/atlas-2.3.0/(D-243's warehouse added;atlas-2.0.0/removed). The synthetic program documents were rewritten into the 6.0.0 shape by a one-off script, values unchanged. The three recorded sites were recorded again from the live site of 2.3.0 byrecord_site_fixtures.py(recorded.json1.3.0). Changed checksums, each computed again outside BEMGen with Python'sjson.dumps(sort_keys=True, separators=(',', ':'), ensure_ascii=False): the 90.1-2019 medium office'sJson.Sha256df121b3e…47a6(wasf792abc0…), the DOE Ref medium office's624bcac4…f9a(was943259b0…), the recorded open office'sca9129bb…df53(wasb4e9a14f…), the unified and detailed medium office 3Ba16c5102…9ecfand9cd43d08…4be9. - Tests first: with the fixtures re-exported and the readers unchanged, the program and construction tests failed on the version check (
ProgramJsonVersionNotSupported,ConstructionJsonKindNotSupported); then the readers moved;ReleaseTwoTestsgains the variant selection, the warning without context, and the refusal of a 5.4.0 program;AtlasConstructionPickerTests.AStoreFileOfAnotherReleaseThanTheIndexIsRefusedfailed before the release check.
D-240 — Stored atlas IDs and texts of releases 2.0.0 to 2.2.0 refused, naming their 2.3.0 IDs through the atlas's ID map (ADR-038)¶
- Date: 2026-11-01 · Decided by: the REL-B builder (the fix plan's item 3 asked to decide between translating on read and a clear refusal naming the map, and to record it) · Status: Accepted · ADR-038 decisions 3 and 4
- Decision. Refusal, not translation: a stored text of releases 2.0.0 to 2.2.0 is program JSON 5.4.0 or unified 3.1.0 / detailed 4.3.0, which BEMGen no longer reads, and its values may have changed in 2.3.0, so translating its ID would claim 2.3.0 for 2.2.0 values.
AtlasIdMapembeds the release'sid-map-2.3.0.jsonbyte for byte (SHA-256b4c79774…da7b, 1,562 IDs). A program JSON 5.x is refused (ProgramJsonVersionNotSupported) with the advice to take it again from release 2.3.0 (Get from Atlas…) or export it with--release 2.3.0, naming the program's 2.3.0 ID (or saying that it kept its ID); a unified 3.1.0 or detailed 4.3.0 document likewise names its set's (ConstructionJsonKindNotSupported). A DOE map or atlas mix stored from site release 2.2.0 or earlier is refused as a whole, before any text is read (AtlasSiteStoredBeforeRelease), naming each moved program's 2.3.0 ID. - Schedule Draw's stored Generator JSON is not refused by name: a
generation.jsonnames no release. A stored 2.2.0 file fails for a moved program withScheduleDrawRecipeNotFound, whose message says that a program of an earlier release has another ID; for a program whose ID did not move it is accepted, and its blocks equal release 2.3.0's but for the moved IDs, so the draw is the same. - Where BEMGen stores atlas IDs: the fixtures and their scripts, the tests, the documents, and the smoke specs now name 2.3.0 IDs (translated through the map); the decision log and past plans keep the IDs of their time; the LOD study keeps its pinned 2.0.0 IDs (D-244); three example definitions hold 2.2.0 texts until a person rebuilds them in Rhino (the release 1.9.0 checklist).
- Tests first:
AtlasProgramMapTests.AMapStoredFromAnEarlierSiteReleaseIsRefusedNamingTheMovedIdsandAtlasProgramMixTests.AMixStoredFromAnEarlierSiteReleaseIsRefusedNamingTheMovedIdsfailed with the check disabled (each key refused withAtlasSiteProgramMismatchinstead) and pass with it;AtlasIdMapTests; the refusals ofReleaseTwoTests(programs and constructions) andFieldsReadPastTests.
D-241 — The six IECC families in the DOE atlas maps; the offices and the supermarket not offered them (the review's F1)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's F1, accepted: "add IECC templates to the generator and derive their blocks; then offer the IECC families in BEMGen's DOE maps"); the REL-B builder (the reading of the site below) · Status: Accepted (the offices amended by D-247) · Amends: D-209 and ADR-036 decision 4
- The site (live index of release 2.3.0, 96 IECC commercial folders over IECC 2006, 2009, 2012, 2015, 2018, 2021): every DOE prototype's folder under the same name except that the IECC offices (
small-office,medium-office,large-office) hold only whole-building programs (WholeBuilding - Sm Office,Plenum, …), so none of the office keys has a program, and there is no supermarket. The eleven other prototypes match by D-209's rule with the gaps of 90.1-2013 and 2019 (Retail,Gym - audience,GuestRoom,Office), and each IECC small hotel'sGuestRooms_Mixedand hospital'sOffice_Mixedhave the members of the 90.1 mixes with the same weights to 1e-4 (D-218's check repeated on the index). IECC programs are exported in their source version only (filledis null with the site's reason). - Decision.
DoeAtlasFolders.Familieslists the six IECC families after the five;FamiliesOf(type)andHolds(type, template)give the families that hold a prototype (all eleven for eleven prototypes, the five for the offices and the supermarket), and the DOE Programs components offer those.Crosswalkgives the IECC families the crosswalk of 90.1-2013 and 2019. Each IECC family resolves 110 keys (with the crosswalk 112) and leavesRetailandGym - audience; the window's coverage saysonly the source version of …, so a map is stored in its source version, and Filled is refused key by key with the site's reason. The offices' keys could take the whole-building program (a crosswalk of every office key toWholeBuilding - …), but that would give every zone one program: a source decision for the owner, not made here. - Tests first:
DoeAtlasFoldersTests(the eleven families,FamiliesOf,Holds, the IECC folders and crosswalk) failed to compile before the change;DoeAtlasProgramMapTestspins every IECC family's 110 keys and gaps, the crosswalk's mixes, and an IECC 2021 quick-service restaurant map stored in its source version and imported, its filled version refused, on the recorded index of 141 folders.
D-242 — Schedule Draw on atlas-schedules 4.1.0 and atlas release 2.3.0: the detailed offices and IECC programs drawable; amends ADR-035¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the fix plan's REL-B: "IECC and detailed offices drawable in Schedule Draw"; the review's F1 and B2); the REL-B builder (the recording) · Status: Accepted · Amends: ADR-035 decision 2 (the pins), D-205
- Decision.
ScheduleDrawSettingpins generator 4.1.0, data release 2.3.0, and its manifestafe7e2ef…fb86c; the request and bundle schema stay 4.0.0 (the atlas's ADR 0059 §5), so the setting is still 2.0.0 withversion4.1.0. The program setting is thegeneration.jsonof release 2.3.0. Release 2.3.0 generates the 84 IECC commercial and 15 detailed office folders (the atlas's ADR 0055), so Get from Atlas… on Schedule Draw finds a generation file for the DOE offices' and the IECC programs' folders, and they are drawn. The messages name release 2.3.0 and the folders still without one. - Recording. The recorded draws were drawn again with
python -m atlas_schedules(atlas-schedules 4.1.0 (request schema 4.0.0); reads atlas release 2.3.0) in a clone of atlas75fec30d, not with the released wheel: the wheel was not downloaded, and the local build of it in the atlas worktree differs in bytes from the published one (SHA-2564f91f8d0…against the published9390facc…), so it was not used. Two runs gave the same bytes.request.jsonis byte-identical to 1.8.0's (the DOE Ref program's ID did not move); its bundle and the 90.1 pair changed; two new pairs record a detailed office program (the 90.1-2019 medium office's open office, filled, drawn from its block's source view) and an IECC program (the IECC 2021 quick-service restaurant's dining, source). - Tests:
ADetailedOfficeAndAnIeccProgramAreDrawn(new), the pins inScheduleDrawTestsandScheduleDrawSettingTests,RealDrawTests(three, withBEMGEN_ATLAS_GENERATORthe atlas clone: passed),AtlasGenerationPickerTests(the detailed office and IECC folders give their files; a folder without one, now from a changed index, is refused).
D-243 — The 152 DOE Ref unified documents of the review's A2 import¶
- Date: 2026-11-01 · Decided by: the REL-B builder (the fix plan's item 6) · Status: Accepted
- Finding. Atlas release 2.3.0 builds the unified
InternalMassof the eight DOE Ref sets (stand-alone retail, small hotel, supermarket, warehouse, both vintages, 19 climates each) from the full inventory (its ADR 0053), so the component andmass_accountingagree and BEMGen's check (D-143) passes. No BEMGen change: the check follows the atlas contract. - Tests. The fixture pair
doe-ref-warehouse-1980-2004-5B(the warehouse, whose component held a third of its accounting before) joins the building-scope fixtures and passes every import test;LiveDoeRefMassTests(withBEMGEN_ATLAS_SITE_LIVE=1) downloads all 152 documents from the live site of release 2.3.0 and imports them: 152 imported, 0 failed.
D-244 — The LOD study stays on its pinned atlas 2.0.0 inputs¶
- Date: 2026-11-01 · Decided by: the REL-B builder · Status: Provisional (for the owner)
- Finding. The study's inputs (
scripts/lod-study/export_inputs.py, atlasv2.0.0), specs, and published results (research/lod-study/) name atlas 2.0.0 IDs and hold program JSON 5.4.0 exports. BEMGen 1.9.0 reads only 6.0.0, soTestSuiteGeneratorof 1.9.0 can no longer generate the study's suites from those inputs; the published results were made at BEMGen8d2e3bd(D-224) and stay as they are, and the atlas site already translates their IDs for its pages (the atlas's ADR 0059 §6). - Decision. No study file changes in this release: re-keying the specs or re-exporting the inputs from release 2.3.0 would change what was run. Running the study again with 1.9.0 needs its inputs exported from release 2.3.0 first (a change of
export_inputs.pyand the specs, and new runs), which is the owner's call.
D-245 — Version 1.9.0 prepared on atlas release 2.3.0; the tag and the releases wait for the Rhino steps¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the fix plan's REL-B, with the standing authority to merge, push
main, tag, and publish once the checks pass, and "never launch Rhino"); the REL-B builder (the preparation and the choice below) · Status: Accepted - Version. 1.9.0 (
Directory.Build.props), onrelease/1.9.0frommainatc681cc7(the merge of PR #25), a minor version as D-204 and D-210 reason: D-064 waives backward compatibility, and no component or parameter GUID, icon, or input or output changed (descriptions only: Program JSON, Construction JSON, Schedule Draw). - What it holds since
v1.8.0: the review fix stages onmain(BG1 D-211 to D-218, LOD1 D-219 to D-224, BG2 D-225 to D-230, BG3 D-233 to D-236) and REL-B (D-239 to D-244, ADR-038). - Review (D-111). A second agent reviewed the diff read-only, rebuilt and ran the test projects (and the live A2 test, the real draws, and the fixture reproduction check), and asked for nine changes, all made (
b154de2,394dc91, and the ADR-038 wording): the absent demands of 6.0.0 listed in Assumptions (they had dropped out), a unified set's release in its Source, the stale 2.2.0 and 75-folder texts, a control character in the checklist, and Schedule Draw's stored Generator JSON recorded under D-240. A re-review approved them. - Checks:
scripts/verify.ps1on8a1ac17: VERIFY PASSED, 0 warnings: Core 1090, Generators 3108 (1 skipped), Integration 3666, Export 3157 (1 skipped), ConstructionJson 341 (1 skipped: the live A2 test), ProgramJson 618 (6 skipped: three real draws and three live site facts), Simulation 276 (16 skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 164, Python 107. Gated:LiveSiteTestswithBEMGEN_ATLAS_SITE_LIVE=13 passed (live 2.3.0, 255 folders, 189 with a generation file);LiveDoeRefMassTests1 passed (152 imported);RealDrawTestswithBEMGEN_ATLAS_GENERATORan atlas clone at75fec30d3 passed.developer_pages.py --check: 124 pages, the one known redaction. Fresh-clone gate: a clean clone ofrelease/1.9.0at14cc1acfrom GitHub (D:\BEMGen-gate\v1.9.0) passedscripts/verify.ps1with 0 warnings and the same counts. There is no CI (D-005). - Release notes (draft).
- Changed: BEMGen reads atlas release 2.3.0: program JSON 6.0.0, unified constructions 4.0.0, detailed constructions 5.0.0. Texts of releases 2.0.0 to 2.2.0 (wired or stored in a definition, including the DOE maps and atlas mixes) are refused, naming each program's or set's ID in release 2.3.0; Get from Atlas… stores them again.
- New: the DOE Programs components offer the six IECC families (2006 to 2021) for the eleven prototypes they hold, in their source version; Schedule Draw uses
atlas-schedules4.1.0 and draws the DOE offices' and IECC programs; a dwelling's window ratios follow its Context; Assumptions lists the filled version's absent demands. - Fixed in the atlas, read now: the 152 DOE Ref unified construction documents (retail, small hotel, supermarket, warehouse) that 1.8.0 refused for their internal mass import.
- With the review fixes since 1.8.0 (D-211 to D-236): the heat-transfer rule, unresolved-climate refusal, the 90.1 crosswalk, opt-in code-rule daylighting, Get from Atlas… on Mix Programs and Construction JSON, canonical checksums.
- Not done, and why. Rhino was not launched, so the examples were not rebuilt (
program-json-office.gh,schedule-draw.gh, anddoe-atlas-map.ghstill hold release 2.2.0 texts, which 1.9.0 refuses), the headless smoke check did not run, and the docs export for the public site was not made. A tag at this commit would publish an examples zip with three refused examples and a site describing 1.8.0, so the tagv1.9.0and the releases onEnvironmentalSystemsLab/BEMGenandEnergy-Atlas/BEMGen-docswait until a person has done the release 1.9.0 checklist's Rhino steps (rebuild and commit the examples, the smoke run, the docs export); then the fresh-clone gate at the tag,scripts/package-release.ps1, and the releases follow as in D-210. The wheel ofatlas-schedules-v4.1.0was not downloaded;RealDrawTestswith it is on the checklist. - For a person in Rhino 8: the release 1.9.0 checklist.
- The Rhino steps, run headless (recorded after the tag). The owner asked on 2026-11-01 for the rebuild; with no Rhino open, on
mainat7027cffin Rhino 8.25.25314.11001: - Examples.
program-json-office.gh,schedule-draw.gh, anddoe-atlas-map.ghwere rebuilt withBEMGEN_EXAMPLES_WRITE=1(4840ee8). All 22 reopened with Validate True, andschedule-draw.ghdrew with Run true on theatlas-schedules4.1.0 wheel of the atlas release (SHA-256 checked), so the wheel'sRealDrawTestsalso passed (3). - Smoke specs. The first full run failed 8 checks in three specs, each an expected value changed by a decision since the last Rhino run, none a defect (
ae287a7):program-json: the canonical checksum (D-226), and the two absent demands after the two resolutions (D-239);reference-programs: Convert2IDF's thirteenth input Daylighting (D-233), and the previous 11-input shape now built without it;schedule-draw: the unmet-holiday-calendar warning (D-217).
- Full run. Then all 30 specs passed with
BEMGEN_SCHEDULE_COMMANDthe wheel's console script: 1,556 Validate True and none False, 1,300 checks passed and none failed, no exception or missing component, nothingSKIPPED. - Merge and checks. PR #28 merged as
13c4379.scripts/verify.ps1passed on the branch and on a fresh clone of13c4379(D:\BEMGen-gate\v1.9.0-tag) with 0 warnings and the counts above. - Published:
- The tag.
v1.9.0, annotated (tag object5dcf576), on13c4379. - Release files. Built by
scripts/package-release.ps1in the gate clone at the tag and checked withsha256sum -c SHA256SUMS.txt:bemgen-1.9.0-rh8-win.zip(1,707,924 bytes,aeeec820…48f321);bemgen-1.9.0-rh8_19-win.yak(1,715,543 bytes,322950d6…a9574);bemgen-1.9.0-examples.zip(366,225 bytes,6c57468e…4d927c).
- Releases.
EnvironmentalSystemsLab/BEMGenv1.9.0with the release notes above.Energy-Atlas/BEMGen-docsv1.9.0with the same notes, the three files andSHA256SUMS.txt, marked latest. - The public site.
- The docs export of the tag (
scripts/docs-export/export.ps1in the gate clone: 103 icons, 66 screenshots, 124 developer pages, 0 gaps, the one known redaction) was imported into the docs repository (0c00925;reference-source.json: 1.9.0 at13c4379). - Its hand-written pages were brought to 1.9.0 (
2955c5c).mkdocs build --strictand its importer tests passed, and the Pages workflow deployed (the live What's new has 1.9.0).
- The docs export of the tag (
- Left for a person. The window checks of the release 1.9.0 checklist.
- Examples README. The examples zip's
README.mdstill carries the paragraph that three examples were not rebuilt; this record's commit correctsexamples/README.mdfor the next version.
D-246 — Stand-alone retail: the core and the front keyed apart, each family's programs by zone; amends D-218 (the owner's G1)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's round-2 answer G1: "the generator keys its zones by family. 90.1-2013, 90.1-2019 and every IECC family:
Core_RetailandFront_Retailtake the atlas programs of those names; DOE Ref and 90.1-2007 keepRetail. No atlas mix"; "follow the source evidence"); the BG4 builder (the realisation below) · Status: Accepted · Amends: D-218 ("Retail stays refused"), ADR-036 decision 4 and its crosswalk alternative · Design note §1 - Source. The DOE Ref geometry (
DOERefRetailStandalone.osm) gives the zonesCore_RetailandFront_Retailone standards space type,Retail, which is how the layout record keyed them (rule 2,osm_space). The 90.1-2013 geometry openstudio-standards names for the type (ASHRAE9012013RetailStandalone.osm,ashrae_90_1_2013.prototype_inputs.json) gives the spaces of those names the space typesCore_RetailandFront_Retail. The atlas follows its sources:Retailin DOE Ref pre-1980, 1980–2004, and 90.1-2007;Core_RetailandFront_Retailin 90.1-2013, 2019, and the six IECC families. - Decision. A generator cannot know a map's family, so the zones are keyed by their own space types once and the families with one space type take it through the crosswalk:
scripts/doe-geometry/extract.pykeys the zones ofKEY_SPLITS(the retail's core and front) by the 90.1-2013 geometry's space of their name (linkosm_space_2013, checked: the space exists, its key differs from rule 2's, the floor areas agree within 0.1 m²). Onlystand-alone-retail.jsonand its answer key change; the other fourteen records are byte-identical. Each key keeps its zone's own glazing: the front's south ratio 0.248352, the core's east and west 0 (no south wall), so the window area is unchanged.DoeAtlasFolders.CrosswalkgivesCore_Retail → RetailandFront_Retail → Retailin DOE Ref pre-1980, 1980–2004, and 90.1-2007.AtlasSiteIndex.MatchKeysmatches a crosswalk target among the folder'ssourceprograms first, then among its department mixes (D-218);AtlasKeyMatch.CrosswalkandCrosswalkMode(in place ofMix) say which. The window's coverage names keys sharing a source program:Core_Retail and Front_Retail take Retail, the family's one program of their space types.DoeSpaceTypesgives both keysSpaceType.Retail.CodeDaylightingtakes the program's openstudio-standards space type, not the key, so itsCore_RetailandFront_Retailexceptions apply to the 90.1-2013 and 2019 programs as before; DOE Ref and 90.1-2007 have no code rule; IECC follows D-248.- Coverage now (with each family's crosswalk, the recorded index of release 2.3.0): DOE Ref pre-1980 and 1980–2004 154 of 154 keys; 90.1-2007, 2013, and 2019 153 of 154; each IECC family 147 of 148 (with D-247). The one gap left outside DOE Ref is the secondary school's
Gym - audience. - Tests first (the integration and map tests failed to compile before the change: no
AtlasKeyMatch.Crosswalk,CrosswalkMode):DoeAtlasFoldersTests.TheRetailCoreAndFrontTakeRetailInTheFamiliesWithoutTheirOwnPrograms,DoeLayoutRecordTests.TheRetailCoreAndFrontAreKeyedByThe2013Geometry,RetailBoxGeneratorTests.TheCoreRetailIsOneZoneReachingTheEntryBetweenTheFrontZones(the keys),DoeProgramMapTests(the space types),AtlasProgramMapTests.ACrosswalkKeyIsCoveredByASourceProgramOfTheTargetAndNamed,DoeAtlasProgramMapTests.TheRetailCoreAndFrontTakeTheirOwnProgramsIn901And2019AndRetailInDoeRef,…TheCrosswalkMatchesTheSplitKeysToTheirProgramsAndLeavesOnlyTheGymAudience,…EachFamilyResolvesTheKeysOnceInTheirPrototypesFolderWithThePinnedGaps; the gap test moved to the secondary school (AnAtlasMapWithAGapIsRefusedByKeyAndItsOtherPresetsAreImported), the mix download test to the warehouse. - Changed outputs. The snapshot
stand-alone-retail-canonical(the two zones'key=), the retail record and answer key. A retail atlas map stored by 1.9.0 (keys withRetail) is refused for its keys until Get from Atlas… stores it again. The LOD study's specs nameRetailand stay on their pinned BEMGen and atlas inputs (D-244). - Fixtures.
record_site_fixtures.pyrecordedsite-doe/again from the live release 2.3.0 index (manifestafe7e2ef…fb86c): the IECC office folders (D-247, 159 folders) and the store files of the 90.1-2019 retail's five keys, the 90.1-2019 secondary school's eleven, the DOE Ref 1980–2004 retail's four, the IECC 2021 medium office's whole-building program, and the IECC 2021 retail core (source). - Docs.
doe-components.md,doe-layouts.md,scripts/doe-geometry/README.md, ADR-036, the smoke checklist's BG4 section, and the smoke specsdoe-atlas-maps,doe-single-storey,doe-presets(updated, not run).
D-247 — The IECC offices offered: every office key takes the folder's whole-building program; amends D-241 (the owner's G9)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's round-2 answer G9: "crosswalk every office key to the folder's whole-building program (consistent with PNNL's IECC model, which gives every zone that program); the window says the rooms are not distinguished"); the BG4 builder (the reading below) · Status: Accepted · Amends: D-241 (the offices not offered the IECC families) and ADR-036 decision 4 · Design note §5
- Source (the live index of release 2.3.0 and the programs'
source_zones): IECCsmall-officeholdsWholeBuilding - Sm Office(Core_ZN,Perimeter_ZN_1to4) andAttic;medium-officeWholeBuilding - Md Office(the fifteen core and perimeter zones) andPlenum;large-officeWholeBuilding - Lg Office-others(the fifteen above-grade core and perimeter zones),WholeBuilding - Lg Office-basement(Basement),OfficeLarge Data Center(DataCenter_bot/mid/top_ZN_6, which the IDF gives their own lighting and equipment schedules),OfficeLarge Main Data Center(DataCenter_basement_ZN_6), andPlenum; every program source only, in all six IECC editions. - Decision.
DoeAtlasFolders.Holdsgives the IECC families the offices (only the supermarket is not held), at the folderssmall-office,medium-office, andlarge-office(FolderPath);Crosswalkgives every office key the folder's whole-building program (WholeBuilding - Lg Office-othersfor the large office, whose BEMGen storeys are all above grade, G3), except the large office'sOfficeLarge Data Center, which matches its own source program as the IDF's data-centre zones have theirs. BEMGen's office layouts have no plenum, attic, or basement zone (G1, G2, G3), soPlenum,Attic,- basement, andMain Data Centertake no key.DoeAtlasFolders.CrosswalkNotegives the window's coveragethe rooms are not distinguished in this family: every key takes WholeBuilding - Md Office, the whole-building program PNNL's IECC model gives every zone(the large office's names its data centres);AtlasMapFamily.Notecarries it in place of listing the keys. The supermarket stays unoffered (no IECC folder). A map is stored in the source version, as every IECC map (D-241). - Tests first (failing to compile before the change: no
AtlasMapFamily.Note,DoeAtlasFolders.CrosswalkNote):DoeAtlasFoldersTests.TheIeccFamiliesHoldEveryPrototypeButTheSupermarket,…EveryIeccOfficeKeyTakesTheWholeBuildingProgram;AtlasProgramMapTests.AFamilyNoteIsShownInItsCoverage;DoeAtlasProgramMapTests.AnIeccOfficeMapGivesEveryKeyTheWholeBuildingProgramAndSaysSo(the IECC 2021 medium office's map stored and imported, every keyiecc-2021.medium-office.source.whole-building-md-office.986e31), and the pinned gaps and crosswalk of every IECC family over the 159 recorded folders.
D-248 — IECC programs take the 90.1-2019 code-rule daylighting; amends D-233 (the owner's G3)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's round-2 answer G3: "IECC programs take the 90.1-2019 code-rule daylighting"); the BG4 builder (the inputs below) · Status: Accepted · Amends: D-233 (IECC had no code rule) and ADR-037 decision 5 and its last alternative · Design note §2
- Decision.
DaylightingCodeRule.Forgives the templatesIECC 2006,2009,2012,2015,2018, and2021the rule setAshrae2013(openstudio-standards' 90.1-2013 to 2019Spacemethods, 90.1-2019's), describedcode rule IECC 2021 (openstudio-standards 90.1-2013 to 2019: IECC takes the 90.1-2019 rule, D-248). Any other template keeps no rule. - The rule's inputs for an IECC program, which carries no
space_typesevidence and nostandards_rule: the openstudio-standards space type is the program'snamebefore_<source.building_type>(the atlas's naming:WholeBuilding - Sm Office_SmallOffice,Core_Retail_RetailStandalone), none when the name has no such suffix; the building type is the document'sstandards_building_type(read now,ProgramJsonDocument.StandardsBuildingType); the office whole-building non-geometry fractions are openstudio-standards 90.1-2019's (NinetyOneTwentyNineteenSpaceTypes:WholeBuilding - Sm Office0.43 / 0.14;- Md Office,- Lg Office,- Lg Office-basement,- Lg Office-others0.68 / 0.32;ashrae_90_1_2019.spc_typ.jsonatc8c1b9eb, SHA-256d3b988cb…c0e72), recorded in theDaylightingRulesource line. So the retail, strip-mall, and apartment exceptions and the office fractions apply to IECC programs as to 90.1-2019's. - Tests first (observed failing before the change: the rule was null):
CodeDaylightingTests.TheIeccEditionsTakeTheNinetyOneTwentyNineteenRule(six editions;IECC 2024andIECChave none),ReleaseTwoTests.AnIeccProgramTakesTheNinetyOneTwentyNineteenRuleWithItsSpaceTypesFractions(the IECC 2021 small office:WholeBuilding - Sm Office, 0.43 / 0.14, 5B),…AnIeccProgramWhoseNameIsNotItsSpaceTypeAndBuildingTypeHasNoSpaceType,IdfCodeDaylightingTests.AnIeccProgramsControlsArePlacedByTheNinetyOneTwentyNineteenRule(controls written and checked).IdfCodeDaylightingTests.AProgramWithoutACodeRuleGetsNoControlsWithAWarningnow gives the IECC fixture the template 90.1-2007. ReplacesReleaseTwoTests.AnIeccProgramsControlsHaveNoCodeRule. - Changed outputs. With Daylighting on, IECC programs' zones get controls where the rule requires them; off (the default) nothing changes. No snapshot changed.
- Docs. ADR-037,
program-json.md,convert2idf.md, the BG3 note's open item, the smoke checklist (the BG3 IECC item superseded, a BG4 item).
D-249 — DOE skylights by atlas family, from each family's source geometry (the owner's G6, ADR-039)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's round-2 answer G6: "skylights in BEMGen's DOE stand-alone retail and warehouse geometry (and any other layout) only where the source geometry carries them, per family if they differ"); the BG4 builder (the reading and the realisation below) · Status: Accepted · Amends: ADR-036 decision 7 for skylights · ADR-039, design note §3
- Sources read (
scripts/doe-geometry/family_skylights.py; every file's SHA-256 in each family'sfilesSha256): the DOE Ref v1.4 pre-1980 and post-1980 IDFs of all fifteen types (16 climates each), the PNNL 90.1-2007, 2013, 2019 and IECC 2006 to 2021 prototype IDFs of the fourteen PNNL types (19 climates each), and for the supermarket the openstudio-standardsc8c1b9ebgeometry its 90.1 prototype inputs name (ASHRAESuperMarket.osmfor 90.1-2007,ASHRAE9012013SuperMarket.osmfor 2013 and 2019). Checked against them: the layout records' source (DOE Ref v1.4 New2004 Chicago: the schools' gyms only), the openstudio-standards template geometries of the retail, warehouse, and schools (equal to the PNNL models outside 6A to 8A), andPrototype.RetailStandalone.rb/Prototype.Warehouse.rb, which put the 90.1-2004/2007 skylights back in 6A, 6B, 7A, and 8A for 90.1-2010+. - Found (skylight area / the key's source roof): no skylight in any DOE Ref pre-1980 or 1980–2004 model, nor in any family's offices, restaurants, strip mall, hotels, hospital, or outpatient. Stand-alone retail
Core_Retail(1600.48 m²): 6.689 m² (0.004179) in 90.1-2007, IECC 2006, 2009, and 6A–8A of the later families; 23.7832 m² (0.01486) in 90.1-2013, 2019, IECC 2015–2021 elsewhere, and IECC 2012 everywhere. WarehouseBulk(3204.84 m²) andFine(1393.41 m²): 14.8645 m² (0.004638) and none in 90.1-2007, IECC 2006, 2009, and 6A–8A of the later families; 47.5664 (0.014842) and 20.8103 m² (0.014935) in 90.1-2013, 2019, IECC 2015–2021 elsewhere; 71.3495 (0.022263) and 25.2696 m² (0.018135) in IECC 2012 everywhere. Primary schoolGym13.378 m² (0.037473), secondary schoolGym80.277 m² (0.040626) andGym - audience53.5473 m² (0.042906) in every 90.1 and IECC family, as the records. SupermarketSales35.6748 m² (0.015344) in 90.1-2013 and 2019 (over its main sales, a part of BEMGen'sSales), none in 90.1-2007. - Decision.
family-skylights.json(embedded,bemgen-doe-family-skylights1, 144 kB) holds per type and family the climate groups (the default the group of the most climates, the template's own geometry) and per group each skylit key's area, roof, and ratio;DoeFamilySkylightsreads it.DoeProgramPresets.KeyRatios(type, key, template, climateZone)keeps the record's window ratios and gives the family's skylight ratio (0 for a key without one),SkylightSource, andClimateZoneAssumed(the zone none or not among the source's, where the groups differ: the default group). The DOE Programs components' atlas maps use the map's family and the Climate Zone input (a warning names the keys when the zone is assumed; the remark says the skylights are the family's source's); the test-suite generator'srecordglazing does for a DOE slot whose programs all have one family's atlas ID (ProgramFiles.AtlasTemplate), in the test case's climate zone. The illustrative presets keep the record's glazing. The generators' skylight rule is unchanged (one skylight per ceiling of the key's ratio, centred, S5). - Tests first (
DoeFamilySkylightsTests, 25, failed to compile before the change: no four-argumentKeyRatios, noDoeFamilySkylights): the retail core and the warehouse storages by family and climate zone, the schools' gyms (none in DOE Ref, the records' elsewhere), the supermarket's sales, no zone or an unknown one taking the default withClimateZoneAssumed, every family of every prototype read with the record's windows, and a family without the prototype refused. Written with their code:DoeAtlasProgramMapTests.ARetailMapOf2019GivesTheCoreThePrototypesSkylights(5A 23.7832 m², 7A 6.689 m² of skylights on the core's roof through the generator, the stacker, and validation),DoeSuiteTests.RecordGlazingTakesTheSkylightsOfTheProgramsFamily(the 90.1-2019 and DOE Ref 1980–2004 warehouse in 3B),scripts/doe-geometry/test_family_skylights.py(3). - Changed outputs. Atlas maps and record-glazing suites of the retail, warehouse, and supermarket get toplit roofs where their family's source has them; the schools' gyms lose theirs in the DOE Ref families. No snapshot changed. The LOD study is unchanged (D-244).
- Docs. ADR-039, ADR-036,
doe-components.md,doe-layouts.md,scripts/doe-geometry/README.md, the test-suite generator's README, the smoke checklist.
D-250 — The prototype check of code-rule daylighting run again on the retail and the warehouse with their skylights¶
- Date: 2026-11-01 · Decided by: the BG4 builder (the check G6 asks for; a record, not a rule) · Status: Accepted · Follows D-235 · Design note §4
- Check. The suite generator (this branch) wrote the 90.1-2019 stand-alone retail and warehouse, 5A Buffalo TMY3, No Simplification, Stack,
settings.daylighting: true,recordglazing, with their atlas release 2.3.0 filled programs (the live site's store files ofcommercial/90-1-2019/retail-standalone/andwarehouse/) and the built-in Example envelope; one scratch script read the controls of these IDFs and of the PNNL 90.1-2019 Buffalo IDFs (controlled floor fraction: the floor of the zones with controls; controlled lighting fraction: each zone's lighting power times its points' shares). The script gives PNNL D-235's numbers. - Result (BEMGen / PNNL; D-235's BEMGen value in brackets): retail controlled floor 0.698 / 0.698 (0.066), controlled lighting 0.090 / 0.387 (0.012), zones with controls 1 of 5 / 1 of 5 (the core); warehouse floor 1.000 / 1.000 (0.052), lighting 0.208 / 0.492 (0.058), 3 of 3 / 3 of 3. Per zone, BEMGen / PNNL: retail core share 0.115 / 0.500; warehouse bulk 0.112 / 0.500, fine 0.230 / 0.500, office 0.768 / 0.390. Both IDFs ran a year in EnergyPlus 26.2.0
4bd7a1f26f(eplus-check): 0 severe; the retail passed with its one expectedPartialDaylightingControl; the warehouse reported one unexpected warning,Site:GroundTemperature:BuildingSurface: Some values fall outside the range of 15-25C., which comes from the Example envelope's ground and not from the controls. - Why the lighting differs. The skylight area is the source's, but BEMGen puts one centred skylight of the key's ratio on each roof (rule S5), where the PNNL core has 16 of 1.49 m² spread over the roof (and the warehouse 32 and 14): grown by 0.7 × the 6.1 m (8.5 m) height, one skylight's toplit area covers far less of the roof than the source's spread ones, so the toplit shares are a quarter (retail core, bulk storage) to a half (fine storage) of PNNL's. The warehouse office is controlled more than PNNL's because BEMGen draws it to the storey height (G4), with taller windows. Distributing skylights as the source does is a change of the skylight rule, for the owner (ADR-039, alternatives).
D-251 — Stage BG4: the review, its fixes, the checks, and the Rhino steps left for a person¶
- Date: 2026-11-01 · Decided by: the BG4 builder (the fix plan's round-2 rules: verify before the merge, Rhino not launched, no version bump) · Status: Accepted · Design note
- Review (D-111). A second agent reviewed the diff read-only against the sources (it reproduced
family_skylights.py --check, re-read sample PNNL, DOE Ref, and openstudio-standards files, and found the areas, the office fractions, and the IECC inputs as recorded) and approved it with fixes, all made: the skylights' climate zone is read as the program import reads it (trimmed, upper case) and the bare7and8as7Aand8A(DoeFamilySkylightsTests.TheClimateZoneIsReadAsTheProgramImportReadsIt, written with the fix, not observed failing first); the suite test gains the DOE Ref primary school's gym, which fails without the family lookup (the record has a skylight);family_skylights.pyrefuses trailing numbers that are not whole vertices; a broken anchor in D-247 and the note's data path corrected. Its point that the department-mix coverage text had no unit test was already covered byAtlasProgramMapTests.ACrosswalkKeyIsCoveredByItsDepartmentMixAndNamed. - Checks.
scripts/verify.ps1onebf0ddb: VERIFY PASSED, 0 warnings: Core 1097, Generators 3150 (1 skipped), Integration 3670, Export 3158 (1 skipped), ConstructionJson 341 (1 skipped), ProgramJson 621 (6 skipped), Simulation 276 (16 skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 169, Python 107.extract.py --checkandfamily_skylights.py --check: CHECK PASSED;test_family_skylights.py3 passed;developer_pages.py --check: 126 pages, the one known redaction. There is no CI (D-005). - Not done here. The version is not bumped and nothing is tagged (REL-B2 releases). Rhino was not launched: the headless smoke specs
doe-atlas-maps,doe-single-storey, anddoe-presetswere updated and not run, and the smoke checklist's BG4 items are for a person. The skylights' distribution (one centred skylight per roof, S5) is unchanged; a source-like distribution is for the owner (D-250, ADR-039).
D-252 — DOE skylights spread as the source does; amends ADR-039 decision 4¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the owner's principle for skylights, "follow the source evidence", and the stage BG5 brief: place the skylights as the source geometry each family cites does, scaled to BEMGen's roof, with BG4's area); the BG5 builder (the realisation below) · Status: Accepted · Amends: ADR-039 decision 4 and DOE geometry note S5 for keys with a source layout · Design note
- Source layouts (
scripts/doe-geometry/family_skylights.py, the files D-249 reads and the layout record's New2004 reference file;family-skylights.jsonformat version 2,layouts): stand-alone retailCore_Retail8 skylights of 0.914 m in 2 rows of 4 in the roof's north-west corner (ratio 0.004179) or 16 of 1.219 m in 4 × 4 over its west half (0.01486); warehouseBulk10 (2 × 5, south-west corner), 32 (8 × 4, the southern 31 m), or 48 (8 × 6, IECC 2012);Fine14 (7 × 2, the north half) or 17 (IECC 2012); supermarketSales24 (4 × 6 over the west half of the main sales, 90.1-2013 and 2019); the gyms 9 (3 × 3), 54 (6 × 9), and 36 (4 × 9) over the whole roof in every 90.1 and IECC family and in the record. One layout per key and ratio: the layouts follow BG4's climate groups exactly, and the New2004 gyms equal the PNNL ones to 0.1 mm. The script refuses what a layout cannot hold (a skylight that is not an axis-aligned horizontal rectangle, a multiplier, a zone turned from north, a key of several zones, a frame other than the record zone's size within 0.01 m, two layouts of one ratio). - Decision.
SkylightLayout.Place(Lod.Core.Geometry) maps a layout's frame, the source roof's bounding box, onto the roof's: each skylight's centre scales with the roof's width and depth, and all skylights scale about their centres by√(area / source area), so their total is the key's area, BG4's ratio × the ceiling area. A skylight that leaves the roof or meets another is not moved:SkylightLayout.PlacereportsSkylightDoesNotFit, and the plan generators then give that ceiling one centred skylight of the same area (S5) with the warningSkylightLayoutNotFitted, so every set of generator parameters valid before stays valid (the warehouse's fine storage, an L around the office, does not hold its source rows when its band is narrowed to 12 m or the office deepened to 20 m). Each ceiling of a zone takes the whole layout with its own area (no DOE zone has two).PlannedZone.SkylightLayoutscarries a zone's layouts;PlanBuilderuses the one whose ratio is the preset's (withinAbsoluteFraction; a key's ratios differ by 0.003 at least, so none is mistaken), any other ratio keeps one centred skylight (S5), and the plan's provenance gainsSkylightLayoutsonly for zones whose ceilings took the layout. The retail, warehouse, supermarket, and school generators give their zones the layouts of their keys (DoeFamilySkylights.WithSourceLayouts), so atlas maps, record-glazing suites, and the schools' illustrative presets get the spread skylights.CentredSkylights.Placekeeps the skylights of a target ceiling that is a detailed ceiling unchanged, so No Simplification and every floor aggregator keep them on unchanged roofs; a merged or split ceiling takes one centred skylight as before (ADR-012). - The prototype check again (D-250's sites and script, this branch, EnergyPlus 26.2.0): controlled floor fraction BEMGen / PNNL retail 0.698 / 0.698, warehouse 1.000 / 1.000 (unchanged); controlled lighting fraction retail 0.402 / 0.387 (D-250 0.090), warehouse 0.618 / 0.492 (0.208). Per zone: retail core 0.515 / 0.500; bulk 0.528 / 0.500, fine 0.710 / 0.500, office 0.768 / 0.390. The gap left is not the skylights': BEMGen's shares are the code rule's geometric toplit fractions (ADR-037) on the source's toplit areas, PNNL gives every toplit retail and warehouse zone two points of 0.25 whatever its toplit area (Miami, Buffalo, Seattle checked), and the fine storage's skylights light 71 % of its floor; the warehouse office is D-250's (G4). Both IDFs ran a year: 0 severe, the retail with its one expected warning, the warehouse with D-250's one unexpected ground-temperature warning from the Example envelope.
- Review (D-111). A second agent reviewed the diff read-only (it reproduced
family_skylights.py --checkand checked one PNNL warehouse skylight against the data) and did not approve it: a family ratio at valid non-default warehouse dimensions made the plan fail withSkylightDoesNotFit, where one centred skylight had worked. Fixed by the fallback above, withSkylightTests.ALayoutThatDoesNotFitTheRoofGivesOneCentredSkylightWithAWarningandDoeSkylightLayoutTests.AWarehouseWhoseOfficeNotchTakesTheFineStoragesSkylightRowGetsOneCentredSkylightWithAWarning(both failing to compile before it: noSkylightLayoutNotFitted); its note that each ceiling of a zone takes the whole layout is now in the documentation. - Tests first (failing to compile before the change: no
SkylightLayout, noDoeFamilySkylights.Layouts; the stack test observed failing against the unchangedCentredSkylights):SkylightLayoutTests(6),SkylightTests.AZoneWithASourceLayoutOfItsPresetsRatioTakesTheLayoutsSkylightsAndSaysSo,…AZoneWhosePresetHasAnotherRatioThanItsLayoutsTakesOneCentredSkylight,…AnUnchangedRoofKeepsTheSourceLayoutThroughTheStackAndAMergedCeilingTakesOneCentredSkylight;DoeSkylightLayoutTests(24: every family's count per key, the area kept, the retail core's first skylight where PNNL's stands, the schools' illustrative gyms, a ratio of no source, every ratio of every family and record with its layout and only the five types, the stack keeping them);test_family_skylights.py(3 more);DoeAtlasProgramMapTests.ARetailMapOf2019GivesTheCoreThePrototypesSkylightsnow also counts 16 and 8. - Changed outputs. The skylit zones' skylights of those five types where the preset has a source's ratio: positions and count, not area. No snapshot changed. The LOD study stays on its pinned inputs (D-244); rerun with this BEMGen, its 90.1 reference sites of the retail, warehouse, supermarket, and primary school would carry spread skylights and its merging zonings one per target, with the same area, and no daylighting.
- Docs. ADR-039,
pipeline.md,validation.md,domain-model.md,doe-layouts.md,doe-components.md,doe-school.md,scripts/doe-geometry/README.md, the smoke checklist's BG5 section, the smoke specdoe-schools(a check added, not run). - Checks.
scripts/verify.ps1on the stage's code: VERIFY PASSED, 0 warnings: Core 1107, Generators 3174 (1 skipped), Integration 3670, Export 3158 (1 skipped), ConstructionJson 341 (1 skipped), ProgramJson 621 (6 skipped), Simulation 276 (16 skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 169, Python (LOD study) 107.family_skylights.py --check: CHECK PASSED;test_family_skylights.py6 passed;developer_pages.py --check: 127 pages, the one known redaction. Rhino was not launched: the smoke checklist's BG5 items are for a person, anddoe-schoolswas not run. There is no CI (D-005); the version is not bumped.
D-253 — Atlas release 2.4.0 contracts: program JSON 6.1.0, constructions unchanged (ADR-040)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the fix plan's round 2, stage REL-B2: "move to atlas 2.4.0", no backward-compatibility shims); the REL-B2 builder (the realisation and the routine choices below) · Status: Accepted · Supersedes in part: D-239 and ADR-038 decision 1 (the program contract and the known release) · ADR-040, design note
- Decision. BEMGen reads atlas release 2.4.0 (atlas PR #50, merge
7c543f5d, its ADR 0060 to ADR 0062): program JSON 6.1.0 only (ProgramJsonDocument.SchemaVersion;schemas/program-json-v6.1.schema.json, revision 1, embedded byte for byte, SHA-25665fc9063…32ed), and the unchanged unified 4.0.0 and detailed 5.0.0, whose schemas stay.KnownReleasesisatlas-release-2.4.0andKnownReleaseManifestsrelease 2.4.0's manifest51ce9c9e…bd7f. A construction document of release 2.3.0 is still read as one of 2.4.0 (the same contract, no set moved:ReleaseTwoTests.ADocumentOfReleaseTwoPointThreeIsTheSameContractAndIsRead).building-services-v1andbundle-v4are unchanged;request-v4only re-keys its example ID, which BEMGen does not embed. - Fixtures.
export_fixtures.pyexports release 2.4.0 (now with--jobs, default 4: each export loads the whole release, and the 56 exports took 2 minutes at 6 at a time); twelve programs (the eight before under their 2.4.0 IDs, and for the cap the 90.1-2019 warehouse fine storage and office, strip mall type 1, and large office others) and the 22 construction pairs inatlas-2.4.0/(equal to 2.3.0's but forrelease);--checkpasses. The synthetic program documents takeschema_version6.1.0 andatlas-release-2.4.0by a one-off substitution. The three recorded sites were recorded again from the live site of release 2.4.0 (record_site_fixtures.py; the dining's ID moved). Changed checksums, computed again outside BEMGen with Python'sjson.dumps(sort_keys=True, separators=(',', ':'), ensure_ascii=False): the 90.1-2019 medium office3462552…c2c4, the DOE Ref medium officecd53e93e…4738, the recorded open officedc119008…98e0, the unified and detailed medium office 3Bd236a842…01a0and20cb77d5…d89e. - Values that moved with the data. The 90.1-2019 medium office's lighting is PNNL's 6.66362 W/m² (6.888902666694222 before, the atlas's ADR 0060 section 2), in the tests, the snapshot
medium-office-preset, and theprogram-jsonsmoke spec; the medium office has 16 evidence rows (14). - Tests first: with the fixtures re-exported and the reader unchanged, 199 program tests failed on
ProgramJsonVersionNotSupported(6.1.0 refused as newer); then the reader and schema moved. - Reported to the owner. The 2.4.0 delta folder carries a rewritten
id-map-2.3.0.jsonwhose header still saysrelease2.3.0 while its 873 doubly moved entries map 2.2.0 IDs to 2.4.0 IDs (the "citing files follow the new IDs" edit reached it). BEMGen embeds release 2.3.0's original file and chains it withid-map-2.4.0.json(D-254); the chain equals the rewritten file for all 1,562 entries, so neither reading gives a wrong ID, but the label is the atlas's to correct.
D-254 — Stored atlas texts of releases 2.0.0 to 2.3.0 refused, naming their 2.4.0 IDs through both ID maps (ADR-040)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the stage brief: "stored 2.3.0 IDs are refused naming their 2.4.0 IDs through
id-map-2.4.0.json; 2.0.0 to 2.2.0 IDs chain throughid-map-2.3.0.jsonfirst, extend D-240's mechanism"); the REL-B2 builder (the realisation) · Status: Accepted · Amends: D-240 and ADR-038 decision 3 · ADR-040 decision 2 - Decision.
AtlasIdMap.Mapsembedsid-map-2.3.0.json(release 2.3.0's, SHA-256b4c79774…da7b, 1,562 IDs) andid-map-2.4.0.json(release 2.4.0's,9d6ead4f…70e9, 2,301 programs, no construction set), byte for byte.Translate(id, release)applies every map whose base release is the ID's release or later: an ID of 2.0.0 to 2.2.0 through both, of 2.3.0 through the second. A program JSON 5.x (an ID of 2.2.0) or 6.0.0 (of 2.3.0), a unified 3.1.0 or detailed 4.3.0 document (2.2.0), and a DOE map or mix stored from a site release before 2.4.0 (AtlasSiteStoredBeforeRelease;FirstReadRelease2.4.0) are refused naming each 2.4.0 ID and the maps used (through the releases' id-map-2.3.0.json and id-map-2.4.0.json, orthe release's id-map-2.4.0.json). Schedule Draw's stored Generator JSON stays as D-240 says: a stored 2.3.0 file fails for a moved program withScheduleDrawRecipeNotFound. - Where BEMGen stores atlas IDs: the tests, smoke specs, and documents name 2.4.0 IDs (translated through the map by a one-off script, the refusal tests keeping their old IDs); the decision log and past plans keep the IDs of their time; the LOD study keeps its pinned 2.0.0 IDs (D-244).
- Tests first:
AtlasIdMapTests(rewritten, failing to compile onAtlasIdMap.Maps): both files byte for byte, IDs moved twice, once in 2.3.0, once in 2.4.0, not at all, and a 2.3.0 ID not looked up in the 2.2.0 map; thenReleaseTwoTests.AProgramJsonOfAnEarlierReleaseIsRefusedNamingItsReleaseTwoPointFourId(5.4.0 and 6.0.0), the construction refusals, and, after the review,AtlasProgramMapTests.AMapStoredFromSiteReleaseTwoPointThreeIsRefusedNamingTheMovedIds.
D-255 — Schedule Draw on atlas-schedules 4.1.1 and atlas release 2.4.0; amends ADR-035¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the stage brief: "pin the generator wheel 4.1.1"); the REL-B2 builder (the recording) · Status: Accepted · Amends: ADR-035 decision 2 (the pins), D-242
- Decision.
ScheduleDrawSettingpins generator 4.1.1 (4.1.0's code, whose wheel bundles the generator view of release 2.4.0, the atlas's ADR 0062 section 4), data release 2.4.0, and its manifest51ce9c9e…bd7f; request and bundle schema 4.0.0 and the setting 2.0.0 are unchanged. The program setting is the folder'sgeneration.jsonof release 2.4.0 (or of the earlier release whose file the delta keeps: the detailed offices'). - Recording. The four recorded draws were drawn again with
python -m atlas_schedules(atlas-schedules 4.1.1 (request schema 4.0.0); reads atlas release 2.4.0) in a checkout of atlas7c543f5d, through BEMGen's own request writer; two runs gave the same bytes. The released wheel (SHA-25621560a1f…) was not downloaded in the build;RealDrawTestswith it is a Rhino-run step (D-260).RealDrawTestswithBEMGEN_ATLAS_GENERATORthe checkout: 3 passed.
D-256 — The unoccupied lighting cap: PNNL's EMS program per zone, on request, off by default (the owner's G4, ADR-040)¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the review's round-2 answer G4: "BEMGen applies it only to zones whose people are not zero at all times"; the stage brief: apply it in Convert2IDF as PNNL does, per zone, and decide the default consistently with D-233 and D-244); the REL-B2 builder (the realisation and the default below) · Status: Accepted; the default and the reading of G4 for a rule on another zone are Provisional, for the owner · Amends: D-234 ("BEMGen adds nothing") · ADR-040 decisions 3 to 6, design note §3
- The rule in BEMGen.
UnoccupiedLightingCap(Lod.Core.Programs) onZoneProgram.LightingCap; Program JSON readslighting_controls.unoccupied_lighting_cap(a null rule a source zero with itsnullsreason), recordsLightingCapin Source, and lists in Left Out when it is written and each source zone's added condition as not applied. A merge or mix keeps it, with a floor-area record, only when every source carries the same rule; otherwise none, with the warningLightingCapDiffers. - Written on request. Convert2IDF's appended input Lighting Cap (index 13,
LCap, Boolean, default false; the component's GUID unchanged),IdfOptions.WriteUnoccupiedLightingCap, and the suite spec'ssettings.unoccupied_lighting_cap(default false) write, per zone with the cap:EnergyManagementSystem:Sensors on the People Occupant Count the rule reads and on each Lights object's electricity rate,EnergyManagementSystem:Actuators (Lights,Electricity Rate) on the general lights (and the additional ones where the rule sets them), andCap<k>_Program:IF (occupants <= 0) && (lighting >= (threshold * area)), the first general lights set tocap * area, elseNULL; one calling manager per calling point. Header, provenance (LIGHTING CAP: …), and the infosLightingCapNotWritten/LightingCapWritten. A building without the cap is written byte for byte as before either way. - The default: off. As D-233 keeps code-rule daylighting off: the cap is a lighting control from the program's
lighting_controlswhose effect depends on the zoning (a merged zone is capped only when all its sources are empty), so on by default a zoning would no longer conserve its reference's hourly lighting (D-157, GLOBAL.md scientific rule 4), and the LOD study, on its pinned inputs (D-244), keeps both off. The owner may turn it on by default; the cost of off is D-257's 2.5 % of the medium office's lighting. - G4. A zone is capped only when the people the rule reads are not zero at all times (an occupancy load with design occupants and a schedule above 0 in some hour). For
other_zone_occupant_count_zerothose are the condition zone's people, not the zone's own: the warehouse storages have none of their own, and PNNL caps them while the office is empty, which is not a permanent cap. The literal reading (the zone's own people) would never cap them. For the owner to confirm. Otherwise the warningLightingCapNotApplied. - What BEMGen's zones cannot express. The
condition_zone(Zone1 Office) is matched to the building's zones by name or ID; without a match the zone is not capped (reading its own zero count would cap it for good). So in BEMGen's DOE warehouse, whose office zone isOffice, the storages are not capped: either the DOE layouts carry their source zone names or the atlas records the condition zone's program (90.1 programs carry nosource_zones), for the owner. Thezone_conditions(the medium and large offices'Perimeter_mid_ZN_2, capped only outside 07:00 to 16:00; the small office'sPerimeter_ZN_2, only while the binary occupancy schedule is 0) name single source zones and are not applied: every capped zone takes the rule without them (1 of 15 or 5 source zones capped somewhat more than in PNNL), listed in the header and Left Out. - Tests first (each observed failing before its code: the core, program JSON, and export tests failing to compile, the suite test on the spec schema):
UnoccupiedLightingCapTests(11),LightingCapImportTests(9),IdfUnoccupiedLightingCapTests(8: off, unchanged without the cap, every occupied zone's program, a zone with no people, a condition zone by name and missing, the strip mall's additional lighting and the large office's calling point, a merged zone),DoeSuiteTests.TheLightingCapIsWrittenOnlyWhenTheSpecAsksForIt, and the opt-inUnoccupiedLightingCapEndToEndTests(D-257).IdfChecksfails any EMS object unless a test passeslightingCap: true, which checks the classes, the sensors' and actuators' objects, the managers' programs, and every Erl name. - Changed outputs. The program preset snapshot
medium-office-presetgains itsLightingCapline; Left Out of the medium office gains two lines (13); no IDF snapshot changed.
D-257 — The unoccupied lighting cap run a year on the 90.1-2019 medium office¶
- Date: 2026-11-01 · Decided by: the REL-B2 builder (the check the stage brief asks for, as D-235 and D-250 did; a record, not a rule) · Status: Accepted
- Office plate (
UnoccupiedLightingCapEndToEndTests,BEMGEN_ENERGYPLUS, EnergyPlus 26.2.04bd7a1f26f, Chicago O'Hare TMY3; the 90.1-2019 medium office program of release 2.4.0, filled, 5A): both years completed with 0 severe, 0 fatal, and 0 warnings; interior lights 64 567.6 kWh off and 62 961.8 kWh on (2.49 % less), equipment 125 213.7 kWh both. (CodeDaylightingEndToEndTestsnow gives 64 567.6 kWh off and 51 395.4 kWh on, D-233's 66 750.5 and 53 133.0 before: the PNNL lighting power of D-253.) - DOE prototype (the suite generator's DOE medium office, 90.1-2019, every key the release 2.4.0 whole-building program, No Simplification, Stack, the built-in Example envelope, Buffalo TMY3,
settings.unoccupied_lighting_capfalse and true;eplus-check): both passed with 0 warnings and 0 severe; 65 of 65 zones got the EMS program; interior lighting 306.37 GJ (85 103 kWh) off and 298.75 GJ (82 986 kWh) on, 2.49 % less; interior equipment 594.14 GJ both. The cap acts at the unoccupied hours whose scheduled lighting is above 0.215 W/m² (the night and weekend fractions of the 2013 office lighting schedule on 6.664 W/m²). Not compared with a PNNL run.
D-258 — The building services of release 2.4.0: zone services listed in Left Out, building-level elevators not read¶
- Date: 2026-11-01 · Decided by: the REL-B2 builder (the stage brief: write or list them per the existing decisions) · Status: Accepted · Follows D-126 and D-172 (building services listed in Left Out, not instantiated)
- Found. Release 2.4.0 adds 84 PNNL 90.1 elevator and kitchen reach-in services (the atlas's ADR 0060 section 3): 66 zone services, which programs list in
building_service_ids(the medium office's two elevators, the kitchen's two reach-ins, …), and 18 building-level exterior elevators of the hospital, large hotel, and large office (assignment: building_level, all heat lost), which are only in the folder's building-services export; services take their schedule fromschedule_id. - Decision. No change: as for every building service since D-126, each of a program's
building_service_idsis a Left Out line (Building service '<id>': not added to the program.;ImportTests.ReportsTheUnusedDesignDayRulesOncePerSchedulepins the medium office's two). BEMGen does not read the building-services export, so the building-level elevators, which no program names, are neither modelled nor listed; they lose all their heat and add no zone load, so the zones' energy balance does not miss them, only the building's metered electricity. Reading the services export (zone services into their beneficiaries' zones, building-level ones as exterior equipment) is a feature for a later minor, for the owner.
D-259 — Toplit daylighting keeps the code rule's computed shares; D-252's warehouse gap accepted¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (asked whether skylit zones should take PNNL's fixed share, two reference points at 0.25 each, the program's recorded
controlled_lighting_fraction, 0.5 or 0.17 in 6A to 8A, to close D-252's warehouse gap of 0.618 against PNNL's 0.492; the answer, relayed on 2026-11-01: "Keep computed") · Status: Accepted · Keeps ADR-037 decisions 3 and 4 - Decision. No code change: a toplit zone's points control the code rule's geometric toplit fractions on BEMGen's geometry. The remaining warehouse gap (controlled lighting fraction 0.618 against PNNL's 0.492, D-252) is accepted; it can be revisited in a later minor.
D-260 — Version 1.10.0 on atlas release 2.4.0, with the unoccupied lighting cap¶
- Date: 2026-11-01 · Decided by: Cheng Xuan Li (the fix plan's round 2, stage REL-B2, with the standing authority to merge and push
main; the go-ahead for the Rhino run, relayed on 2026-11-01: "you have the go ahead for rhino run to rebuild the samples etc."); the REL-B2 builder (the preparation) · Status: Accepted - Version. 1.10.0 (
Directory.Build.props), a minor version as D-245 reasons: D-064 waives backward compatibility; no component or parameter GUID or icon changed; Convert2IDF gains the appended input Lighting Cap (index 13), and descriptions change (Program JSON, Construction JSON, Schedule Draw, Convert2IDF). - What it holds since
v1.9.0: the round-2 stages BG4 (D-246 to D-251) and BG5 (D-252) onmain, and REL-B2 (D-253 to D-259, ADR-040). - Review (D-111). A second agent reviewed the code diff read-only (it rebuilt and ran the tests,
export_fixtures.py --check, and recomputed the ID chain against the atlas's rewritten map) and approved it, with findings: the warehouse storages' condition zone, which BEMGen's DOE warehouse cannot name (recorded in D-256 for the owner), the reading of G4 for a rule on another zone (D-256, for the owner), the threshold's product parenthesised (Erl evaluates*before>=; the year runs gave the same lighting before and after), a site-level test of a map stored from release 2.3.0, the 2.2.0 refusal naming both maps, a neutral name for the writer's optional-section record, and stale comments: all made (77032e4e). - Checks:
scripts/verify.ps1onrelease/1.10.0: VERIFY PASSED, 0 warnings: Core 1118, Generators 3174 (1 skipped), Integration 3670, Export 3166 (1 skipped), ConstructionJson 343 (1 skipped), ProgramJson 639 (6 skipped), Simulation 276 (17 skipped), IdfBatchRunner 105 (3 skipped), TestSuiteGenerator 170, Python 107. Gated:LiveSiteTestswithBEMGEN_ATLAS_SITE_LIVE=13 passed (live 2.4.0);LiveDoeRefMassTests1 passed (152 imported, 0 failed);RealDrawTestswithBEMGEN_ATLAS_GENERATORthe atlas checkout at7c543f5d3 passed; the opt-inUnoccupiedLightingCapEndToEndTestsandCodeDaylightingEndToEndTestswithBEMGEN_ENERGYPLUS2 passed (D-257).export_fixtures.py --check: CHECK PASSED.developer_pages.py --check: 129 pages, the one known redaction. There is no CI (D-005). - Release notes (draft).
- Changed: BEMGen reads atlas release 2.4.0: program JSON 6.1.0 (constructions unchanged). Texts of releases 2.0.0 to 2.3.0 (wired or stored in a definition, including the DOE maps and atlas mixes) are refused, naming each program's ID in release 2.4.0 through the atlas's ID maps; Get from Atlas… stores them again. Schedule Draw uses
atlas-schedules4.1.1. - New: Convert2IDF's input Lighting Cap (off by default) writes the atlas's unoccupied lighting cap, PNNL's 90.1-2019 rule for the offices, restaurants, retail, and warehouse, as an EnergyPlus EMS program per zone whose people are not zero at all times; the test-suite spec's
settings.unoccupied_lighting_capdoes the same for a suite. - From the atlas, read now: PNNL's lighting power for the 90.1 offices (90.1-2019 medium office 6.664 W/m²); the 90.1 elevators and kitchen reach-ins, listed in Left Out as building services.
- With the round-2 BEMGen fixes since 1.9.0 (D-246 to D-252): the stand-alone retail's core and front keyed apart, the IECC offices offered, IECC programs on the 90.1-2019 daylighting rule, and the DOE skylights by family, spread as the source does.