comcheck-api 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- comcheck_api/DISCLAIMER.md +24 -0
- comcheck_api/__init__.py +99 -0
- comcheck_api/ai/__init__.py +30 -0
- comcheck_api/ai/skill/SKILL.md +285 -0
- comcheck_api/ai/skill/__init__.py +5 -0
- comcheck_api/ai/skill/reference/operations.md +101 -0
- comcheck_api/ai/skill/reference/simulation.md +99 -0
- comcheck_api/ai/skill/reference/types.md +90 -0
- comcheck_api/ai/skill/scripts/__init__.py +1 -0
- comcheck_api/ai/skill/scripts/validate_code.py +210 -0
- comcheck_api/api/__init__.py +1 -0
- comcheck_api/api/api_services.py +273 -0
- comcheck_api/cli.py +136 -0
- comcheck_api/client/__init__.py +1 -0
- comcheck_api/client/comcheck_client.py +335 -0
- comcheck_api/constants/__init__.py +0 -0
- comcheck_api/constants/building_area_constants.py +35 -0
- comcheck_api/constants/common_constants.py +116 -0
- comcheck_api/constants/envelope_constants.py +250 -0
- comcheck_api/defaults.py +150 -0
- comcheck_api/exceptions.py +54 -0
- comcheck_api/introspection.py +188 -0
- comcheck_api/managers/__init__.py +0 -0
- comcheck_api/managers/components/__init__.py +0 -0
- comcheck_api/managers/components/building_area.py +11 -0
- comcheck_api/managers/components/envelope/__init__.py +0 -0
- comcheck_api/managers/components/envelope/ag_wall.py +97 -0
- comcheck_api/managers/components/envelope/bg_wall.py +39 -0
- comcheck_api/managers/components/envelope/door.py +11 -0
- comcheck_api/managers/components/envelope/floor.py +11 -0
- comcheck_api/managers/components/envelope/roof.py +30 -0
- comcheck_api/managers/components/envelope/skylight.py +11 -0
- comcheck_api/managers/components/envelope/window.py +11 -0
- comcheck_api/managers/data_manager.py +369 -0
- comcheck_api/project_operations/__init__.py +8 -0
- comcheck_api/project_operations/project_building_area_operations.py +107 -0
- comcheck_api/project_operations/project_envelope_operations.py +899 -0
- comcheck_api/schemas/comCheck.schema.json +6463 -0
- comcheck_api/types/__init__.py +49 -0
- comcheck_api/types/api_types.py +127 -0
- comcheck_api/types/common_types.py +32 -0
- comcheck_api/types/core_types.py +4198 -0
- comcheck_api/types/custom_base_model.py +314 -0
- comcheck_api/utilities/__init__.py +5 -0
- comcheck_api/utilities/common.py +50 -0
- comcheck_api/utilities/envelope_utilities.py +46 -0
- comcheck_api/utilities/id_registry.py +79 -0
- comcheck_api/utilities/project_utilities.py +60 -0
- comcheck_api/validation.py +64 -0
- comcheck_api-1.0.0.dist-info/METADATA +244 -0
- comcheck_api-1.0.0.dist-info/RECORD +54 -0
- comcheck_api-1.0.0.dist-info/WHEEL +4 -0
- comcheck_api-1.0.0.dist-info/entry_points.txt +2 -0
- comcheck_api-1.0.0.dist-info/licenses/LICENSE +24 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
DISCLAIMER
|
|
2
|
+
|
|
3
|
+
This material was prepared as an account of work sponsored by an agency of the
|
|
4
|
+
United States Government. Neither the United States Government nor the United
|
|
5
|
+
States Department of Energy, nor Battelle, nor any of their employees, nor any
|
|
6
|
+
jurisdiction or organization that has cooperated in the development of these
|
|
7
|
+
materials, makes any warranty, express or implied, or assumes any legal
|
|
8
|
+
liability or responsibility for the accuracy, completeness, or usefulness or
|
|
9
|
+
any information, apparatus, product, software, or process disclosed, or
|
|
10
|
+
represents that its use would not infringe privately owned rights.
|
|
11
|
+
|
|
12
|
+
Reference herein to any specific commercial product, process, or service by
|
|
13
|
+
trade name, trademark, manufacturer, or otherwise does not necessarily
|
|
14
|
+
constitute or imply its endorsement, recommendation, or favoring by the United
|
|
15
|
+
States Government or any agency thereof, or Battelle Memorial Institute. The
|
|
16
|
+
views and opinions of authors expressed herein do not necessarily state or
|
|
17
|
+
reflect those of the United States Government or any agency thereof.
|
|
18
|
+
|
|
19
|
+
PACIFIC NORTHWEST NATIONAL LABORATORY
|
|
20
|
+
operated by
|
|
21
|
+
BATTELLE
|
|
22
|
+
for the
|
|
23
|
+
UNITED STATES DEPARTMENT OF ENERGY
|
|
24
|
+
under Contract DE-AC05-76RL01830
|
comcheck_api/__init__.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""COMcheckWeb API - Python client for programmatic building energy code compliance.
|
|
2
|
+
|
|
3
|
+
This package provides a type-safe, automated interface to the U.S. Department of
|
|
4
|
+
Energy's COMcheck Web API for building energy code compliance verification.
|
|
5
|
+
|
|
6
|
+
Quick Start:
|
|
7
|
+
>>> from comcheck_api import COMcheckClient
|
|
8
|
+
>>> client = COMcheckClient(api_key="your-api-key")
|
|
9
|
+
>>> project = client.get_project("123")
|
|
10
|
+
>>> session_id = client.start_run_simulation(project)
|
|
11
|
+
|
|
12
|
+
Basic Usage:
|
|
13
|
+
from comcheck_api import (
|
|
14
|
+
COMcheckClient,
|
|
15
|
+
COMCheckHTTPError,
|
|
16
|
+
export_to_json,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
client = COMcheckClient(api_key="your-key")
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
projects = client.list_projects()
|
|
23
|
+
export_to_json(projects, "projects.json")
|
|
24
|
+
except COMCheckHTTPError as e:
|
|
25
|
+
print(f"HTTP error: {e.status_code}")
|
|
26
|
+
|
|
27
|
+
Advanced Usage:
|
|
28
|
+
from comcheck_api import (
|
|
29
|
+
COMcheckClient,
|
|
30
|
+
project_envelope_operations,
|
|
31
|
+
)
|
|
32
|
+
from comcheck_api.types import AgWall, ComBuilding
|
|
33
|
+
|
|
34
|
+
# For type imports, use the types submodule
|
|
35
|
+
# from comcheck_api.types import ...
|
|
36
|
+
|
|
37
|
+
# For advanced utilities, use the utilities submodule
|
|
38
|
+
# from comcheck_api.utilities import ...
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Client
|
|
42
|
+
from .client import COMcheckClient
|
|
43
|
+
|
|
44
|
+
# Exceptions
|
|
45
|
+
from .exceptions import (
|
|
46
|
+
COMCheckAPIError,
|
|
47
|
+
COMCheckHTTPError,
|
|
48
|
+
COMCheckConnectionError,
|
|
49
|
+
COMCheckValidationError,
|
|
50
|
+
COMCheckSimulationError,
|
|
51
|
+
COMCheckProjectNotFoundError,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Project Defaults
|
|
55
|
+
from . import defaults
|
|
56
|
+
|
|
57
|
+
# Project Operations
|
|
58
|
+
from .project_operations import (
|
|
59
|
+
project_building_area_operations,
|
|
60
|
+
project_envelope_operations,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Introspection helpers
|
|
64
|
+
from .introspection import list_operations, lookup_type
|
|
65
|
+
|
|
66
|
+
# Validation helpers
|
|
67
|
+
from .validation import validate_project
|
|
68
|
+
|
|
69
|
+
# Utilities
|
|
70
|
+
from . import utilities
|
|
71
|
+
|
|
72
|
+
# Types
|
|
73
|
+
from . import types
|
|
74
|
+
|
|
75
|
+
__all__ = [
|
|
76
|
+
# Client
|
|
77
|
+
"COMcheckClient",
|
|
78
|
+
# Exceptions
|
|
79
|
+
"COMCheckAPIError",
|
|
80
|
+
"COMCheckHTTPError",
|
|
81
|
+
"COMCheckConnectionError",
|
|
82
|
+
"COMCheckValidationError",
|
|
83
|
+
"COMCheckSimulationError",
|
|
84
|
+
"COMCheckProjectNotFoundError",
|
|
85
|
+
# Project Defaults
|
|
86
|
+
"defaults",
|
|
87
|
+
# Project Operations
|
|
88
|
+
"project_building_area_operations",
|
|
89
|
+
"project_envelope_operations",
|
|
90
|
+
# Introspection
|
|
91
|
+
"list_operations",
|
|
92
|
+
"lookup_type",
|
|
93
|
+
# Validation
|
|
94
|
+
"validate_project",
|
|
95
|
+
# Utilities
|
|
96
|
+
"utilities",
|
|
97
|
+
# Types
|
|
98
|
+
"types",
|
|
99
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""AI-facing surface for the comcheck_api package.
|
|
2
|
+
|
|
3
|
+
Ships the canonical Skill folder under ``comcheck_api/ai/skill/``
|
|
4
|
+
(SKILL.md + reference docs + examples + scripts). Use
|
|
5
|
+
:func:`skill_root` to get the on-disk path to the bundled folder —
|
|
6
|
+
useful for installing it into ``<project>/.claude/skills/`` or
|
|
7
|
+
``~/.claude/skills/``.
|
|
8
|
+
|
|
9
|
+
Introspection and validation helpers live on the SDK itself
|
|
10
|
+
(``comcheck_api.list_operations``, ``comcheck_api.lookup_type``,
|
|
11
|
+
``comcheck_api.validate_project``).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from importlib.resources import as_file, files
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def skill_root() -> Path:
|
|
21
|
+
"""Return the on-disk path to the bundled Skill folder.
|
|
22
|
+
|
|
23
|
+
Uses ``importlib.resources`` so the path resolves correctly
|
|
24
|
+
whether the package is installed normally or zip-imported.
|
|
25
|
+
"""
|
|
26
|
+
with as_file(files("comcheck_api.ai.skill")) as p:
|
|
27
|
+
return Path(p)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = ["skill_root"]
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: comcheck-api
|
|
3
|
+
description: Use this skill when the user is writing Python code that uses
|
|
4
|
+
the comcheck_api package to build COMcheck project JSON, run compliance
|
|
5
|
+
simulations against the PNNL COMcheck Web API, or work with envelope
|
|
6
|
+
or building-area operations. Triggers on imports of `comcheck_api`,
|
|
7
|
+
mentions of COMcheck/ASHRAE 90.1/IECC compliance, or requests to
|
|
8
|
+
validate building energy code compliance.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# COMcheck API Python Client Skill
|
|
12
|
+
|
|
13
|
+
## When to use this skill
|
|
14
|
+
|
|
15
|
+
Triggers:
|
|
16
|
+
- The user imports `comcheck_api` or asks for help with it.
|
|
17
|
+
- The user mentions COMcheck, ASHRAE 90.1, IECC, or commercial building
|
|
18
|
+
energy code compliance.
|
|
19
|
+
- The user wants to build, update, or run a simulation on a COMcheck
|
|
20
|
+
project from Python.
|
|
21
|
+
|
|
22
|
+
## Core concepts
|
|
23
|
+
|
|
24
|
+
- **Single entry point**: `COMcheckClient` is the only client class
|
|
25
|
+
users instantiate. Construct with `api_key=...`. The client does
|
|
26
|
+
**not** auto-read any environment variable — read it yourself and
|
|
27
|
+
pass it: `COMcheckClient(api_key=os.environ["COM_API_KEY"])`.
|
|
28
|
+
- **Project shape**: a project is a `ComBuilding` Pydantic model with
|
|
29
|
+
lowercase top-level fields: `project` (metadata), `location`,
|
|
30
|
+
`envelope`, `lighting` (which contains `wholeBldgUse[]` — the
|
|
31
|
+
building areas), `hvac`, `renewable`, and `control` (energy code).
|
|
32
|
+
No `Project`/`Control` PascalCase aliases exist.
|
|
33
|
+
The fields `hvac`, `renewable`, and the **interior-lighting fixtures
|
|
34
|
+
inside `activityUse[]`**, plus exterior lighting (`exteriorUse[]`)
|
|
35
|
+
and the shared `fixtureSchedule[]`, exist on the model but have
|
|
36
|
+
**no operation functions** — leave them at template defaults. Only
|
|
37
|
+
`lighting.wholeBldgUse[]` (building areas, including each area's
|
|
38
|
+
own `interiorLightingSpace` singleton) is mutable, via
|
|
39
|
+
`project_building_area_operations`.
|
|
40
|
+
- **Operation modules (functional)**: building areas and envelope
|
|
41
|
+
components are added/updated/removed via free functions in
|
|
42
|
+
`project_building_area_operations` and `project_envelope_operations`.
|
|
43
|
+
Each function takes a `ComBuilding` and returns a new `ComBuilding`.
|
|
44
|
+
- **Envelope items attach to a building-area key**: every
|
|
45
|
+
`add_*_to_project` envelope function takes
|
|
46
|
+
`(project, building_area_key, new_component)`. Look up the key
|
|
47
|
+
with `ba_ops.get_building_area_keys_from_project(project)` first.
|
|
48
|
+
Default projects have **no** building areas — add one with
|
|
49
|
+
`ba_ops.add_building_area_to_project(...)` before any envelope
|
|
50
|
+
work.
|
|
51
|
+
- **Defaults**: `comcheck_api.defaults` has `get_default_*_template()`
|
|
52
|
+
functions that return Pydantic models filled with sensible defaults.
|
|
53
|
+
Always start from these.
|
|
54
|
+
- **Simulation flow is async**: `start_run_simulation` returns a
|
|
55
|
+
session ID. Poll `get_simulation_status` until status is
|
|
56
|
+
`"SUCCESS"` (terminal-ok) or `"FAILED"` (terminal-error), then call
|
|
57
|
+
`get_simulation_result`. See `comcheck_api.types.SimulationStatus`
|
|
58
|
+
for known lifecycle values (the catalog isn't exhaustive — only
|
|
59
|
+
the terminal pair is guaranteed stable).
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import os
|
|
65
|
+
import time
|
|
66
|
+
|
|
67
|
+
from comcheck_api import COMcheckClient
|
|
68
|
+
from comcheck_api.defaults import get_default_project_template
|
|
69
|
+
from comcheck_api.types import SimulationStatus
|
|
70
|
+
|
|
71
|
+
# Client does NOT auto-read COM_API_KEY — pass it in.
|
|
72
|
+
client = COMcheckClient(api_key=os.environ["COM_API_KEY"])
|
|
73
|
+
|
|
74
|
+
# Build a project from a default template
|
|
75
|
+
project = get_default_project_template()
|
|
76
|
+
project.project.projectTitle = "5,000 sqft office in Seattle"
|
|
77
|
+
|
|
78
|
+
# Run a simulation
|
|
79
|
+
session_id = client.start_run_simulation(project)
|
|
80
|
+
|
|
81
|
+
# Poll until terminal — SUCCESS or FAILED
|
|
82
|
+
while True:
|
|
83
|
+
status = client.get_simulation_status(session_id)
|
|
84
|
+
if status["status"] == SimulationStatus.SUCCESS:
|
|
85
|
+
break
|
|
86
|
+
if status["status"] == SimulationStatus.FAILED:
|
|
87
|
+
raise RuntimeError(f"Simulation failed: {status.get('message')}")
|
|
88
|
+
time.sleep(5)
|
|
89
|
+
|
|
90
|
+
result = client.get_simulation_result(session_id)
|
|
91
|
+
print(result["performanceRating"])
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Conventions (do)
|
|
95
|
+
|
|
96
|
+
- Use `get_default_*_template()` to start any new component, then
|
|
97
|
+
customize. Don't construct `ComBuilding` from scratch.
|
|
98
|
+
- Use the operation modules (e.g.,
|
|
99
|
+
`env_ops.add_ag_wall_to_project(project, area_key, wall)`) to
|
|
100
|
+
mutate project structure. Don't manipulate nested dicts directly.
|
|
101
|
+
- For envelope items, look up the building-area key first via
|
|
102
|
+
`ba_ops.get_building_area_keys_from_project(project)`. The key
|
|
103
|
+
goes between `project` and the new component in every
|
|
104
|
+
`add_*_to_project` envelope call.
|
|
105
|
+
- Set enum-typed fields (`orientation`, `wallType`, `code`, etc.)
|
|
106
|
+
with members from the matching `*Options` enum imported from
|
|
107
|
+
`comcheck_api.types` — not raw strings, which trigger
|
|
108
|
+
Pydantic serialization warnings.
|
|
109
|
+
- Pass `api_key=` explicitly to `COMcheckClient(...)`. The SDK does
|
|
110
|
+
**not** auto-load any env var — read it yourself:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from dotenv import load_dotenv
|
|
114
|
+
load_dotenv()
|
|
115
|
+
client = COMcheckClient(api_key=os.environ["COM_API_KEY"])
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`client.set_api_key(api_key)` is the equivalent post-construction
|
|
119
|
+
setter. Users get a Personal Access Token from the COMcheck Web
|
|
120
|
+
site (Settings → Developer Setting); see the
|
|
121
|
+
[GitHub README](https://github.com/pnnl/comcheckweb-api-python#1-obtain-an-api-key)
|
|
122
|
+
or the
|
|
123
|
+
[Getting Started page](https://pnnl.github.io/comcheckweb-api-python/getting-started/)
|
|
124
|
+
for the full walkthrough.
|
|
125
|
+
- Wrap network calls in try/except and catch `COMCheckHTTPError`,
|
|
126
|
+
`COMCheckConnectionError`, `COMCheckValidationError`,
|
|
127
|
+
`COMCheckSimulationError`, `COMCheckProjectNotFoundError`.
|
|
128
|
+
- The package uses `httpx`, not `requests`.
|
|
129
|
+
|
|
130
|
+
## Conventions (don't)
|
|
131
|
+
|
|
132
|
+
- Don't construct project JSON by hand — use templates + operation
|
|
133
|
+
functions.
|
|
134
|
+
- Don't suggest `requests` — the SDK is `httpx`-based.
|
|
135
|
+
- Don't import private modules (anything starting with `_`).
|
|
136
|
+
- Don't poll `get_simulation_status` faster than every 5 seconds.
|
|
137
|
+
- Don't put the API key in source code; use env var or argument.
|
|
138
|
+
- Don't use `comcheck_api.managers.*` (e.g. `AgWallListManager`,
|
|
139
|
+
`RoofListManager`). Those are internal list-mutation helpers; they
|
|
140
|
+
bypass the validation logic in the operation modules. Always go
|
|
141
|
+
through `project_envelope_operations` and
|
|
142
|
+
`project_building_area_operations` instead.
|
|
143
|
+
- Don't add, update, or remove interior lighting (the
|
|
144
|
+
`activityUse[]` fixtures), exterior lighting (`exteriorUse[]`,
|
|
145
|
+
`fixtureSchedule[]`), HVAC/mechanical, or renewable-energy
|
|
146
|
+
components — no operations exist for them. The whole-building
|
|
147
|
+
`interiorLightingSpace` singleton on each `WholeBldgUse` *is*
|
|
148
|
+
editable through `project_building_area_operations`; the per-
|
|
149
|
+
activity lighting nested under `activityUse[]` is not. The
|
|
150
|
+
`COMcheckClient` user methods (`list_projects`, `get_project`,
|
|
151
|
+
`update_project`, `start_run_simulation`, `get_simulation_status`,
|
|
152
|
+
`get_simulation_result`, `set_api_key`) are fully supported and
|
|
153
|
+
fine to use. If asked for an unsupported mutation area, tell the
|
|
154
|
+
user it's not implemented and offer building-area / envelope /
|
|
155
|
+
simulation instead. Confirm operation scope with
|
|
156
|
+
`comcheck_api.list_operations()` (only `building_area` and
|
|
157
|
+
`envelope` groups exist).
|
|
158
|
+
|
|
159
|
+
## Common patterns
|
|
160
|
+
|
|
161
|
+
### Adding a building area, then an above-grade wall
|
|
162
|
+
|
|
163
|
+
`get_default_project_template()` starts with **zero building
|
|
164
|
+
areas** — add one before attaching any envelope component.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from comcheck_api import (
|
|
168
|
+
project_envelope_operations as env_ops,
|
|
169
|
+
project_building_area_operations as ba_ops,
|
|
170
|
+
)
|
|
171
|
+
from comcheck_api.defaults import (
|
|
172
|
+
get_default_building_area_template,
|
|
173
|
+
get_default_ag_wall_template,
|
|
174
|
+
)
|
|
175
|
+
from comcheck_api.types import OrientationOptions
|
|
176
|
+
|
|
177
|
+
# 1. Add the building area (no areas exist by default).
|
|
178
|
+
area = get_default_building_area_template()
|
|
179
|
+
area.areaDescription = "Open office"
|
|
180
|
+
project = ba_ops.add_building_area_to_project(project, area)
|
|
181
|
+
|
|
182
|
+
# 2. Look up its key.
|
|
183
|
+
area_key = ba_ops.get_building_area_keys_from_project(project)[0]["key"]
|
|
184
|
+
|
|
185
|
+
# 3. Attach the wall to that area.
|
|
186
|
+
wall = get_default_ag_wall_template()
|
|
187
|
+
wall.description = "South wall"
|
|
188
|
+
wall.orientation = OrientationOptions.SOUTH # use the enum, not "SOUTH"
|
|
189
|
+
wall.grossArea = 4800.0 # field is grossArea, not area
|
|
190
|
+
project = env_ops.add_ag_wall_to_project(project, area_key, wall)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Listing the user's projects and updating one
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
projects = client.list_projects()
|
|
197
|
+
target = next(p for p in projects if p["name"] == "My office") # key is "name", not "title"
|
|
198
|
+
project_obj = client.get_project(target["_id"]) # note the underscore
|
|
199
|
+
project_obj.project.projectTitle = "My office (revised)"
|
|
200
|
+
client.update_project(project_id=target["_id"], project_data=project_obj)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`list_projects()` returns **raw, untyped dicts** straight from the API
|
|
204
|
+
(no TypedDict enforces the shape). Each entry looks like:
|
|
205
|
+
`{"_id", "name", "energyCode", "status", "sharing", "ownedByMe",
|
|
206
|
+
"lastUpdated"}`. The display name is under `"name"` — there is **no
|
|
207
|
+
`"title"` key** (matching on `"title"` silently fails with
|
|
208
|
+
`StopIteration`/`KeyError`).
|
|
209
|
+
|
|
210
|
+
`get_project(project_id, mode="json")` returns a raw dict instead of
|
|
211
|
+
a `ComBuilding` model — handy when you just need the JSON shape.
|
|
212
|
+
|
|
213
|
+
### Polling a simulation with a timeout
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
import time
|
|
217
|
+
from comcheck_api.types import SimulationStatus
|
|
218
|
+
|
|
219
|
+
session_id = client.start_run_simulation(project)
|
|
220
|
+
deadline = time.time() + 300 # 5 min
|
|
221
|
+
while time.time() < deadline:
|
|
222
|
+
status = client.get_simulation_status(session_id)
|
|
223
|
+
if status["status"] == SimulationStatus.SUCCESS:
|
|
224
|
+
result = client.get_simulation_result(session_id)
|
|
225
|
+
break
|
|
226
|
+
if status["status"] == SimulationStatus.FAILED:
|
|
227
|
+
raise RuntimeError(f"Simulation failed: {status.get('message')}")
|
|
228
|
+
time.sleep(5)
|
|
229
|
+
else:
|
|
230
|
+
raise TimeoutError(f"Simulation {session_id} did not complete in 5 min")
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Gotchas
|
|
234
|
+
|
|
235
|
+
- **Field names are lowercase camelCase**, not PascalCase.
|
|
236
|
+
`project.project.projectTitle`, `project.control.code`,
|
|
237
|
+
`project.lighting.wholeBldgUse[0].areaDescription`. There is no
|
|
238
|
+
`Project`, `Control`, `WholeBldgUse` (top-level), or `.title`.
|
|
239
|
+
- **AG/BG wall area field is `grossArea`**, not `area`. Same for
|
|
240
|
+
roofs/floors/windows/doors. Setting `.area` silently does nothing
|
|
241
|
+
because Pydantic models reject unknown attributes only in strict
|
|
242
|
+
mode.
|
|
243
|
+
- **Use enum members for typed fields**: `OrientationOptions.NORTH`,
|
|
244
|
+
`WallTypeOptions.WOOD_FRAME_16_AG_WALL`,
|
|
245
|
+
`EnergyCodeOptions.CEZ_90_1_2022`. Setting them to raw strings
|
|
246
|
+
works at runtime but emits `PydanticSerializationUnexpectedValue`
|
|
247
|
+
warnings every time the model serializes.
|
|
248
|
+
- **There is no `STEEL_FRAME` wall type — steel framing maps to
|
|
249
|
+
`METAL_FRAME_*`.** `WallTypeOptions` members are `WOOD_FRAME_16/24`,
|
|
250
|
+
`METAL_FRAME_16/24`, `METAL_WALL_WO_TB`, `METAL_BLDG`, `CONCRETE`,
|
|
251
|
+
`MASONRY`, `OTHER` (each suffixed `_AG_WALL`). A user asking for a
|
|
252
|
+
"steel-frame wall" wants `WallTypeOptions.METAL_FRAME_16_AG_WALL`
|
|
253
|
+
(or `_24_`).
|
|
254
|
+
- **`COMcheckClient(api_key=...)` does not auto-read env vars.** No
|
|
255
|
+
`COM_API_KEY` fallback exists in `__init__`. Pass it explicitly.
|
|
256
|
+
- **`SimulationStatus` is a known-values catalog, not an exhaustive
|
|
257
|
+
contract.** The server may emit lifecycle values not yet in the
|
|
258
|
+
enum (e.g. `EVALUATING` was added in a later version). The
|
|
259
|
+
`status` field comes back as a plain `str` so unknown values
|
|
260
|
+
don't crash polling. Only `SUCCESS` and `FAILED` are guaranteed
|
|
261
|
+
terminal — break/raise on those, keep polling for everything
|
|
262
|
+
else (don't enumerate non-terminals).
|
|
263
|
+
- **A 500 from `start_run_simulation` usually means bad project
|
|
264
|
+
data, not a server outage.** All HTTP errors surface as the same
|
|
265
|
+
`COMCheckHTTPError` — there is no validation-specific exception, so
|
|
266
|
+
a payload the engine rejects (e.g. a malformed shape like assigning
|
|
267
|
+
a list to an object field, or project state the engine won't
|
|
268
|
+
accept) comes back as a bare HTTP 500, indistinguishable from a
|
|
269
|
+
real outage. To tell them apart, simulate a fresh
|
|
270
|
+
`get_default_project_template()` project: if that succeeds, the 500
|
|
271
|
+
is your project data, not the server. Inspect
|
|
272
|
+
`COMCheckHTTPError.status_code` / `.response_data` for detail.
|
|
273
|
+
|
|
274
|
+
## When you need more detail
|
|
275
|
+
|
|
276
|
+
- For the authoritative list of what's implemented → call
|
|
277
|
+
`comcheck_api.list_operations()`. Only operations it returns are
|
|
278
|
+
supported.
|
|
279
|
+
- For envelope assemblies (roof, walls, floor, windows, doors,
|
|
280
|
+
skylights, thermal bridges) → read `reference/operations.md`.
|
|
281
|
+
- For Pydantic model field-level details → read `reference/types.md`.
|
|
282
|
+
- For the simulation start/poll/fetch flow → read
|
|
283
|
+
`reference/simulation.md`.
|
|
284
|
+
- To validate generated code against a mocked client → run
|
|
285
|
+
`scripts/validate_code.py`.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Project Operations Reference
|
|
2
|
+
|
|
3
|
+
Operation functions are free functions in two modules:
|
|
4
|
+
|
|
5
|
+
- `comcheck_api.project_operations.project_building_area_operations`
|
|
6
|
+
- `comcheck_api.project_operations.project_envelope_operations`
|
|
7
|
+
|
|
8
|
+
Each function takes a `ComBuilding` and a payload, and returns a new
|
|
9
|
+
`ComBuilding`. Treat them as immutable transformations.
|
|
10
|
+
|
|
11
|
+
## Building area operations
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from comcheck_api import project_building_area_operations as ba_ops
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Function | Purpose |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `add_building_area_to_project(project, new_building_area)` | Add a `WholeBldgUse` building area to the project. |
|
|
20
|
+
| `update_building_area_in_project(project, building_area_key, updates)` | Update fields of an existing building area by key. |
|
|
21
|
+
| `remove_building_area_from_project(project, building_area_key)` | Remove a building area by key. |
|
|
22
|
+
| `get_building_area_keys_from_project(project)` | List `[{key, areaDescription}, …]` for the project. |
|
|
23
|
+
|
|
24
|
+
## Envelope operations
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from comcheck_api import project_envelope_operations as env_ops
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Every envelope `add_*_to_project` call attaches the new component to
|
|
31
|
+
a building-area key. Default projects have no areas — add one first:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from comcheck_api.defaults import get_default_building_area_template
|
|
35
|
+
|
|
36
|
+
area = get_default_building_area_template()
|
|
37
|
+
area.areaDescription = "Open office"
|
|
38
|
+
project = ba_ops.add_building_area_to_project(project, area)
|
|
39
|
+
|
|
40
|
+
area_key = ba_ops.get_building_area_keys_from_project(project)[0]["key"]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Component | Add signature | Update / Remove key |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| Roof | `add_roof_to_project(project, building_area_key, new_roof)` | `assemblyType` |
|
|
46
|
+
| Above-grade wall | `add_ag_wall_to_project(project, building_area_key, new_ag_wall)` | `assemblyType` |
|
|
47
|
+
| Below-grade wall | `add_bg_wall_to_project(project, building_area_key, new_bg_wall)` | `assemblyType` |
|
|
48
|
+
| Floor | `add_floor_to_project(project, building_area_key, new_floor)` | `assemblyType` |
|
|
49
|
+
| Skylight | `add_skylight_to_project(project, building_area_key, new_skylight, roof=None)` | `assemblyType` |
|
|
50
|
+
| Window | `add_window_to_project(project, building_area_key, new_window, wall=None)` | `assemblyType` |
|
|
51
|
+
| Door | `add_door_to_project(project, building_area_key, new_door, wall=None)` | `assemblyType` |
|
|
52
|
+
| Thermal bridge | `add_thermal_bridge_to_project(project, building_area_key, ag_wall, ...)` | (no update/remove yet) |
|
|
53
|
+
|
|
54
|
+
The `update_*_in_project` and `remove_*_from_project` functions take
|
|
55
|
+
the component's `assemblyType` string, e.g.
|
|
56
|
+
`update_ag_wall_in_project(project, ag_wall_assembly_type, updates)`.
|
|
57
|
+
|
|
58
|
+
## Nesting rules
|
|
59
|
+
|
|
60
|
+
- Skylights live inside a `Roof`. Pass `roof=` to
|
|
61
|
+
`add_skylight_to_project` to attach to a specific roof; otherwise
|
|
62
|
+
it goes on the first one in the area.
|
|
63
|
+
- Windows and doors live inside an above-grade or below-grade wall.
|
|
64
|
+
Pass `wall=` to target a specific wall.
|
|
65
|
+
- Thermal bridges always require an `ag_wall=` argument.
|
|
66
|
+
- Floors and walls live directly under the building area.
|
|
67
|
+
|
|
68
|
+
## Working with templates
|
|
69
|
+
|
|
70
|
+
Always start from `comcheck_api.defaults`:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from comcheck_api.defaults import (
|
|
74
|
+
get_default_project_template,
|
|
75
|
+
get_default_roof_template,
|
|
76
|
+
get_default_ag_wall_template,
|
|
77
|
+
get_default_bg_wall_template,
|
|
78
|
+
get_default_floor_template,
|
|
79
|
+
get_default_window_template,
|
|
80
|
+
get_default_door_template,
|
|
81
|
+
get_default_skylight_template,
|
|
82
|
+
get_default_thermal_bridge_template,
|
|
83
|
+
get_default_building_area_template,
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Each returns a fully-populated Pydantic model with sensible defaults
|
|
88
|
+
(Boulder, CO; metal-frame walls; double-pane low-E glazing; etc.).
|
|
89
|
+
Customize fields after construction, then attach to a building area:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from comcheck_api.types import OrientationOptions
|
|
93
|
+
|
|
94
|
+
area_key = ba_ops.get_building_area_keys_from_project(project)[0]["key"]
|
|
95
|
+
|
|
96
|
+
roof = get_default_roof_template()
|
|
97
|
+
roof.grossArea = 6000.0 # field is grossArea, not area
|
|
98
|
+
roof.cavityRValue = 38.0
|
|
99
|
+
roof.orientation = OrientationOptions.UNSPECIFIED_ORIENTATION
|
|
100
|
+
project = env_ops.add_roof_to_project(project, area_key, roof)
|
|
101
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Simulation Flow Reference
|
|
2
|
+
|
|
3
|
+
Simulations are asynchronous on the server side. Three-step pattern:
|
|
4
|
+
|
|
5
|
+
1. **Start** — submit the project, receive a session ID.
|
|
6
|
+
2. **Poll** — repeatedly check status until it reports `SUCCESS`
|
|
7
|
+
(terminal-ok) or `FAILED` (terminal-error).
|
|
8
|
+
3. **Fetch** — retrieve the result by session ID.
|
|
9
|
+
|
|
10
|
+
## Status values
|
|
11
|
+
|
|
12
|
+
`get_simulation_status(...)["status"]` is a string. Known values are
|
|
13
|
+
enumerated in `comcheck_api.types.SimulationStatus` (a `StrEnum`),
|
|
14
|
+
but **the catalog is not exhaustive** — the server may emit values
|
|
15
|
+
the SDK doesn't yet enumerate. The status field is typed as `str`
|
|
16
|
+
specifically so unknown values pass through without crashing the
|
|
17
|
+
polling loop. Only the terminal pair (`SUCCESS` / `FAILED`) is
|
|
18
|
+
guaranteed stable; treat anything else as "still in progress."
|
|
19
|
+
|
|
20
|
+
| Value | Terminal? | Meaning |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `INITIALIZING` | no | Server is preparing the run |
|
|
23
|
+
| `GENERATING_BASELINE` | no | Building the baseline (code-minimum) model |
|
|
24
|
+
| `GENERATING_PROPOSED` | no | Building the proposed (user) model |
|
|
25
|
+
| `RUNNING_SIMULATIONS` | no | Energy simulations are executing |
|
|
26
|
+
| `CALCULATING_RESULTS` | no | Post-processing performance metrics |
|
|
27
|
+
| `EVALUATING` | no | Evaluating compliance results |
|
|
28
|
+
| `SUCCESS` | **yes** | Result is ready; call `get_simulation_result` |
|
|
29
|
+
| `FAILED` | **yes** | Simulation failed; check the `message` field |
|
|
30
|
+
| (any other value) | no | Future / unknown server state — keep polling |
|
|
31
|
+
|
|
32
|
+
Because `SimulationStatus` is a `StrEnum`, `==` works against both
|
|
33
|
+
the enum member and the raw string:
|
|
34
|
+
``status == SimulationStatus.SUCCESS`` and
|
|
35
|
+
``status == "SUCCESS"`` are equivalent.
|
|
36
|
+
|
|
37
|
+
## Methods
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from comcheck_api import COMcheckClient
|
|
41
|
+
client = COMcheckClient(api_key="...")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Method | Returns | Purpose |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `client.start_run_simulation(project, project_id=None)` | `str` (session ID) | Kick off the simulation. If `project_id` is provided, the project is also saved/updated. |
|
|
47
|
+
| `client.get_simulation_status(session_id)` | `dict` | Current status. Fields: `sessionId`, `status` (see the Status values table above), optional `message`. |
|
|
48
|
+
| `client.get_simulation_result(session_id)` | `dict` | Final result. Fields: `sessionId`, `performanceRating`, `energyCreditPerformanceRating`, `proposedBpf`, `baselineBpf`. |
|
|
49
|
+
|
|
50
|
+
### Loading an existing project to simulate
|
|
51
|
+
|
|
52
|
+
| Method | Returns | Notes |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `client.list_projects()` | `list[dict]` | Each project dict uses **`_id`** (with the underscore), not `id`. |
|
|
55
|
+
| `client.get_project(project_id, mode="python")` | `ComBuilding` | Default. Returns a Pydantic model you can pass straight into `start_run_simulation`. |
|
|
56
|
+
| `client.get_project(project_id, mode="json")` | `dict` | Returns the raw project JSON instead of a parsed model. |
|
|
57
|
+
|
|
58
|
+
## Polling pattern
|
|
59
|
+
|
|
60
|
+
Always include a timeout. 5 minutes is a safe default for a typical
|
|
61
|
+
small/medium project.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import time
|
|
65
|
+
from comcheck_api.types import SimulationStatus
|
|
66
|
+
|
|
67
|
+
session_id = client.start_run_simulation(project)
|
|
68
|
+
deadline = time.time() + 300
|
|
69
|
+
|
|
70
|
+
while time.time() < deadline:
|
|
71
|
+
status = client.get_simulation_status(session_id)
|
|
72
|
+
if status["status"] == SimulationStatus.SUCCESS:
|
|
73
|
+
break
|
|
74
|
+
if status["status"] == SimulationStatus.FAILED:
|
|
75
|
+
raise RuntimeError(f"Simulation failed: {status.get('message')}")
|
|
76
|
+
time.sleep(5)
|
|
77
|
+
else:
|
|
78
|
+
raise TimeoutError(f"Simulation {session_id} did not complete in 5 minutes")
|
|
79
|
+
|
|
80
|
+
result = client.get_simulation_result(session_id)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Result interpretation
|
|
84
|
+
|
|
85
|
+
- `performanceRating` is the primary pass/fail metric — typically
|
|
86
|
+
expressed as a percentage margin against the baseline. Positive =
|
|
87
|
+
better than baseline = pass.
|
|
88
|
+
- `proposedBpf` / `baselineBpf` are Building Performance Factors for
|
|
89
|
+
the proposed and baseline (code-minimum) buildings.
|
|
90
|
+
- `energyCreditPerformanceRating` is the credit rating used when
|
|
91
|
+
optional measures (renewables, advanced controls, etc.) are
|
|
92
|
+
included.
|
|
93
|
+
|
|
94
|
+
## Don't
|
|
95
|
+
|
|
96
|
+
- Don't poll faster than every 5 seconds — the backend rate-limits.
|
|
97
|
+
- Don't run multiple concurrent simulations from the same API key
|
|
98
|
+
unless you've confirmed your quota allows it.
|
|
99
|
+
- Don't drop the `session_id` — there's no recovery without it.
|