Skip to content

S09: OpenStudio native-input read-back and ventilation alignment - #33

Merged
michaldengusiak merged 34 commits into
sow/2026-Q4from
feature/s09-openstudio-input-readback
Oct 23, 2026
Merged

michaldengusiak merged 34 commits into
sow/2026-Q4from
feature/s09-openstudio-input-readback

Conversation

@michaldengusiak

@michaldengusiak michaldengusiak commented Oct 21, 2026 •

Copy link
Copy Markdown

S09 of the SAM simulation roadmap: an independent OpenStudio/EnergyPlus native-input read-back, plus the owner-approved ventilation alignment and, from revision 5, the S07 §5.1 lighting split. Contract: SAM documentation/SIMULATION_INPUT_MAPPING_S07.md. Record: documentation/S09-OpenStudio-InputReadBack-PR.md.

Do not merge without the owner's decision. Revision 12 implements the approved S07 §5.2 amendment U1 (SAM#218 merge b06dbe8c, closeout 0168d03f) and passed independent code review. Its acceptance run at head d26665b is packed (see "Acceptance" below): both fixtures PASS with exit 0, all 21 negatives FAIL. Revisions 10 and 11 passed independent review earlier, including the per-layer fix 4ea2e6c. PASS here is scoped native-input conformance of the OpenStudio export to S07: not simulation equivalence, not a Phase B PASS (which needs S08–S11) and not an owner acceptance of anything else. Ventilation A and lighting B1 stay recorded; OD-L1 stays open. The PR's draft state was set by the owner account and is not changed by this work.

Acceptance at head d26665b (2026-10-23, owner-authorised)

Run after revision 12 passed independent code review. Local only; hosted CI does not run it. Raw root C:\TasOut\s09\acc7-u1 (machine-local). Sanitized pack: documentation/S09-OpenStudio-InputReadBack-evidence/acceptance-2026-10-23/. The earlier packs are kept as history. Binaries were built Release x64 from d26665b (code 8edf793): harness 8a77b235…, library d2368ce8…, writer 67a2ded7…. Fixture and Budapest EPW hashes were checked by the script. Export and translation used the OpenStudio SDK 3.10.0 only; no engine, simulation or sizing was run.

Run Exit (approved) Coverage Out-of-engine-scope report.txt SHA-256
box 0 PASS 319/319 105 (69 differing, 36 equal) 868a5808d4cc1a96513e54b8189e647c8f45f4375b058a940ac1dee719b959fe
two 0 PASS 592/592 216 (138 differing, 78 equal) 9e3a3f0ed0e2fd6d8703154a0cb71a69ffa10344d1e4e253c29886840a6c466d
box-repeat 0 PASS 319/319 105 (69, 36) identical to box
  • Negatives: all 21 FAIL with exit 10, each by its intended mechanism (multiplicity and orientation consistency, DSOA, identity, layer conductivity, people density, T-LIGHT-SPLIT), and the original IDFs are unchanged. Out-of-engine-scope stays at 105 / 216 in all of them except identity-swap-names (180): there the two swapped walls lose verified identity, so their unused-face rows stay gated.
  • Determinism: the repeat's IDF and report.txt are byte-identical, and both report.txt files match the revision-12 test session. The OSM differs only by OpenStudio's random handles, as in every earlier pack.
  • Provisional baseline: box 0, two-zone 12. That pre-decision rule set has no T-VENT-INACTIVE, so the two V1 zones' six rows stay G1. The approved rule set is the verdict.
  • Windows PowerShell 5.1 (scripts not changed):
    • $PSScriptRoot is empty in the parameter defaults, so -Exe and -Target were passed explicitly.
    • Pack-S09-Evidence.ps1 reads reports as ANSI, which turned "§" into "§". That first pack was discarded and repacked with Get-Content defaulted to UTF-8; all 24 packed report-approved.txt files are byte-identical to the raw run.
    • Commands are in the pack's README. A script fix would be a separate change.

Revision 12: S07 §5.2 opaque optical face scope (amendment U1)

Tested content 8edf793a63b274cafe5e55b9e85fa597d6307a10. Changes only the S09 comparator and report (Compare/OsComparison.cs, Report/OsReport.cs), the S09 tests and one note in the material-mapping doc. Unchanged: the writer, importer, CLI, the shared core (SAM_Tas c50e9169), the linked ReadBackExpected.cs, fixtures, tolerances and workflow.

  1. OUT-OF-ENGINE-SCOPE (informational, not gated) applies to a row-3a absorptance or emissivity row only when all of these hold:

    • the face is one EnergyPlus 25.1 does not use (a buried face, or the outer face of an interzone, ground or adiabatic side);
    • the side's face role is established independently (see 2);
    • the SAM layer is an opaque material and the native layer an opaque Material;
    • the SAM value is authored (S-VALUE, or S-ZERO: an authored zero counts as a value) and the native value is read, both within [0, 1];
    • the shared evaluation is a plain numeric comparison (MATCH, WITHIN-TOLERANCE or OUT-OF-TOLERANCE) with no other trigger.

    A mismatching row qualifies whether or not the native value equals the other face. The row is never MATCH, NOT-APPLICABLE or an accepted limitation.

  2. Face role (OpaqueRole): derived from raw SAM, the side and the native surface, never from the writer's selector. All of these must hold:

    • the native surface exists and its identity is verified;
    • the SAM spaces are 1 or 2 and distinct, and include this space;
    • the panel type has an opaque boundary;
    • the panel is not both two-sided and adiabatic or ground;
    • the native Outside Boundary Condition is exactly the SAM one (an adiabatic panel written as Ground, a ground preprocessor and similar forms are not read as Ground);
    • the native construction has one layer per SAM layer;
    • for an interzone side, the partner names this surface back and holds this construction reversed.

    Any other failure is named as Unsupported "optics role: …" (BLOCKED technical at least), and none of the side's rows is excluded.

  3. Stays gated: every used face, every thickness, λ, ρ and c row, and any row with a missing, NaN, blank, unreadable or out-of-domain value. An out-of-domain value is INPUT-INVALID (SAM) or EXPORT-DEFECT (native). Each such row says why it was not excluded. The S09-only OS-MAT-SINGLE-SIDED listing (MAPPING-DIFFERENCE, revisions 2–11) is removed.

  4. Gate and report. The informational result stays in Results, so coverage is unchanged. It carries the shared core's informational flag, so the shared gate ignores it, and everything else gates as before. The report uses schema s09-readback-report/3:

    • CountByOutcome counts each result once and lists these rows as OutOfEngineScope;
    • NonPass and the new GatedNonPass hold blockers only;
    • OutOfEngineScope holds the counts (total, differing, equal, by scope, by property) and every row: SAM value, native value, |d|, side, layer, property, SAM material, native object, reason and amendment;
    • report.txt adds both counts to its first line and a separate OUT-OF-ENGINE-SCOPE block.
Fixture Revision 11 Revision 12 Out-of-engine-scope
s08-fix-box BLOCKED owner (12), 319/319 PASS (0), 319/319 105: 69 differing (the former MAPPING-DIFFERENCE rows), 36 equal (formerly Match 30, WithinTolerance 6)
s08-fix-two BLOCKED owner (12), 592/592 PASS (0), 592/592 216: 138 differing, 78 equal (formerly Match 60, WithinTolerance 18)

The rest of each outcome distribution is unchanged. report.txt SHA-256 (byte-identical in two sessions): box 868a5808…b959fe, two-zone 9e3a3f0e…c466d. Example row: layer1.internalEmissivity: SAM 0.87, native 0.95, |d| 0.07999999999999996 (OutsideTolerance); buried layer face (layer 1 of 3, SAM internal face); SAM 'FIX_Concrete'.

  • Tests: 23 new S09 cases (OpaqueFaceScopeTests.cs), each through the CLI:

    • both fixtures: the in-scope keys equal the unused faces derived independently in the test, and the differing rows equal the single-sided set;
    • used faces: an outdoor face; interzone in both directions;
    • thermal: a buried-layer thickness;
    • an informational row next to a conductivity or return-air blocker;
    • authored zero, differing and equal;
    • native NaN, blank, unreadable and 1.25 on an unused face;
    • SAM absent, NaN and 1.4 on an unused face;
    • an adiabatic-wall control;
    • five undeterminable roles: ground preprocessor, adiabatic written as Ground, interzone not mirrored, partner missing, adiabatic interzone.

    Only the pre-U1 expectations of existing tests changed (fixture PASS, three variants now PASS). Every negative and every revision 10/11 regression is retained.

  • A/B (revision-12/ab-pre-u1.txt), the adcad2f library on the same runs:

    • fixtures and valid variants: BLOCKED owner (12), or FAIL (10) for the authored zero, where U1 gives PASS;
    • the undeterminable-role and out-of-domain-SAM cases: hidden as BLOCKED owner (12), where U1 names and gates them;
    • every defect case: the same exit under both.
  • For review:

    • removing the listing means an unused-face row that cannot be excluded and differs is OUT-OF-TOLERANCE (FAIL);
    • a native exact 0 against an authored non-zero value stays EXPORT-DEFECT;
    • the face premise of EnergyPlus 25.1 is S07's and was not re-verified here (no engine run).
  • Evidence: documentation/S09-OpenStudio-InputReadBack-evidence/revision-12/.

