Skip to content

Add composite ("mixed-use") building type representation - #39

Open
nllong wants to merge 3 commits into
mainfrom
feat/ensemble-building-types
Open

Add composite ("mixed-use") building type representation#39
nllong wants to merge 3 commits into
mainfrom
feat/ensemble-building-types

Conversation

@nllong

@nllong nllong commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

Real buildings are often not well represented by a single BuildStock building type -- e.g. a building that is 70% office space over 30% ground-floor retail. This adds CompositeBuildingType, which models such a building as a fraction-weighted combination of two or more (product, building_type) components (fractions must sum to 1.0), then downloads/combines their real time series into one synthetic composite time series.

Composites can mix ComStock (commercial) and ResStock (residential) components in the same combination -- e.g. ground-floor retail under apartments -- since both products publish the same 15-minute time series layout for the currently supported releases (column-naming/unit-suffix differences are normalized automatically before combining).

What's included

  • src/buildstock_processor/composite.py:
    • CompositeComponent / CompositeBuildingType -- construction, validation, .normalized() to fix up percentages that don't sum perfectly due to rounding
    • combine_composite_time_series() -- linearly blends already-downloaded component time series DataFrames (composite[column][t] = sum(component.fraction * component_series[column][t] for component in composite)), normalizing ComStock/ResStock's differing unit-suffix column conventions, and gracefully narrowing to columns shared by every component (ComStock and ResStock don't publish identical end-use categories -- e.g. ComStock has district cooling/heating outputs ResStock's residential schema doesn't)
    • pull_composite_time_series() -- end-to-end: downloads one representative building's time series per component via process_building_time_series(), then combines them
    • normalize_time_series_columns() -- exposed publicly since callers inspecting raw component series need it too
  • 03_composite_building_example.ipynb -- executed end-to-end against real data (outputs stripped per repo convention), demonstrating:
    1. Defining/validating/normalizing an composite
    2. A same-product example (70% MediumOffice / 30% RetailStripmall) with load-shape and annual-total plots
    3. A cross-product example (55% ComStock retail / 45% ResStock multifamily) modeling ground-floor retail under apartments
  • tests/test_composite.py -- 23 tests (22 unit, 1 real-network integration test following the existing @pytest.mark.integration convention)
  • README / docs/usage.md updates, pyproject.toml notebook packaging, .gitignore entry for the new datasets/composite cache dir

Testing

  • uv run pytest -- all tests pass (real-network integration test included)
  • uv run mypy -- clean
  • uv run pre-commit run --all-files -- clean
  • Manually executed the new notebook top-to-bottom via jupyter nbconvert --execute against real OEDI data to confirm all cells/plots run without errors

🤖 Generated with Copilot CLI

@nllong nllong changed the title Add ensemble ("mixed-use") building type representation Add composite ("mixed-use") building type representation Jul 28, 2026
Nicholas Long and others added 2 commits July 28, 2026 07:48
Real buildings are often not well represented by a single BuildStock building
type -- e.g. a building that is 70% office space over 30% ground-floor retail.
EnsembleBuildingType models this as a fraction-weighted combination of two or
more (product, building_type) components, including mixing ComStock
(commercial) and ResStock (residential) components in the same ensemble (e.g.
ground-floor retail under apartments).

- ensemble.py: EnsembleComponent/EnsembleBuildingType (with validation and
  .normalized() for imperfect percentage sums), combine_ensemble_time_series()
  (linearly blends component time series, normalizing ComStock/ResStock's
  differing unit-suffix column conventions, gracefully dropping columns not
  shared by every component), and pull_ensemble_time_series() (end-to-end
  download + combine)
- 03_ensemble_building_example.ipynb: executed end-to-end against real data,
  demonstrating a same-product example and a cross-product (ComStock+ResStock)
  mixed-use example
- 23 new tests (unit + 1 real-network integration test)
- README/docs/usage.md updates, pyproject.toml notebook packaging

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Renames EnsembleBuildingType/EnsembleComponent -> CompositeBuildingType/
CompositeComponent, ensemble.py -> composite.py, and combine/pull_ensemble_
time_series -> combine/pull_composite_time_series throughout the module,
tests, notebook, README, and docs. Purely a naming change; no behavior
changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nllong
nllong force-pushed the feat/ensemble-building-types branch from 669afeb to ff92136 Compare July 28, 2026 13:50
…s/prose (#40)

Renames organizational-name references from "NREL" to "NLR" (National
Laboratory of the Rockies) throughout module docstrings, comments, README.md,
and the ENERGY STAR crosswalk docs, following the org's rebrand.

Literal, hardcoded S3 bucket/path segments (e.g. the "nrel-pds-building-stock"
prefix in real OEDI data lake URLs) are intentionally left unchanged, since
those are real AWS paths, not branding text -- renaming them would break every
metadata/time-series download. Test assertions checking those exact URLs are
also left unchanged. Added "NLR" to cspell.json's allowlist (kept "NREL" too,
since it still appears in the literal bucket paths).

Co-authored-by: Nicholas Long <nicholas.long@nrel.gov>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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