ADR-033: Schedule Draw — the atlas generator as an external command, drawn per zone, aggregated by the existing rules¶
- Status: Provisional (D-194 to D-196), pending the owner's reading; the released generator 3.0.0 pinned in 1.6.0 (D-198); decisions 2, 4, 8, 9, and 10 amended for generator 4.0.0 and atlas release 2.2.0 in 1.7.0 by ADR-035 (D-205); decision 10 amended by D-128 and D-207: the source program's SHA-256 is that of its text with LF line endings, the setting's and the program settings' those of their bytes; and by D-226: the source program's SHA-256 is that of its canonical JSON (the atlas's
json_sha256), the setting's and the program settings' still those of their bytes - Date: 2026-10-29
- Context: the owner's requirement (D-133, D-134), design note, program JSON 5.4.0 (ADR-027, D-172), test suites (ADR-020), the atlas's
atlas_schedules3.0.0 (its ADR 0035 and ADR 0047), atlas release 2.0.0'sgenerator.json(its ADR 0046)
Context¶
The owner asked for stochastic schedules as an optional add-on beside the deterministic ruleset schedules of program JSON (requirement 1 to 6): a program setting, a generator setting, and a seed give one program JSON; draws are per zone with shifted seeds; the generator runs once per test case; merged zones aggregate the drawn zone schedules and never get a draw of their own. The atlas now ships the generator, atlas_schedules 3.0.0 (request and bundle schema 3.0.0), a readable port of ComStock and ResStock run as python -m atlas_schedules request.json -o bundle.json, and release 2.0.0 ships a generator.json beside each prototype's programs: 967 programs' recipes keyed by release program ID, with a source view everywhere and a reviewed view for 90.1. The requirement left five items to settle.
Decision¶
- An external command, not a C# port. BEMGen writes the request to a file in a new temporary folder, runs a configurable command line with
<request> -o <bundle>appended (defaultpython -m atlas_schedules, in a given working folder, the atlas checkout; later the console scriptatlas-schedulesof the atlas package), captures its standard error, stops it after the setting's timeout (default 600 s), and reads the bundle. No shell runs, nothing is expanded, and BEMGen opens no network connection; the child getsPYTHONHASHSEED=0andPYTHONIOENCODING=utf-8. The service lives inLod.ProgramJson(namespaceLod.ProgramJson.ScheduleDraw), since it reads and writes program JSON 5.4.0 with the strict reader and schema there; the process runner is behindIScheduleGenerator, so tests use a recorded bundle. Why: the generator is validated against native OpenStudio and Ruby answer keys within 1e-6 (atlas ADR 0035); a second port would need its own validation and would drift. - Pinned versions with clear diagnostics. The setting must name
atlas_schedules3.0.0 and bundle schema 3.0.0 (ScheduleDrawGeneratorNotSupported); the bundle'sschema_versionmust be 3.0.0 and itsgenerator_version3.0.0 or a PEP 440 pre-release or development version of it (3.0.0rc1,3.0.0.dev0, which the current atlas main reports), accepted with the warningScheduleDrawPreReleaseand recorded; anything else, a post-release included, isScheduleDrawVersionMismatch. A new generator version needs a new pin and its ADR. - What is drawn (requirement item 1). The roles occupancy, lighting, electric and gas equipment, hot water (flow and target temperature), activity, and the heating and cooling setpoints, all by default; the setting's
drawlist narrows them, and the others keep their rulesets. Ventilation and infiltration are not program schedules the generator draws, and the water-use zone gains' fractions are left out as BEMGen leaves those gains out (program-json.md). A drawn schedule becomes anannualschedule of 2007 with the bundle's values copied as written, a new IDdraw:<slot>:<source ID>, andsource_contextnaming its source, slot, owner, seed, and operating group; the loads point to it, and a load'sschedule_id_by_climateis replaced with a warning. - Magnitudes and conservation. Release 2.0.0's commercial recipes bind the source magnitude to the sampled schedule (atlas ADR 0047): the program's load value is the amplitude. A draw keeps the program's value and checks it equals the bundle's amplitude in the same unit (
ScheduleDrawBindingMismatchotherwise: a wronggenerator.jsonor view), and that amplitude × Σ values × s equals the bundle's annual total withinToleranceSettings.RelativeLoad(ScheduleDrawBindingNotKept). Every drawn document is read back by the program JSON reader before it is returned. - Calendar (item 3). The generator's calendar is 2007, hourly, local standard time; drawn schedules are never remapped, so a draw needs the calendar year 2007 (
ScheduleDrawCalendarYear; a suite with schedules is refused otherwise). A suite's holidays apply to the schedules that are not drawn. - Seeds (item 4). The test case's seed is the request's master seed. Every instance is its own operating group by default, keyed by its instance key, so its hours are sampled from the generator's own seed policy, the SHA-256 policy
atlas_conditional_seeded_v1of the master seed and the key; BEMGen computes and records that seed (tested against the generator's values).operating_hours: sharedgives one group (building) for every instance. In a suite the key is<plan index>.<zone ID>(characters other than letters, digits,.,_,-become-), so the zone seed derives from the zone ID and the test case's seed, and the sample index is the seed's place in the spec's list. - Where the draw happens in a suite (requirements 4 to 6). One generator run per test case, on the zones of the generated plans, before any zoning: each zone of each distinct plan is an instance with its slot's program and floor area, and the zone takes its drawn program, imported as the slot's program would be. A floor entry that stands for several storeys shares its plan's draws, since a multiplied floor stands for identical floors. The simplifiers and stackers aggregate the drawn zone programs as they aggregate any programs, so every site of a test case derives from the same drawn zones and a merged zone never gets a draw.
- Supported recipes. Commercial and fixed recipes only. Apartment units (ResStock units) bind annual energies to end uses that program JSON 5.4.0 does not hold (clothes washer, television, ceiling fan, ...), and composition parents use another slot vocabulary (
people_heat, mixed loads), so both areScheduleDrawKindNotSupported; a mixed slot underschedules:is a plan problem, since a zone would need a draw per member. - The setting format. BEMGen's own
schedule_draw_setting1.0.0 (embedded schemaschedule-draw-setting-v1.schema.json):generator(name, version, bundle schema),view(sourcedefault, orreviewed),draw,operating_hours,occupancy(the generator's occupant-type option, passed through),timeout_seconds. The command and its working folder are machine settings, not part of the setting or the spec: inputs of the component, and--schedule-command/--schedule-folder(orBEMGEN_SCHEDULE_COMMAND/BEMGEN_ATLAS_GENERATOR) of the test-suite generator. - Provenance. Each drawn document's
metadata.schedule_drawholds the generator, its reported and pinned versions, the setting's, the program settings', and the source program's SHA-256, the view, the recipe, the master seed, the instance key, floor area, operating group, seed policy and seed, the request's and bundle's SHA-256, and every schedule drawn and not drawn. The same inputs and seed give byte-identical documents (the request is compact JSON with one line feed, the bundle's values are copied as written).
Alternatives¶
- Port the generator to C#. Rejected (decision 1): its validation is against native answer keys that BEMGen cannot rerun, and two ports would drift.
- One generator run per zone, with seeds shifted by BEMGen. Rejected: requirement 6 asks for one run per test case, and the generator already derives an instance seed from the master seed and the key; BEMGen records it.
- Draw each storey of a multiplied floor separately. Rejected: a floor multiplier stands for identical floors (ADR-009), so its storeys cannot differ; the reference and every test site share one drawn plan.
- Draw the merged zones. Rejected by requirement 4 and GLOBAL scientific rules 1 and 4: a merged zone would get schedules its sources do not have, so the hourly scheduled loads would no longer be conserved.
- Take the bundle's amplitude in place of the program's value. Rejected for v1: for the supported recipes they are equal by construction, and a difference means the recipe and program disagree, which should stop the draw rather than silently change a load.
- Convert apartment units' end uses into program loads. Rejected for now (scientific rule 3): it would change the program's load assumptions; left open.
Consequences¶
- Schedule Draw (
SDraw, panel 1 Program) draws one program; its output goes into Program JSON. A suite withschedules:writes one test case per seed with its draw's request and provenance indraws/,test_casesgains the draw's columns, andschedule_drawsrecords each zone's seed; suites without the section are unchanged. - Drawn documents are larger (8 annual schedules of 8,760 values) and their schema check takes about 2 s each, so a suite's test case takes a few seconds per zone; parallel test cases share the cost.
- Open: apartment units and composition parents; mixed slots; a calendar other than 2007; the software-only launcher's reviewed-source policy, which must be refreshed by a person for the changed sources and should list
BEMGEN_ATLAS_GENERATORamong its activation variables.
The released generator (D-198, version 1.6.0)¶
The pin of decision 2 is met by the released package: atlas-schedules 3.0.0, the wheel atlas_schedules-3.0.0-py3-none-any.whl (SHA-256 f082bb12f951fdebd78ddcf8ab20cc65a267a02a70213de4d7f13d56648229cb) of the atlas release atlas-schedules-v3.0.0, which reports generator_version 3.0.0, so a draw gives no ScheduleDrawPreRelease. Install it with python -m pip install atlas_schedules-3.0.0-py3-none-any.whl in a virtual environment (Python 3.11 or later; it brings numpy and jsonschema); the command is then the console script <venv>\Scripts\atlas-schedules.exe (or atlas-schedules with the environment active), or <venv>\Scripts\python.exe -m atlas_schedules; either runs in any folder, so Folder may stay unset. No atlas checkout is needed. The component's default command, python -m atlas_schedules, is unchanged: it works when that python has the package installed.
Checked on 2026-10-29 with Python 3.14.7 in D:\bemgen-venv-schedules: RealDrawTests passed with BEMGEN_ATLAS_COMMAND set to the console script and again to python.exe -m atlas_schedules (generator 3.0.0, bundle SHA-256 a94624b0…1838, byte-identical runs); the recorded bundle of Fixtures/draw/ was recorded again with it, since the first recording (generator 3.0.0.dev0 of atlas 1bc25767) differs only in generator_version. The commercial generator.json files are those of release 2.0.0; release 2.1.0 changes only the eight apartment folders' unit recipes, which Schedule Draw refuses (decision 8).
Generator 4.0.0 (D-205, version 1.7.0)¶
Atlas release 2.2.0 removed the generator.json files and generator 4.0.0 takes release program IDs with a view; ADR-035 records how Schedule Draw follows: the pins (generator 4.0.0, request and bundle schema 4.0.0, data release 2.2.0 by its manifest SHA-256), the release's generation.json as the program setting, the export's own view as the request's values, the shared services from the export, setting 2.0.0, and the provenance's release block. The section above records the 1.6.0 state.