Revision 11: opaque multi-layer import order (owner-approved), fail closed

Tested content 4ea2e6c2ac5805426ed1b39bdd5d88b39b1c723f (commits 37841ea, 5241bdd, f5f207b, 4ea2e6c; f5f207b was the reviewed code). Importer only, plus the new import error code SAM-OSI-CON-002. The writer, S09, revision 10, lighting, ventilation and the single-layer safeguards are unchanged.

  1. Layer order.
    • An opaque layered construction used by planar surfaces is read innermost layer first, so SAM's first layer faces the surface's zone.
    • Unchanged: fenestration (glazing, gas, SAM door panes), unstamped opaque door panes, shading-only, unused, air-boundary and non-layered constructions.
    • A construction shared by surfaces and a door or shade is split and reported (SAM-OSI-APX-001).
  2. Orientation.
    • An interzone panel is built from the side carrying the SAM-written forward construction, also after a geometric-fallback pairing.
    • The spaces are added in a stable topological order that puts that side first, because the writer takes a panel's first space from the cluster's space order. This deviates from the proposal, which relied on the visit order: OpenStudio's handle-based space order broke that.
    • A panel whose building side then ends up second is re-read from its partner.
  3. Fail closed (owner instruction, replacing the first draft's warning-only re-read).
    • Every imported opaque surface panel is checked against snapshots of its native surfaces. The first side must hold the SAM layers innermost first and the second side their mirror. The absorptances the writer would put on every face EnergyPlus uses must equal the native ones.
    • A mismatch is error SAM-OSI-CON-002, naming the panel, construction, face and values, and the import is unsuccessful (no model).
    • Examples: a mixed model whose re-read would move Brick's internal face onto a zone face (0.5 / 0.4 / 0.4 instead of the native 0.9 / 0.7 / 0.7), contradictory forward sides with differing faces, and an edited single-sided wall.
    • Equivalent re-reads and equal-face edits still succeed.
  4. Per-layer consistency (4ea2e6c; the review of 89a2216, P1).
    • Gap: the check compared merged SAM material names only. A 20 mm copy of the 12.5 mm Plasterboard, with the same source metadata, on one side of an interzone wall imported successfully and re-exported 12.5 mm on both sides.
    • Fix: the native snapshot now keeps each StandardOpaqueMaterial layer's thickness, conductivity, density and specific heat. Both sides are compared, each in its own direction, with the panel's SAM layers.
    • The review's reproduction now raises SAM-OSI-CON-002 ("layer 2 ('Reviewer_Plasterboard_20mm') of the surface in space B is 0.02 m thick but the SAM layer is 0.0125 m") and returns no model.
    • Still supported: valid mirrors, one material twice at two thicknesses, and reuse of one material at other thicknesses in unrelated constructions. Massless and air-gap handling is unchanged.
    • Observation, not changed: an imported AirGap keeps its pre-existing 0 thickness, which the writer refuses on re-export.
  • Reproduction from the review: it now re-exports Brick → Insulation → Plasterboard_Internal with the edited 0.42 zone face, and the import succeeds.
  • Tests: 39 new cases (M4/OpaqueLayerOrderImportTests.cs). Each compares every surface's layer thicknesses and thermal properties, plus the absorptances of every used face, before export and after re-export, or requires the named error. They cover:
    • the multi-layer reproduction, edited and unedited;
    • interzone panels in both space orders;
    • geometric fallback;
    • unstamped native models;
    • deterministic mixed models: divergent → error, equivalent → success, equal faces → success;
    • contradictory forward sides;
    • edited walls;
    • controls for glazing, door panes, shading, air boundaries, non-layered and single-layer constructions, and the shared-use split.
  • A/B:
    • With revision 10's importer, 18 of 20 layer-order cases fail.
    • Without the orientation and space order, the mixed cases fail.
    • With the warning-only draft, 7 of the 12 fail-closed cases fail (silently changed faces, or reversed layers).
  • Compatibility:
    • Every multi-layer opaque import now has the opposite SAM layer order to earlier imports. Models imported before revision 11 keep their reversed constructions; there is no automatic migration, and re-importing the source OSM corrects them.
    • Edited or mixed models whose faces cannot be kept now fail with SAM-OSI-CON-002 instead of importing with changed faces.
  • Evidence: documentation/S09-OpenStudio-InputReadBack-evidence/revision-11/.

Revision 10: review of revision 9 (group membership, shared equal-face edits)

Tested content 78f1014 (full SHA in the record). Importer only (ToSAM/MaterialLibrary.cs, OpenStudioImportContext.cs); the writer, SAM-OS-MAT-003, S09 and glazing are unchanged.

  1. A malformed member no longer inherits a valid group's material.
    • Each object carrying a group's SAM.MaterialGuid is validated on its own before any merged material is returned for it, and only a validated member of the group resolves to it.
    • A malformed or inconsistent object (face, identity, stamped optic, written record) is imported as its own material with its own thermal properties and a warning naming it. Import order does not matter.
    • Reproduction from the review: the third object with face BROKEN now imports with conductivity 9 (was 0.25, silent), and the valid Plasterboard pair still merges.
  2. A shared equal-face edit is kept on the faces the object supplies.
    • Internal variants supply the zone face. External objects of materials whose faces differ supply the external face, which keeps genuine asymmetry.
    • External objects of materials whose faces agree take their faces from the constructions using them: in a SAM-written forward construction the last layer is the zone face and the first is the outer face. A mirrored reverse construction adds nothing, and an unknown direction supplies both faces. With no exposed position, the edit goes to the external face and each stated internal face.
    • Reproduction from the review (single-layer Insulation, visible absorptance 0.6 → 0.42): it now imports 0.58 on both faces and re-exports valid with 0.42 and no SAM-OS-MAT-003. Revision 9 invented differing faces.
    • The single-layer differing-face refusal is unchanged.
  • Tests: 23 new face-optics cases (53 in total, up from 30). They cover both reproductions, import orders, malformed and conflicting identity, whole-model import, a thermal conflict, single-layer and interzone single-layer edits, multi-layer zone, outer and middle layers, the interzone multi-layer case, and divergent and unchanged controls.
  • A/B: with revision 9's MaterialLibrary.cs and OpenStudioImportContext.cs restored, exactly the 16 regressions fail and the 7 controls pass.
  • Left open by revision 10, corrected by revision 11: on import, a multi-layer opaque construction came back in reversed layer order. Revision 10 recorded a bounded correction proposal, which the owner approved as revision 11.
  • Evidence: documentation/S09-OpenStudio-InputReadBack-evidence/revision-10/.

Revision 9: review of revision 8 (native edits, merging, inherited faces)

Tested content 354e5322fa3a4c31a984de911c48534e768c40e4.

  1. Native edits are kept.
    • Every opaque object now records what was written (SAM.Written.*), including inherited values and OpenStudio defaults.
    • On import, an absorptance that differs from its written value is an edit. It is kept on the face its object carries and reported (SAM-OSI-MAT-001).
    • An unchanged default stays unstated.
    • Reproduction from the review (light reflectance unstated, visible absorptance 0.7 → 0.42): it now imports internal light reflectance 0.58 and re-exports 0.42. Revision 8 lost the edit.
  2. Merge by explicit source identity.
    • Face and thickness objects with the same SAM.MaterialGuid become one SAM material, with the source Guid restored and each construction layer's thickness kept.
    • A conflict in thermal properties, authored optics or per-face values is reported (SAM-OSI-ID-001) and those objects are not merged.
    • Objects are never merged by name.
  3. Inheritance is reported.
    • An unstated (absent or NaN) internal value on a zone face that takes the external value raises the new SAM-OS-MAT-004 at Summary.
    • Import keeps the value unstated.
    • S09 now reads NaN as unstated. Those zone-face rows are S-UNSPEC, which S07 classifies as EXPORT-DEFECT (FAIL), because S07 lists no default for an internal face.
  • Tests:
    • Writer/importer face-optics tests: 30, up from 21, with controls for an unchanged default, a missing source identity and fully stated internal values.
    • S09: 2 new cases (absent and NaN).
  • A/B: with revision 8's Material.cs, MaterialLibrary.cs, OpenStudioImportContext.cs and OsComparison.cs restored, the 9 new face-optics regressions and both S09 provenance cases fail; the controls pass.
  • Found, not fixed (pre-existing): the importer keeps OpenStudio's outside-first layer order, so a multi-layer construction comes back reversed on round trip. That needs its own task. The re-export tests therefore read a single-layer floor.
  • Evidence: documentation/S09-OpenStudio-InputReadBack-evidence/revision-9/.

Revision 8: opaque face-optics correction

Tested content fc83a6df1f843f82a043353616a2b4e42a94e6d6. Owner-approved in this PR with conditions (2026-10-22).

  • Verified trace: SAM stores layers inside → outside from the panel's first space (GetSpaces() order). The writer wrote every opaque layer's External optics. One material object served every position. Forward and reverse constructions share objects in mirror order. EnergyPlus uses only the outermost outside face and the innermost zone face.
  • Writer:
    • SAM's first layer carries its Internal* optics: an _Internal variant where the material's faces differ, thermally identical to the external-face object.
    • Every other layer keeps External*. The face follows SAM layer position, so interzone constructions stay exact mirrors.
  • Single layer:
    • Ground or adiabatic use: the zone face is written.
    • Outdoor or interzone use with differing faces (including a two-sided panel whose pairing failed): error SAM-OS-MAT-003, no construction, unsuccessful conversion. No external, averaged or split substitute.
    • Checked per panel use, before any cached construction is reused.
  • Orientation: the forward construction goes to the first space's side only. A missing first side is error SAM-OS-ADJ-001 and is never swapped (verified at unit level).
  • Import identity:
    • Every opaque object stamps SAM.MaterialGuid, SAM.MaterialFace and each authored SAM.Optic.* value.
    • The importer restores exactly those values on both faces, even from an Internal variant alone, and never invents a value or merges by name.
    • Malformed or inconsistent identity or values are warned (SAM-OSI-ID-001 / MAT-001) and fall back to the single native value.
  • S09:
    • The used faces are derived from raw SAM data and the side only, never from the writer's selector, and a used face is never listed.
    • Unused faces are listed only when the native value is the layer's other authored face.
    • A single-layer outdoor or interzone side is named Unsupported.
    • OwnerDecisions is removed, and Run / compare are as at revision 6.
Fixture Revision 7 Revision 8
s08-fix-box exit 14 BLOCKED owner, exit 12, 319/319; 69 listed rows, all on unused faces; 18 zone-face rows pass
s08-fix-two exit 14 BLOCKED owner, exit 12, 592/592; 138 listed rows, all on unused faces; 36 zone-face rows pass
  • Regressions:
    • Swapped zone face (external values on each variant): FAIL on exactly 18 / 33 zone-face rows.
    • A wrong optical value: FAIL.
    • Single-layer Screed floor: exit 12.
    • Single-layer outdoor roof: SAM-OS-MAT-003, named Unsupported, FAIL.
  • A/B (8 optics tests):
    • With the 7fccdcc writer restored: 8 fail (fixtures exit 10).
    • With the 8fd0ffc comparator restored: 8 fail (the swapped face is hidden at exit 12).
  • Open:
    • The remaining unused-face rows need an owner decision: accept them, or an S07 3a scope decision through a separate SAM PR.
    • The effect on results is unmeasured; no engine was run.
  • Evidence: documentation/S09-OpenStudio-InputReadBack-evidence/revision-8/.

Revision 7: owner decisions and acceptance reporting (acceptance superseded by revision 8)

Tested content 614a7a8d70b6a70c5628a0ef3b57eaec0c5ec794.

Decision (owner, 2026-10-22) Bounds
Optics option 1: OS-MAT-SINGLE-SIDED accepted as a limitation The two reviewed fixtures only. Exposed zone faces differ by ρs/ρv −0.06, ε −0.01 (Plasterboard) / +0.04 (Screed), 18 / 33 rows; the other 51 / 105 rows sit on faces EnergyPlus does not use. Effect on results unmeasured.
Ventilation A: T-VENT-INACTIVE extended to OpenStudio Rows 6 and 8b, the S08 decision 3 preconditions unchanged. Exercised only by the two-zone V1/V1 fixture.
Lighting B1: the Decision 5 reading confirmed OpenStudio applies p₀ to an unstated p, stated with SAM-OS-IC-002. OD-L1 stays open; no fixture uses the fallback; the Tas side needs a separate SAM_Tas approval.

Ventilation A and lighting B1 confirm what revisions 1–6 already implement; they change no code or verdict.

  • Acceptance reporting (Compare/OwnerDecisions.cs, OsComparison.Run, Cli/ReadBackRead.cs):
    • The S08 core already supports per-quantity, per-fixture owner acceptances (rank 5, exit 14). S09 had passed none, so every report was exit 12.
    • One acceptance is supplied: 3a MAPPING-DIFFERENCE, with the decision reference. It applies only under the approved rules, and only when both the fixture id and the frozen-model SHA-256 (already verified by compare) are a reviewed fixture's.
    • Any other model, fixture id or rule set gets none: BLOCKED owner, exit 12.
    • FAIL, BLOCKED and DOCUMENTED-DEFAULT results (including an unstated p under OD-L1) are unchanged.
    • No schema, exit code, tolerance, writer, importer, core or SAM_Tas change.
  • Fixtures: box 319/319 and two-zone 592/592, exit 14, with outcome counts identical to revision 5. Each report.txt differs only in the status line and the accepted rows' reasons.
  • Tests: 5 new S09 tests, and 1 changed to require exit 14.
    • Fixture-id, unknown-id and provisional-rule controls, plus a changed-model control: all exit 12.
    • A wrong Plasterboard Solar Absorptance FAILs (exit 10) on the external-face rows under the acceptance.
  • A/B: with 8fd0ffc2's ReadBackRead.cs restored (no acceptance), the 2 acceptance tests fail and the 5 controls pass.
  • Not a Phase B PASS. The roadmap gate needs S08-S11 PASS. Recorded acceptance is "Not PASS — accepted limitation"; no roadmap exception is requested.
  • Evidence: documentation/S09-OpenStudio-InputReadBack-evidence/revision-7/.

Revision 6: fail-closed guards in the lighting read-back (independent review of 31af6022; passed independent technical review)

Tested content 225881f00971c7cab3596e94867924a11866e147. Only OsComparison.LightingSplit and its regressions change. The writer and importer split, fixtures, tolerances and shared core are unchanged.

  • Native domain and energy conservation, checked before any S07 tolerance.
    • The reviewed head accepted a split shifted by 0.0004 per field (each row within 0.001): p = 1 summing to 1.0008, and p = 0 with Fraction Radiant −0.0004. Both gave exit 12.
    • Now Return Air, Radiant and Visible must each lie in [0, 1], and their sum must not exceed 1 (S07 §5.1: "a sum above 1 is an input error").
    • A violation is a named EXPORT-DEFECT on all five lighting rows: FAIL, exit 10, with no comparison made.
    • Boundary: 1e-12 (OsQuantities.LightingFractionBoundary) absorbs only the decimal-to-double rounding of the IDF text. It is not an S07 tolerance and loosens none. Valid p = 1 and p = 0 splits pass.
  • Plenum-calculated return air (IDF Lights index 12).
    • Blank, absent or No (the EnergyPlus default) is the static split.
    • Yes cannot be a verified static T-LIGHT-SPLIT.
    • Yes, an uninterpretable value or a relabelled field makes all five lighting rows UNVERIFIED, with a named Unsupported entry: BLOCKED technical, exit 11.
Reviewer artifact (copied; read and compared fresh) Reviewed head Revision 6
control, p1-control exit 12 exit 12, identical counts
p1-invalid-sum (sum 1.0008) exit 12, lighting accepted exit 10, 5 named EXPORT-DEFECT
p0-negative-fraction (Radiant −0.0004) exit 12, lighting accepted exit 10, 5 named EXPORT-DEFECT
dynamic-plenum (Yes) exit 12, lighting accepted exit 11, 5 UNVERIFIED + named Unsupported
  • Regressions (new CLI negative cases plus 8 S09 tests):
    • neg-lighting-sum-above-one (on a p = 1 export) and neg-lighting-radiant-to-visible (on a p = 0 export) → exit 10.
    • neg-lighting-plenum-yes, -unknown and -relabelled → exit 11.
    • Valid controls: p = 1, p = 0, and ctl-lighting-plenum-no (unchanged counts).
  • A/B: with the reviewed OsComparison.cs restored, the 5 regressions fail (exit 12) and the 3 controls pass.
  • Fixture reports are byte-identical to revision 5. Evidence: documentation/S09-OpenStudio-InputReadBack-evidence/revision-6/.

Revision 5: S07 §5.1 lighting split (amendment L1, SAM#217 113873a0)

Checked first: SAM post-merge Test run 39667707673 and Build (Windows) run 39667707679 succeeded on 113873a0.

  • Writer (InternalCondition.cs, new LightingSplit.cs):
    • p = LightingRadiantProportion, the total radiant share. Fraction Radiant = p·(1 − s), Fraction Visible = p·s, with s = 0.2415 / 0.576 at full precision. Return Air Fraction is not set (0); convective is 1 − p.
    • LightingViewCoefficient is never read. When authored it is reported as SAM-OS-IC-001 information: a comfort coefficient, not an energy fraction.
    • An authored p, including 0, is preserved. SAM itself keeps p within [0, 1].
    • An absent or NaN p takes p₀ = 0.576 (Fraction Radiant 0.3345, Fraction Visible 0.2415). This replaces the silent 0.32 / 0.25.
    • New SAM-OS-IC-002 information at Summary, once per SpaceType. It states the values, s, the reference category and its limits; for p₀ it also says it is not a regulatory, NCM, TM59 or dwelling value and names OD-L1.
    • The occupancy and equipment view-coefficient reasons no longer claim a missing visible-fraction field.
  • Importer: p = Fraction Radiant + Fraction Visible, and the view coefficient is not set.
    • Warning when Return Air Fraction > 0 (it becomes convective).
    • Information when the short-wave share differs from s.
    • A sum above 1 is warned and not imported.
  • S09 read-back: five rows per space, from the raw SAM p and the §5.1 constants only:
    • Return Air (exact 0), F_R + F_V (p), F_V (p·s), F_R (p·(1 − s)) and the convective remainder (1 − p), at 0.001.
    • A blank field of a present Lights object reads as its IDD default 0.
    • An authored p is ACCEPTED-TRANSFORMATION (MATCH at 0).
    • An unstated p is S-UNSPEC on every row: DOCUMENTED-DEFAULT, BLOCKED owner, never PASS.
    • The view-coefficient comparison and its exception are removed.
  • Fixtures (S09 test runs at 20f8fb65; the acceptance scripts were not run, so the committed pack stays pre-L1 acc3):
Fixture Status Coverage Non-PASS
s08-fix-box (p 0.37) BLOCKED owner, exit 12 319/319 OS-MAT-SINGLE-SIDED (69) only
s08-fix-two (p 0.34, 0.41) BLOCKED owner, exit 12 592/592 OS-MAT-SINGLE-SIDED (138) only

The IDFs differ from the revision-4 acceptance only in the Lights Fraction Radiant / Visible lines, which equal the §5.1 vectors (revision-5/idf-diff-acc6.txt).

  • Negatives (FAIL on both fixtures, only lighting rows failing):
    • the previous mapping (F_R = p, F_V = 0.25);
    • F_V = 0 with p > 0;
    • F_R = p with F_V > 0;
    • Return Air 0.1;
    • F_R and F_V swapped.
  • Also tested:
    • An authored 0.
    • An authored view coefficient leaves every lighting row, coverage and count unchanged.
    • An absent or NaN p gives exactly five DOCUMENTED-DEFAULT rows.
  • Compatibility:
    • Every OpenStudio export's Lights fractions change. EnergyPlus lighting gains are redistributed; nothing was simulated.
    • An authored view coefficient no longer reaches EnergyPlus.
    • Import meaning changes. Earlier imported SAM files cannot be identified or migrated.
    • There is one more Summary information code.
    • Tas keeps its TBD value for an unstated p until the separate, OD-L1-gated SAM_Tas PR.
  • Unchanged: fixtures, weather, tolerances, shared core, ventilation, optics, the occupancy and equipment defaults, SAM_Tas and Ladybug.

Earlier revisions

Record correction after the review of 59036783 (documentation only)

  • Writer-test command fixed. The record's reproduction line for tests/SAM.Analytical.OpenStudio.Tests had no category filter, so it would select the suite's 25 [Category("Simulation")] cases (390 total, 365 without them). It now reads:
    dotnet test tests/SAM.Analytical.OpenStudio.Tests/SAM.Analytical.OpenStudio.Tests.csproj -c Release -p:Platform=x64 --filter 'TestCategory!=Simulation'
    The benchmark line already filtered; no other S09 reproduction command runs the writer suite.
  • Historical 388 passed / 2 skipped (kept, labelled): revision 3's retained machine-local summary (C:\TasOut\s09\rev3-ab\other-tests.txt, not committed) shows Total 390, i.e. no category filter, so the Simulation cases were in scope; it does not show whether EnergyPlus was invoked. Revision 2 kept no log; its scope is otherwise unverified. No simulation authorisation is recorded for these runs, and none is inferred. Revision 1's 363/2 matches the filtered count, so the earlier "superseded" note is withdrawn.
  • Reviewer-run results at 59036783 (not run by this session): S09 62/62; benchmark excluding Simulation 51/51; filtered writer suite 363 passed, 2 skipped.
  • Record only (documentation/S09-OpenStudio-InputReadBack-PR.md). No source, test, workflow, fixture, weather, tolerance or writer change; no local test rerun.

Revision 4 (after the failed final-head Build 39603021836 at e0a6ea19)

Build (Windows) run 39603021836 failed 1 of 58 S09 tests: EveryTargetedMutation_Fails("neg-people-density","s08-fix-box") hit a DirectoryNotFoundException loading compare-approved/report.json. Benchmark 51/51 and SPDX passed. Dependencies were sow/2026-Q4, SAM_Tas c50e9169. The failed run is kept, with its disposition, in the record and in documentation/S09-OpenStudio-InputReadBack-evidence/revision-4/.

  • What CI shows: compare returned before writing any report (exit 64 or 70 only). The test loaded report.json before checking the exit code, so the exit code and stderr were lost, and no artifact was kept. Which branch ran in CI is not known.
  • Defect found: process freshness was judged by process id alone (reader = export id, compare = reader id → exit 70). Windows reuses the ids of exited processes. A negative copy keeps the id of the one-time export, whose process exited long before, so a fresh reader can carry it.
    • Measured on this workstation: 141 of 1000 sequential harness launches got an id an earlier launch already had. Not measured on a GitHub runner.
  • Reproduced: a negative copy given its reader's id, where the reader started after the export finished.
    • Reviewed-head build: exit 70, the reader did not run in a fresh process after the export, no compare-approved (3/3). This is the CI signature.
    • Revision 4: FAIL, exit 10, with the unchanged report (3/3).
  • Cause: likely id reuse, but unproven. An exception inside compare is not excluded by CI evidence. A recurrence now uploads its diagnostics.
  • Fix (0c232875):
    • A process is its id and OS creation time. export.json s09-export/2 and native.json s09-native/2 record it.
    • A missing identity still fails (exit 70). The reader must still start after the export finished.
    • report.txt is unchanged; report.json Inputs gain the three creation times.
  • Diagnostics: TestSupport.ReadAndCompare checks that report.json exists before parsing it. A failed read/compare reports its exit code, labelled stdout/stderr, both process identities and times, and the run's files, and writes s09-diagnostics/*.txt. The Build workflow uploads those only on failure (SAM_OpenStudio-s09-diagnostics, 30 days).
  • Tests: 4 new.
    • A reused id is judged: FAIL, exit 10. This test fails against the reviewed head's check.
    • The same process, an unrecorded identity, and a read before the export finished stay exit 70 with no report.
  • Unchanged: hashes, fixtures, weather, tolerances, the shared core, the writer, the comparison, and every existing negative and assertion. No retry was added.
  • Observation (not changed): S08 SAM_Tas ReadBackCompare.cs:159 has the same id-only check. It was not assessed; a change there would be a separate SAM_Tas task.

Revision 3 (after the independent review of 6f7e6b7d)

The review found that OsComparison.Multiplicity skipped the OSM/IDF multiplier check when the IDF Zone or FenestrationSurface:Detailed had no OS:ThermalZone / OS:SubSurface of its name. With the object removed from model.osm (and the manifest's OSM hash updated), a fresh read and compare kept exit 12, 316/316, unchanged outcome counts, and no Consistency or Unsupported entry.

  • Counterpart identity (OsComparison.OsmCounterpart). Consistency is claimed only against exactly one OSM object of the IDF object's name, read under the file's own labels.
    • No such object, while every OSM object of that class has a readable Name: a proven missing counterpart, so Consistency → FAIL (exit 10).
    • Two objects of the name, or any unreadable Name: the identity is unproven → Unsupported → BLOCKED technical (exit 11).
  • OSM Multiplier (OsmMultiplier): read at its known position under its label. Blank is still the documented default 1. A missing or differently labelled field now blocks instead of defaulting to 1.
  • Local change only: no reader, report-schema, writer, shared-core, fixture, weather or tolerance change.
Same inputs, fresh processes (IDF unchanged) Reviewed head 6f7e6b7d Revision 3 c1e53328
s08-fix-box / s08-fix-two, unchanged BLOCKED owner, exit 12, 316/316 · 586/586 the same; identical outcome counts and report text
every OS:ThermalZone removed (box; two-zone) exit 12 ×2, complete coverage, no Consistency or Unsupported FAIL, exit 10 ×2
every OS:SubSurface removed (box; two-zone) exit 12 ×2, the same FAIL, exit 10 ×2
neg-osm-missing-subsurfaces (box), neg-osm-missing-thermal-zone (two-zone, zone A) exit 12 ×2 FAIL, exit 10 ×2

In each FAIL, coverage and outcome counts equal the unchanged fixture's. The only Consistency entries are multiplicity: IDF … '…' has no OS:… in the OSM …, and there are no Unsupported entries. Evidence: documentation/S09-OpenStudio-InputReadBack-evidence/revision-3/.

Assessment wording corrected (record only):

  • The Tas-backed U/g/LT claim is scoped to the four traced SAM_UI WPF workflows.
  • The values.Length >= 6 / values[6] guard is recorded as a code defect, separate from the reported Ug display issue. That issue's cause is not established.
  • World coordinates are described as the code handles them: supported, with absolute vertices; refused only with a non-zero north axis or zone relative north.
  • The hosted-CI wording is updated.

Revision 2 (after the independent review of d6d1cc5b)

The review showed three native mutations of the box run left the report unchanged (316/316, BLOCKED owner, exit 12): Zone Multiplier 1→2, window Multiplier blank→2, Building North Axis blank→90.

  • Multiplicity (OsComparison.Multiplicity).
    • The comparator reads Zone, ZoneGroup and FenestrationSurface:Detailed multipliers. A blank field is the documented EnergyPlus default 1, never assumed.
    • SAM has no multiplier, so any effective multiplier ≠ 1 FAILs.
    • The OSM multipliers must agree with the IDF (through one identified counterpart since revision 3).
  • Orientation (OsComparison.Orientation).
    • It applies the EnergyPlus 25.1 Relative/World rules for the Building North Axis and the Zone relative north, against SAM's NorthAngle (unstated = writer default 0).
    • A rotation SAM does not state FAILs. OS:Building must agree with the IDF.
  • Fail closed. These uninterpretable configurations go to a new Unsupported list: BLOCKED technical (exit 11), never PASS. Report schema /2.
    • an unreadable or non-integer multiplier;
    • an unknown coordinate system;
    • World with a non-zero north axis;
    • an OS:Space transform;
    • a stated SAM North Angle.
Same inputs, fresh processes Reviewed head d6d1cc5b Revision 2
s08-fix-box / s08-fix-two, unchanged BLOCKED owner, exit 12, 316/316 · 586/586 the same; identical outcome counts
reviewer's 3 copies (zone mult., window mult., north axis) exit 12 ×3 FAIL, exit 10 ×3
neg-zone-multiplier, neg-window-multiplier, neg-north-axis exit 12 ×3 FAIL, exit 10 ×3
the same with the OSM made consistent (-both) exit 12 ×3 FAIL, exit 10 ×3, from the SAM-intent check alone

Ventilation (writer fix, unchanged since revision 1)

  • V0/V1 (no Ventilation profile selected): the DSOA keeps its requirement rates. Its fraction schedule is all-zero, so no outdoor air is delivered. Before this PR the requirement was delivered unscheduled.
  • V2: the selected profile schedules the delivery.
  • A selection that does not resolve gives error SAM-OS-SCH-001 and no delivery. It is never unscheduled.
  • The per-space override follows the same rule. Ideal Loads states DCV None, NoEconomizer and no heat recovery, and links the effective DSOA.

Fixture verdicts before L1 (revisions 1–4, acceptance acc3/acc6; superseded by revision 5 above)

Fixture Status Coverage Remaining items then
s08-fix-box (V2) BLOCKED owner, exit 12 316/316 single-sided opaque optics (69 rows), lighting visible-fraction default (withdrawn by L1)
s08-fix-two (V1, V1) BLOCKED owner, exit 12 586/586 single-sided opaque optics (138 rows), lighting visible-fraction default (2; withdrawn by L1)
  • All 16 negatives FAILed in acceptance: the original 8, the 6 of revision 2 and the 2 of revision 3.
  • Both fixtures are unrotated, with every multiplier 1, as SAM states.
  • Each IDF zone and subsurface has its one OSM object.

OS-MAT-SINGLE-SIDED decision table (option 1 accepted 2026-10-22, the two reviewed fixtures only)

EnergyPlus 25.1 uses only the outermost and innermost layers' absorptances (.eio Opaque Construction Report).

  • Exposed faces: only the Plasterboard and Screed zone faces differ: ρs and ρv −0.06; ε −0.01 / +0.04. That is 18 box rows and 33 two-zone rows.
  • Buried-layer metadata: the other 51 / 105 rows, on faces EnergyPlus never uses.
  • Construction-aware material variants could represent every exposed face of both fixtures, since all are multi-layer. Under the current S07 3a scope the buried rows would remain.
  • Single-layer constructions stay constrained. Neither fixture has one.
  • Full table and options in the record. Option 1 is accepted for the two reviewed fixtures only, with effects unmeasured. Options 2 (writer variants) and 3 (3a scope) stay separate and unauthorised.

Construction-performance assessment (record only)

  • Tas-backed values: in the four traced SAM_UI WPF workflows, U, g and LT come from Tas through a TCD. These are the U-value window, the glazing window, the thermal edit services / construction alternatives, and the Glazing Builder. Other routes were not traced.
  • SAM.Analytical.Glazing has no U output and is used only by Grasshopper.
  • The S09 U is a shared-formula conversion check, not an EnergyPlus calculation.
  • No independent reference test exists anywhere.
  • Two code defects were found by reading:
    • the GetUValue short-array guard (Length >= 6, then values[6]);
    • swapped Nusselt tilt interpolation.
  • The reported Ug display issue has no established cause. The guard defect could produce it only if Tas returned exactly 6 values, and that is unproven.
  • The smallest later scope is an .eio-reading harness. It is unverified whether EnergyPlus writes the .eio report without a simulation, so that needs a separately authorised run.

Owner decisions (2026-10-22)

  1. Optics: option 1 (acceptance, revision 7) is superseded. The owner asked for the mismatch to be corrected, and revision 8 implements option 2 (construction-aware variants). The unused-face rows await an owner decision.
  2. T-VENT-INACTIVE extended to OpenStudio, with identical preconditions (ventilation A).
  3. OD-L1 reading confirmed (lighting B1); OD-L1 itself stays open. §5.1 and §8 gate only the SAM_Tas fallback on OD-L1, and list "absent p → p₀ with the diagnostic" as a required change for this PR under the approved D-LIGHT.
    • Revision 5 therefore applies p₀ to an unstated p in OpenStudio, where it replaces the silent 0.32 / 0.25 and no template value.
    • It states the category and limits and claims no suitability outside them.
    • If the owner reads OD-L1 as also gating this fallback, only the writer's unstated-p branch changes. The authored-input corrections stand alone.
    • No current fixture uses the fallback.

The lighting visible-fraction default of revisions 1–4 needs no decision: L1 withdrew it.

Also recorded:

  • S08 record discrepancy: the SAM_Tas S08 PR-C record lists three two-zone openings, but the signed-off fixture has one. This is record-only; SAM_Tas is unchanged.
  • Unchecked direction: an OSM zone or subsurface with no IDF object is not looked for.
  • Duplicate OS:Building: it makes compare exit 70 (fails closed, but not as a named BLOCKED technical entry). Not changed here.

Tests (local, Release x64)

  • Revision 12 at 8edf793 (record: revision-12/test-summaries.txt): S09 120/120; writer/importer --filter 'TestCategory!=Simulation' 471 passed, 2 skipped (real-SQL fixtures; local only, hosted CI does not run it); benchmark 51/51. Built Release x64, then dotnet test --no-build. No engine, simulation, acceptance or packing script.
  • Revision 11 final, tested content 4ea2e6c2ac5805426ed1b39bdd5d88b39b1c723f (harness os-input-readback.dll 4f746d9a…):
    • S09: 97/97. The fixture reports are byte-identical to revision 9.
    • Writer/importer, run with --filter 'TestCategory!=Simulation': 471 passed, 2 skipped (462 + 9 per-layer consistency cases).
    • Benchmark, run with --filter TestCategory!=Simulation: 51/51.
    • A/B: with f5f207b's names-only check restored, the 3 regressions fail (each imported with a changed thickness) and the 6 controls pass.
    • The review's consistency probe, run unchanged: the import is now unsuccessful with the named error.
    • Not run: acceptance and packing scripts, and any EnergyPlus or Tas execution.
  • Revision 11, tested content f5f207b456a3e7e2429ead4054c779031615e2b7 (harness os-input-readback.dll 6b4c510d…):
    • S09: 97/97. The fixture reports are byte-identical to revision 9.
    • Writer/importer, run with --filter 'TestCategory!=Simulation': 462 passed, 2 skipped (432 + 30 new).
    • Benchmark, run with --filter TestCategory!=Simulation: 51/51.
    • Superseded intermediate runs: at 5241bdd, one writer case failed. EveryDeclaredDiagnosticCode_Exists broke because I edited the coverage manifest, which the test reads live, during that run. It passes at f5f207b.
    • Not run: acceptance and packing scripts, and any EnergyPlus or Tas execution.
  • Revision 10, tested content 78f10146481dbd4fe46af8761813fa4f9d58d357 (harness os-input-readback.dll 3589bc9c…):
    • S09: 97/97. The fixture reports are byte-identical to revision 9.
    • Writer/importer, run with --filter 'TestCategory!=Simulation': 432 passed, 2 skipped (409 + 23 new).
    • Benchmark, run with --filter TestCategory!=Simulation: 51/51.
    • A/B: as described above. The review's probe, run unchanged against the revision-10 binaries, confirms both fixes. Its ROUNDTRIP lines are unchanged, because the layer-order defect is still open.
    • Not run: acceptance and packing scripts, and any EnergyPlus or Tas execution.
  • Revision 9, tested content 354e5322fa3a4c31a984de911c48534e768c40e4 (harness os-input-readback.dll 4edf422b…):
    • S09: 97/97. The fixture reports are byte-identical to revision 8.
    • Writer/importer, run with --filter 'TestCategory!=Simulation': 409 passed, 2 skipped.
    • Benchmark, run with --filter TestCategory!=Simulation: 51/51.
    • A/B: as described above.
    • Not run: acceptance and packing scripts, and any EnergyPlus or Tas execution.
  • Revision 8, tested content fc83a6df1f843f82a043353616a2b4e42a94e6d6 (harness os-input-readback.dll 44cb7177…):
    • S09 tests: 95/95.
    • Writer/importer, run with --filter 'TestCategory!=Simulation': 400 passed, 2 skipped. That is 21 new face-optics tests plus 3 test files adjusted.
    • Benchmark, run with --filter TestCategory!=Simulation: 51/51.
    • A/B: as described above.
    • Not run: acceptance and packing scripts, and any EnergyPlus or Tas execution.
  • Revision 7, tested content 614a7a8d70b6a70c5628a0ef3b57eaec0c5ec794 (harness os-input-readback.dll e41b13ee…):
    • S09 tests: 93/93. That is the 88 revision-6 tests (1 changed to require exit 14) plus 5 new.
    • A/B: as above, 2 failed and 5 passed.
    • Not run: the writer suite and benchmark tests (no writer, importer or benchmark file changed), the acceptance and packing scripts, and any engine.
  • Revision 6, tested content 225881f00971c7cab3596e94867924a11866e147 (harness os-input-readback.dll a05202d0…):
    • S09 tests: 88/88. That is the 80 revision-5 tests, unchanged, plus 8 new (5 regressions, 3 valid controls).
    • A/B: with the reviewed OsComparison.cs (31af6022) restored, exactly the 5 regressions fail.
    • Reviewer artifacts: the 5 artifacts, read and compared fresh, as in the table above.
    • Writer suite (--filter 'TestCategory!=Simulation'): 379 passed, 2 skipped. Revision 6 changes no writer or writer test.
    • Benchmark (not Simulation): 51/51.
    • Not run: acceptance and packing scripts, simulation, sizing, Tas or EnergyPlus execution.
  • Revision 5 (20f8fb65): S09 80/80; writer 379 passed / 2 skipped; benchmark 51/51; A/B as in the record.
  • Revision 4 (0c232875): S09 62/62; acceptance acc6; benchmark 51/51.
  • Hosted CI does not run the writer suite; those results are local only.

Final head and hosted checks

Head 39a6c90c24b31e82d26f44ed9fefaf0037d41726: the acceptance pack and record commit (documentation only) on top of d26665b; the tested code content is still 8edf793a63b274cafe5e55b9e85fa597d6307a10.

  • Build (Windows) run 39946621236: success (merge 3055755 of 39a6c90 into base 7f216657, unchanged). SAM, SAM_SQLite and SAM_Validation resolved to sow/2026-Q4, and SAM_Tas to c50e91698e931f98633ac2cb169417e4568da61d (sow/2026-Q4); no task branch shadows a dependency. Benchmark 51/51, S09 120/120, no failed test. Hosted CI does not run the writer/importer suite (471 passed / 2 skipped, local) or the acceptance scripts (local, above).
  • SPDX run 39946621363: success.

Previous head d26665bd2e397d011cd1dda382c3e93004e1c231 (revision 12 record; the acceptance run's head):

  • Build (Windows) run 39934532092: success (merge 0482be0 of d26665b into base 7f216657, unchanged). SAM, SAM_SQLite and SAM_Validation resolved to sow/2026-Q4, and SAM_Tas to c50e91698e931f98633ac2cb169417e4568da61d (sow/2026-Q4); no task branch shadows a dependency. Benchmark 51/51, S09 120/120, no failed test. Hosted CI does not run the writer/importer suite; its 471 passed / 2 skipped is local only.
  • SPDX run 39934532259: success.

Previous head adcad2f4383ab3763839e4a7e0a87c84d1468e4c (revision 11 final): the record and evidence commit after the tested content 4ea2e6c2.

  • Build (Windows) run 39838540508: success. SAM, SAM_SQLite and SAM_Validation resolved to sow/2026-Q4, and SAM_Tas to c50e91698e931f98633ac2cb169417e4568da61d (sow/2026-Q4). Benchmark 51/51, S09 97/97, no failed test. Hosted CI does not run the writer/importer suite; its 471 passed / 2 skipped is local only.
  • SPDX run 39838540363: success.

Previous head 89a2216da8acbde8abfd25430d0b0cf15f9f9730 (revision 11 as reviewed): the record and evidence commit after the tested content f5f207b4.

  • Build (Windows) run 39824715351: success. SAM, SAM_SQLite and SAM_Validation resolved to sow/2026-Q4, and SAM_Tas to c50e91698e931f98633ac2cb169417e4568da61d (sow/2026-Q4). Benchmark 51/51, S09 97/97, no failed test. Hosted CI does not run the writer/importer suite; its 462 passed / 2 skipped is local only.
  • SPDX run 39824715431: success.
  • Intermediate heads with code only (37841ea, 5241bdd, f5f207b) were never pushed alone; this push carries all four commits.

Previous head 93ec3151 (revision 10): the revision-10 record and evidence commits (e04ff905, then a whitespace-only fix) after the tested content 78f10146.

  • SPDX run 39766696573: success.
  • Build (Windows) run 39766696575: success. SAM, SAM_SQLite and SAM_Validation resolved to sow/2026-Q4, and SAM_Tas to c50e91698e931f98633ac2cb169417e4568da61d (sow/2026-Q4). Benchmark 51/51, S09 97/97, no failed test. Hosted CI does not run the writer/importer suite.
  • The intermediate head e04ff905 also ran Build 39766679143 and SPDX 39766679140 (success). It is superseded.

Previous head 806d5449 (revision 9):

  • Build (Windows) run 39761623252: success.
  • SPDX run 39761623127: success.

Previous head 901e592a (revision 8):

  • Build (Windows) run 39755063548: success.
  • SPDX run 39755063557: success.

Previous head 7fccdccb (revision 7):

  • Build (Windows) run 39744024838: success.
  • SPDX run 39744024866: success.

Previous head 8fd0ffc2 (revision 6, passed independent technical review), tree 17d328c9:

  • Build (Windows) run 39726520323: success.
    • Merge 07ec875 of 8fd0ffc into base 7f216657 (sow/2026-Q4, unchanged).
    • SAM, SAM_SQLite and SAM_Validation resolved to sow/2026-Q4; SAM_Tas to c50e91698e931f98633ac2cb169417e4568da61d (sow/2026-Q4).
    • Benchmark 51/51, S09 88/88. The diagnostics upload was skipped, as nothing failed.
  • SPDX run 39726520315: success.

Earlier head 31af6022 (revision 5): Build 39672548198 (S09 80/80, benchmark 51/51) and SPDX 39672548197 succeeded. Earlier heads are in the record.

🤖 Generated with Claude Code

michaldengusiak and others added 30 commits October 21, 2026 12:34
…file

S07 D-F2 and the S09 owner ventilation policy: the supply-air parameters are
a requirement; the selected Ventilation profile is the delivery.

- V0/V1 (no selection): the DSOA keeps the requirement rates and gets an
  all-zero fraction schedule (SAM_Schedule_VentilationNotDelivered), so no
  outdoor air is delivered. It was unscheduled (fraction 1) before.
- V2: the selected profile is the fraction schedule (unchanged).
- A selection that does not resolve to a Ventilation profile (missing or of
  another type) is an invalid input: an error, and no delivery (never
  unscheduled).
- The per-space OutsideSupplyAirFlow DSOA follows the same rule, and now
  states method Sum and every rate explicitly. Before, it had no schedule and
  relied on OpenStudio's Flow/Person defaults.
- Ideal Loads states DCV None, NoEconomizer and no heat recovery, and links
  the space's effective DSOA explicitly (its own, else the inherited
  SpaceType one).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
os-input-readback has three steps, each run in its own process:
- export: the production writer converts the frozen model and saves the OSM;
  the saved OSM is then forward-translated to IDF with the SDK.
- read: a text-only reader turns the OSM and IDF into native.json. It uses
  neither the SDK nor the writer.
- compare: S07 rows 1-10 against SAM intent, gated by the S08 core.

Reuse, not copies: the S08 evaluation core is a project reference and the S08
SAM-intent builder ReadBackExpected.cs is linked, both from the sibling
SAM_Tas checkout. The S08 signed-off fixtures are reused byte for byte, with
their hashes checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Negative mode: eight disposable mutations, covering identity, schedule,
  missing reference, value and OSM-only changes.
- Run-S09-ReadBack.ps1 checks the fixture and EPW hashes, then runs the
  acceptance and its negatives. Pack-S09-Evidence.ps1 copies the minimal,
  sanitized evidence into the repository.
- 33 offline tests: the reader and schedule units, the writer ventilation
  policy (V1, V2, V2 back to V1, missing and wrong-type selection, override,
  Ideal Loads controls, V0), and the end-to-end fresh-process read-back with
  every negative.
- CI: a sparse clone of SAM_Tas holding only the evaluation core and
  ReadBackExpected.cs, then the new tests run.
- The V1 note uses SAM-OS-HVAC-001 (information), as the other Ideal Loads
  notes do, so it is not counted as an unsupported object. The D1 deferral
  test now counts only the deferral messages.
- The mapping document describes the delivery rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vidence

- The report now names OS-MAT-SINGLE-SIDED once per fixture. The rows are
  listed as MAPPING-DIFFERENCE by S09, from the writer's SAM-OS-MAT-002, not by
  S07: accepting them is an owner decision.
- The evidence packer keeps only report.txt for the negatives.
- documentation/S09-OpenStudio-InputReadBack-evidence/: sanitized acceptance
  evidence (box, two, box-repeat; 8 negatives).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Independent review of d6d1cc5: a Zone Multiplier 1->2, a window Multiplier
blank->2 and a Building North Axis blank->90 left the box report unchanged
(316/316, BLOCKED owner, exit 12).

- OsComparison.Multiplicity: Zone, ZoneGroup and FenestrationSurface:Detailed
  multipliers read with their EnergyPlus default 1 for a blank field; any
  effective multiplier other than 1 contradicts SAM (no multiplier) and FAILs;
  the OSM OS:ThermalZone / OS:SubSurface multipliers must agree with the IDF.
- OsComparison.Orientation: GlobalGeometryRules, Building North Axis and Zone
  Direction of Relative North under the EnergyPlus 25.1 Relative/World rules,
  against SAM's NorthAngle (unstated = writer default 0); an unrotated model
  is the only supported configuration. A rotation SAM does not state FAILs;
  OS:Building must agree with the IDF.
- Anything the reader cannot interpret (unreadable or non-integer multiplier,
  unknown coordinate system, World with a non-zero north axis, an OS:Space
  transformation, a non-zero SAM North Angle) goes to a new Unsupported list:
  BLOCKED technical, never PASS. Report schema s09-readback-report/2.
- The reader keeps ZoneGroup, OS:SubSurface and the simple Window/Door/
  GlazedDoor (and :Interzone) classes; the latter are refused as extras.
- Six negatives (the reviewer's three IDF-only mutations and an OSM-consistent
  variant of each), wired into the acceptance script; 17 new tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fe097f

- Record: revision 2 (multiplicity and orientation against SAM intent, before
  and after on the same inputs), the OS-MAT-SINGLE-SIDED decision table for
  both fixtures (18/33 exposed-face rows vs 51/105 buried-layer metadata;
  still proposed and owner-unaccepted) and the construction-performance
  calculation assessment (no implementation).
- Evidence: acc2 acceptance and 14 negatives repacked; revision-2/ab (reviewed
  head vs revision on the same native inputs); revision-2/optics-table
  evidence program and its output.

Tested content: cfe097f.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ified OSM counterpart

OsComparison.Multiplicity skipped the OSM/IDF multiplier check when the IDF Zone or
FenestrationSurface:Detailed had no OS:ThermalZone / OS:SubSurface of its name, so a model
with the OSM object removed kept BLOCKED owner, exit 12, with complete coverage.

- OsmCounterpart: when every OSM object of the type has a readable Name and none matches, the
  counterpart is proven missing: Consistency (FAIL). A duplicate name or an unreadable Name
  leaves the identity unproven: Unsupported (BLOCKED technical).
- OsmMultiplier reads the field at its known position under the file's own label. Blank is
  still the documented default 1; a missing or differently labelled field blocks instead of
  defaulting to 1.
- The multiplicity Supporting line states how many IDF objects matched their OSM object.
- Negatives neg-osm-missing-thermal-zone and neg-osm-missing-subsurfaces (CLI and acceptance
  script); 8 tests: both cases on both fixtures, the fixture counterpart counts, and three
  BLOCKED-technical cases (duplicated zone, unlabelled zone Name, relabelled subsurface
  Multiplier).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>


- Record: the missing-OSM-counterpart finding (independent review of 6f7e6b7), the change, the
  before/after on both fixtures, the rules table, and the 16 negatives.
- Assessment wording: the Tas-backed U/g/LT claim scoped to the four traced SAM_UI WPF
  workflows; the GetUValue short-array guard recorded as a code defect separate from the
  reported Ug display issue, whose cause is not established; World coordinates described as
  the code handles them (supported, refused only with a non-zero north axis or zone relative
  north); hosted-CI wording updated (dependency resolution, which suites run, the 6f7e6b7 runs).
- Evidence: acc3 repacked (every existing report-*.txt unchanged; 2 new negatives) and the new
  revision-3/ A/B folder with its scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a failed CLI step

Build 39603021836 at e0a6ea1 failed one negative (neg-people-density, box):
compare wrote no report.json and the test loaded it before checking, so the
compare exit code and stderr were lost.

- Windows reuses the id of an exited process. A negative copy keeps the id
  of an export that exited long before, so a fresh reader can carry it, and a
  fresh comparison can carry the reader's. The freshness check compared ids
  only and refused such a read as infrastructure (exit 70, no report).
- export.json (s09-export/2) and native.json (s09-native/2) now record the
  OS creation time of their process. The same process is the same id and
  creation time; a missing identity is still an infrastructure failure, and
  the reader must still start after the export finished.
- TestSupport.ReadAndCompare asserts report.json exists before parsing it.
  A failed read or compare reports the exit code, labelled stdout/stderr,
  both process identities and times, and the run's files, and writes them
  to s09-diagnostics; CI uploads those text files when a step fails.
- 4 tests: a reused id is judged (FAIL, exit 10); the same process, an
  unrecorded identity and a read before the export finished stay exit 70.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ailure at 0c23287

The failed final-head run at e0a6ea1 is preserved with its disposition:
compare exited before writing a report and the test lost its output. The
id-only freshness check refuses a fresh process whose Windows process id was
reused; reproduced locally (reviewed head: exit 70, no report; revision 4:
FAIL with the unchanged report). That this caused the CI run is likely but
unproven. acc6 reports and IDFs are byte-identical to acc3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…historical 388/2 from the reviewer's filtered 363/2

The writer-suite reproduction command had no category filter and would select
the 25 Simulation-category cases. It now passes --filter 'TestCategory!=Simulation'.
The historical 388 passed / 2 skipped results (Total 390) are labelled as an
unfiltered scope; the retained revision-3 summary is machine-local and the
revision-2 scope is unverified. No simulation authorisation is inferred.
Reviewer-run results at 5903678 are recorded as reviewer-attributed.
Record only; no source, test, workflow or fixture change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-SPLIT)

SAM LightingRadiantProportion p is the total radiant share of the lighting
gain (SAM#217, 113873a0). The writer now sets Fraction Radiant = p(1-s),
Fraction Visible = p*s with s = 0.2415/0.576 at full precision, leaves
Return Air Fraction unset (0) and never reads LightingViewCoefficient, a
comfort coefficient. Authored p, including 0, is preserved; an absent or
NaN p takes the common default p0 = 0.576. Both cases raise the new
SAM-OS-IC-002 information at Summary, stating the values, s, the reference
category and its limits, and for p0 the open owner decision OD-L1. An
authored view coefficient is reported with SAM-OS-IC-001 information; the
occupancy/equipment view-coefficient reasons no longer claim a missing
visible-fraction field.

The importer recovers p = Fraction Radiant + Fraction Visible without
setting the view coefficient, warns when Return Air Fraction > 0 or the
fractions sum above 1, and informs when the short-wave share differs
from s.

S09 read-back: rows for Return Air Fraction (exact), F_R + F_V, F_V, F_R
and the convective remainder, expected from the raw SAM p and the S07
constants only; an absent/NaN p stays S-UNSPEC (DOCUMENTED-DEFAULT, never
PASS). The view-coefficient comparison and its exception are removed.
Five negative cases, authored-zero, comfort-coefficient-independence and
unstated-p tests; writer and importer regressions; coverage manifests and
mapping documents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…plit at 20f8fb6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fore any tolerance

Independent review of 31af602 reproduced two gaps in OsComparison.LightingSplit.

1. Native domain and energy conservation are now checked before the S07
   comparison: Return Air, Radiant and Visible each in [0, 1] and their sum
   at most 1 (S07 §5.1: a sum above 1 is an input error). A violation is a
   named EXPORT-DEFECT on all five lighting rows, with no tolerance applied.
   The floating-point boundary 1e-12 (OsQuantities.LightingFractionBoundary)
   absorbs only IDF decimal-to-double rounding; no S07 tolerance changes.
   The reviewed head accepted p = 1 with sum 1.0008 and p = 0 with
   Fraction Radiant -0.0004 (exit 12); both now FAIL (exit 10).

2. The Lights "Return Air Fraction Calculated from Plenum Temperature"
   setting is read. Blank, absent or No (the EnergyPlus default) keep the
   static split. Yes cannot be a verified static T-LIGHT-SPLIT; Yes, an
   uninterpretable value or an unreadable field make every lighting row
   UNVERIFIED and name the space in Unsupported: BLOCKED technical (exit 11).

CLI cases neg-lighting-sum-above-one, neg-lighting-radiant-to-visible,
neg-lighting-plenum-yes/-unknown/-relabelled and the control
ctl-lighting-plenum-no; 8 S09 tests (p = 1 and p = 0 controls).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…guards at 225881f

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed fixtures (exit 14)

The owner accepted OS-MAT-SINGLE-SIDED (optics option 1) on 2026-10-22 for the two
reviewed fixtures only, with result effects unmeasured. OwnerDecisions supplies one
3a MAPPING-DIFFERENCE acceptance to the S08 core only under the approved rules and
when the fixture id and the verified frozen-model SHA-256 are a reviewed fixture's;
anything else stays BLOCKED owner, exit 12. Failures, blocks and the OD-L1
documented default are unchanged. 5 S09 tests; 93/93 locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…14a7a8

Records the owner decisions of 2026-10-22 (optics option 1 for the two reviewed
fixtures only, result effects unmeasured; ventilation A; lighting B1 with OD-L1
open), revision 6's completed technical review, the exit-14 fixture reports,
the 93/93 S09 run and the A/B. States that an accepted limitation is not a
Phase B PASS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ision 8)

EnergyPlus opaque materials are single-sided and use only the outermost
outside face and the innermost zone face. SAM's first layer now carries its
internal-face optics (an _Internal variant where faces differ) in both
construction directions; every other layer keeps the external face, so
interzone constructions stay exact mirrors.

- Single-layer outdoor or interzone constructions with differing faces:
  error SAM-OS-MAT-003, no construction, unsuccessful conversion; ground and
  adiabatic use the zone face; checked per use before cache reuse.
- The forward construction follows the panel's first space; a missing first
  side is a named error, never a swap.
- Every opaque object stamps its source Guid, face and authored face optics;
  the importer restores both faces from them and warns on inconsistent
  identity or values, never inventing or merging by name.
- S09 derives the used faces from SAM and orientation alone; a used face is
  never listed; unused faces are listed only when native is the other face.
- Revision 7's optics acceptance (OwnerDecisions) is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…correction at fc83a6d

Records the owner's revision-8 conditions, the verified trace, the face rule,
the single-layer error, orientation and import identity, removal of revision
7's acceptance (history kept), fixture results (BLOCKED owner, exit 12, only
unused-face rows), S09 95/95, writer/importer 400/2 skipped and benchmark
51/51 (Simulation excluded), both A/Bs and the remaining limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…port inherited faces (revision 9)

Independent review of 901e592:
- Every opaque object records what was written (SAM.Written.*), so an import
  tells an unchanged default from an edit. An edit is kept on the face the
  object carries and reported (SAM-OSI-MAT-001); an unchanged default stays
  unstated. The reported re-export case now keeps 0.42 instead of 0.7.
- SAM-written face and thickness objects of one explicit source Guid merge
  into one SAM material with its Guid restored and each construction layer's
  thickness kept; thermal, authored or per-face conflicts are reported
  (SAM-OSI-ID-001) and not merged; never merged by name.
- An unstated (absent or NaN) internal value on a zone face that takes the
  external value is reported at Summary (new SAM-OS-MAT-004); its provenance
  stays unspecified on import and in S09 (NaN now reads as unstated).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…354e532

Records native-edit preservation, merging by source identity, SAM-OS-MAT-004, the regressions, controls and A/B, S09 97/97, writer/importer 409/2 skipped, benchmark 51/51 (Simulation excluded), and the pre-existing import layer-order defect found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…red equal-face edits on the faces served (revision 10)

Review of 354e532 (two P1 importer defects):

- An opaque object carrying a group's SAM.MaterialGuid is validated on its own
  before any merged material is returned for it, and must be a recorded member
  of that group. A malformed or identity-inconsistent object is imported as its
  own material with its own thermal properties and a named diagnostic; it is
  never aliased to a valid group. Order-independent.
- A native edit is kept on every SAM face the object supplies. Internal variants
  supply the zone face; external objects of divergent materials the external
  face; external objects of equal-face materials take their faces from the
  constructions that use them (forward SAM construction last = zone face, first =
  outer face; unstamped mirrors skipped; other exposed uses supply both). A
  single-layer equal-face edit therefore stays equal and re-exports valid. The
  writer and the single-layer differing-face refusal are unchanged.

Regressions for both reproductions with import-order, malformed/conflicting
identity, single-layer, interzone, multilayer, divergent and unchanged controls.
The pre-existing multilayer layer-order defect is unchanged and still recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the review of 806d544 at 78f1014

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the writer's first space on import (revision 11)

Owner-approved opaque-only correction of the pre-existing multi-layer import
order (review of 806d544):

- An opaque layered OS:Construction used by planar surfaces is read innermost
  layer first, so SAM's first layer faces the surface's zone. Fenestration
  (glazing, gas, SAM door panes), subsurface-only (unstamped door panes),
  shading-only, unused, air-boundary and non-layered constructions keep
  OpenStudio's order. A construction shared by surfaces and a subsurface or
  shade is split (surfaces innermost first with the SAM Guid; other uses in
  OpenStudio order with a new Guid), reported as SAM-OSI-APX-001.
- An interzone panel is built from the side carrying the SAM-written forward
  construction, also for a geometric-fallback pair, and the spaces are added in
  a stable topological order that puts that side first: the writer reads a
  panel's first space from the cluster's space order. An unstamped panel whose
  building side is then second is re-read from its partner (APX-001).

19 round-trip tests compare every surface's layers, thicknesses, thermal
properties and used-face absorptances (export, import, export): the reviewer's
multi-layer reproduction with the edited 0.42 zone face, interzone in both
space orders, geometric fallback, unstamped native and mixed models, and
glazing, door-pane, shading, air-boundary, non-layered and single-layer
controls. Mapping and to-SAM coverage docs record the compatibility impact;
previously imported models are not migrated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… deterministic (revision 11)

The re-read of an unstamped interzone panel whose building side is second in
the space order keeps its layers, but a face-stamped layer material whose
authored faces differ takes the new first space's face role; the
SAM-OSI-APX-001 diagnostic is now a warning that says so (mapping doc
updated). The mixed-model test places the stamped half's forward construction
on the space OpenStudio visits second, so the re-read is reached on every run
(both authored space orders), with equal-face materials.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ep its used faces (revision 11)

Owner instruction: replace the warning-only re-read of an interzone panel.

- Every imported opaque surface panel is checked against managed snapshots of
  its native surfaces: the first space's surface must hold the SAM layers
  innermost first and a second side their mirror, and the absorptances the
  writer would put on each face EnergyPlus uses (both zone faces of an
  interzone panel; the zone and outdoor faces of an outdoor surface) must
  equal the native ones. A mismatch is the new error SAM-OSI-CON-002, naming
  the panel, construction, face and values, and the import returns no model.
- A panel whose building side ends up second is re-read from its partner,
  including a SAM-written panel lost to contradictory forward sides; the
  check decides. The re-read and contradiction diagnostics say so.
- Tests: deterministic mixed models with differing faces (named error,
  unsuccessful), an equivalent re-read with differing faces and equal-face
  cases (success, every surface reproduced), contradictory forward sides
  (error with differing faces, success with equal faces), and an edited
  single-sided wall (error; equal-face control succeeds). CON-002 is pinned
  in the released-code table, the to-SAM coverage manifest (145 entries) and
  the audit doc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r and the fail-closed face check at f5f207b

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s on both interzone sides (revision 11)

Review of 89a2216: VerifySurfaceFaces compared merged SAM material names only, so a
different-thickness object of the same source material on one side of an interzone
wall (12.5 mm on one side, a 20 mm copy on the other) imported successfully and
re-exported 12.5 mm on both sides.

The native snapshot now records each StandardOpaqueMaterial layer's thickness,
conductivity, density and specific heat; both sides are compared in their own
direction with the SAM panel's layer thicknesses and materials. A mismatch is
SAM-OSI-CON-002 and no model. Massless and air-gap layers keep their documented
nominal-thickness import (matched by material only).

Tests: the reviewer's reproduction on either side and a repeated material with
swapped thicknesses fail named; a repeated material at two thicknesses, one material
at different thicknesses in unrelated constructions (box and interzone), an
interzone cavity wall and a massless interzone layer import as before. The mapping
doc's fail-closed text replaces the superseded warning-only wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ea2e6c; revision 10 passed review

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@michaldengusiak
michaldengusiak marked this pull request as ready for review October 23, 2026 09:09
michaldengusiak and others added 3 commits October 23, 2026 11:47
… OUT-OF-ENGINE-SCOPE (revision 12)

Row-3a absorptance and emissivity on faces EnergyPlus 25.1 does not use are reported as
OUT-OF-ENGINE-SCOPE (informational, not gated), only when the side's face role is established
independently of the writer (raw SAM spaces, type and flags; the native boundary; the interzone
partner and its mirrored construction) and both values are valid and comparable. Used faces,
thermal layer properties and invalid, unreadable or undeterminable rows stay gated; an
undeterminable role is named as unsupported. The S09-only OS-MAT-SINGLE-SIDED listing is
removed. Report schema s09-readback-report/3 keeps both values, the difference, side, layer,
reason, amendment and counts, apart from the gated non-PASS items.

Both fixtures: PASS (scoped native-input conformance), coverage 319/319 and 592/592.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8edf793; revision 11 per-layer fix accepted

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…SS, 21/21 negatives FAIL [acceptance-2026-10-23]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@michaldengusiak
michaldengusiak merged commit 81acb8e into sow/2026-Q4 Oct 23, 2026
2 checks passed
@michaldengusiak
michaldengusiak deleted the feature/s09-openstudio-input-readback branch October 23, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant