enerdatasynth 0.4.1__tar.gz

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.
Files changed (50) hide show
  1. enerdatasynth-0.4.1/LICENSE +21 -0
  2. enerdatasynth-0.4.1/PKG-INFO +237 -0
  3. enerdatasynth-0.4.1/README.md +197 -0
  4. enerdatasynth-0.4.1/pyproject.toml +64 -0
  5. enerdatasynth-0.4.1/setup.cfg +4 -0
  6. enerdatasynth-0.4.1/src/enerdatasynth/__init__.py +71 -0
  7. enerdatasynth-0.4.1/src/enerdatasynth/cli.py +214 -0
  8. enerdatasynth-0.4.1/src/enerdatasynth/data/__init__.py +0 -0
  9. enerdatasynth-0.4.1/src/enerdatasynth/data/preprocess.py +447 -0
  10. enerdatasynth-0.4.1/src/enerdatasynth/data/temperature.py +386 -0
  11. enerdatasynth-0.4.1/src/enerdatasynth/diffusion/__init__.py +0 -0
  12. enerdatasynth-0.4.1/src/enerdatasynth/diffusion/ddpm.py +120 -0
  13. enerdatasynth-0.4.1/src/enerdatasynth/diffusion/schedules.py +38 -0
  14. enerdatasynth-0.4.1/src/enerdatasynth/evaluation/__init__.py +0 -0
  15. enerdatasynth-0.4.1/src/enerdatasynth/evaluation/baselines.py +72 -0
  16. enerdatasynth-0.4.1/src/enerdatasynth/evaluation/calibration.py +140 -0
  17. enerdatasynth-0.4.1/src/enerdatasynth/evaluation/failure_analysis.py +192 -0
  18. enerdatasynth-0.4.1/src/enerdatasynth/evaluation/metrics.py +58 -0
  19. enerdatasynth-0.4.1/src/enerdatasynth/evaluation/results_report.py +123 -0
  20. enerdatasynth-0.4.1/src/enerdatasynth/generators/__init__.py +13 -0
  21. enerdatasynth-0.4.1/src/enerdatasynth/generators/base.py +59 -0
  22. enerdatasynth-0.4.1/src/enerdatasynth/generators/load.py +165 -0
  23. enerdatasynth-0.4.1/src/enerdatasynth/models/__init__.py +54 -0
  24. enerdatasynth-0.4.1/src/enerdatasynth/models/denoiser.py +87 -0
  25. enerdatasynth-0.4.1/src/enerdatasynth/models/transformer.py +96 -0
  26. enerdatasynth-0.4.1/src/enerdatasynth/sampling/__init__.py +0 -0
  27. enerdatasynth-0.4.1/src/enerdatasynth/sampling/generate.py +91 -0
  28. enerdatasynth-0.4.1/src/enerdatasynth/sdk.py +281 -0
  29. enerdatasynth-0.4.1/src/enerdatasynth/training/__init__.py +0 -0
  30. enerdatasynth-0.4.1/src/enerdatasynth/training/sweep.py +224 -0
  31. enerdatasynth-0.4.1/src/enerdatasynth/training/train.py +183 -0
  32. enerdatasynth-0.4.1/src/enerdatasynth/utils/__init__.py +0 -0
  33. enerdatasynth-0.4.1/src/enerdatasynth/utils/config.py +183 -0
  34. enerdatasynth-0.4.1/src/enerdatasynth/visualization/__init__.py +0 -0
  35. enerdatasynth-0.4.1/src/enerdatasynth/visualization/plots.py +218 -0
  36. enerdatasynth-0.4.1/src/enerdatasynth.egg-info/PKG-INFO +237 -0
  37. enerdatasynth-0.4.1/src/enerdatasynth.egg-info/SOURCES.txt +48 -0
  38. enerdatasynth-0.4.1/src/enerdatasynth.egg-info/dependency_links.txt +1 -0
  39. enerdatasynth-0.4.1/src/enerdatasynth.egg-info/entry_points.txt +2 -0
  40. enerdatasynth-0.4.1/src/enerdatasynth.egg-info/requires.txt +22 -0
  41. enerdatasynth-0.4.1/src/enerdatasynth.egg-info/top_level.txt +1 -0
  42. enerdatasynth-0.4.1/tests/test_calibration.py +73 -0
  43. enerdatasynth-0.4.1/tests/test_config.py +102 -0
  44. enerdatasynth-0.4.1/tests/test_diffusion.py +66 -0
  45. enerdatasynth-0.4.1/tests/test_generators.py +99 -0
  46. enerdatasynth-0.4.1/tests/test_metrics.py +38 -0
  47. enerdatasynth-0.4.1/tests/test_models.py +55 -0
  48. enerdatasynth-0.4.1/tests/test_preprocessing.py +131 -0
  49. enerdatasynth-0.4.1/tests/test_public_api.py +117 -0
  50. enerdatasynth-0.4.1/tests/test_sampling.py +58 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sonakshi Sharma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.4
2
+ Name: enerdatasynth
3
+ Version: 0.4.1
4
+ Summary: A research SDK for synthetic PJM energy resource profiles (electric load today; a common interface for future resource types)
5
+ Author-email: Sonakshi Sharma <s.sonakshi2006@gmail.com>
6
+ License: MIT
7
+ Keywords: diffusion-model,synthetic-data,electric-load,pjm,time-series,energy,generative-model,research-sdk
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Requires-Python: >=3.9
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: numpy
20
+ Requires-Dist: pandas
21
+ Requires-Dist: torch
22
+ Requires-Dist: scikit-learn
23
+ Requires-Dist: statsmodels
24
+ Requires-Dist: holidays
25
+ Requires-Dist: matplotlib
26
+ Requires-Dist: requests
27
+ Requires-Dist: tqdm
28
+ Requires-Dist: pyarrow
29
+ Requires-Dist: pyyaml
30
+ Provides-Extra: viz
31
+ Requires-Dist: seaborn>=0.13; extra == "viz"
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: pytest-cov; extra == "dev"
35
+ Requires-Dist: ruff; extra == "dev"
36
+ Requires-Dist: mypy; extra == "dev"
37
+ Requires-Dist: build; extra == "dev"
38
+ Requires-Dist: twine; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ <div align="center">
42
+
43
+ # ⚡ enerdatasynth
44
+
45
+ ### A research SDK for synthetic PJM energy resource profiles
46
+
47
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
48
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
49
+ [![Tests: 59 passing](https://img.shields.io/badge/tests-59%20passing-brightgreen.svg)](tests/)
50
+ [![Packaging: verified](https://img.shields.io/badge/packaging-pip%20install%20verified-blue.svg)](MIGRATION.md)
51
+
52
+ **Generate realistic synthetic electric-load profiles from a fitted diffusion model — no ML expertise required.**
53
+
54
+ ```python
55
+ import enerdatasynth
56
+
57
+ load = enerdatasynth.model("pjm_load", zone="PS")
58
+ load.set(month=7, Tmax=86, Tmin=70, weekday=True)
59
+ samples = load.sample(n=100) # (100, 24) — real MW, ready to use
60
+ ```
61
+
62
+ [Quick Start](#-quick-start) · [Documentation](#-documentation) · [Model Card](MODEL_CARD.md) · [Benchmark Results](#-benchmark-results) · [Citation](#-citation)
63
+
64
+ </div>
65
+
66
+ <br>
67
+
68
+ <div align="center">
69
+ <img src="data/research_history/reports/week7/profiles_headtohead.png" alt="Mean daily load profile: enerdatasynth's transformer baseline vs. classical time-series baselines vs. real data" width="720">
70
+
71
+ <sub>Mean daily profile — the validated transformer architecture (data/research_history/) vs. classical baselines vs. real PJM load. See <a href="#-benchmark-results">Benchmark Results</a> for numbers from the currently-shipped pipeline.</sub>
72
+ </div>
73
+
74
+ <br>
75
+
76
+ ---
77
+
78
+ ## Why this exists
79
+
80
+ Utilities, grid operators, and energy researchers frequently need realistic synthetic load data — for privacy-preserving data sharing, stress-testing forecasting models, or scenario planning — without exposing real customer or grid data. `enerdatasynth` wraps a validated conditional diffusion pipeline behind an SDK that hides every ML implementation detail: you describe a day (season, temperature, calendar type), and get back a realistic profile.
81
+
82
+ ## ✨ Features
83
+
84
+ - **A fluent, implementation-hiding API** — `model()` → `.set()` → `.sample()`. No tensors, no checkpoint paths, no architecture classes ever surface.
85
+ - **A validated transformer architecture** — selected via a controlled 4-way architecture comparison (MLP/CNN/attention/transformer), not the first thing that was tried. See [Research Summary](#-research-summary).
86
+ - **A corrected diffusion sampler** — an earlier DDIM-style sampler was found to silently corrupt every result it touched; the fix is the only sampler shipped today.
87
+ - **Automatic bias calibration** — a systematic energy-error offset, diagnosed and corrected, applied automatically at generation time.
88
+ - **A common generator interface** (`BaseGenerator`) designed so a future resource type (e.g. wind) can be added through the same SDK contract — see [Extending](docs/extending.md).
89
+ - **Config-driven, YAML-first** hyperparameters (`data/configs/`), a real test suite (`tests/`, ~2–20s), and a full audit trail of every design decision ([MIGRATION.md](MIGRATION.md)) — nothing here is asserted without a file backing it.
90
+
91
+ ## 🔌 Supported Generators
92
+
93
+ | Resource | Status | Zones | Entry point |
94
+ |---|---|---|---|
95
+ | Electric load | ✅ Available | `PS` (PJM/PSE&G, NJ) | `enerdatasynth.model("pjm_load", zone="PS")` |
96
+ | Wind | 🧩 Interface-ready, not implemented | — | See [`docs/extending.md`](docs/extending.md) — no wind data, model, or results exist in this project |
97
+
98
+ ## 📦 Installation
99
+
100
+ ```bash
101
+ pip install . # from a local clone
102
+ # or, once built:
103
+ pip install dist/*.whl
104
+ ```
105
+
106
+ Both install paths — plus `pip install -e .` for development — are verified end-to-end (see [MIGRATION.md](MIGRATION.md) for what "verified" means here, including a real packaging bug that was only caught by actually installing the built wheel).
107
+
108
+ ```bash
109
+ pip install ".[viz,dev]" # + seaborn, + pytest/ruff/mypy/build/twine
110
+ ```
111
+
112
+ ## 🚀 Quick Start
113
+
114
+ ```python
115
+ import enerdatasynth
116
+
117
+ load = enerdatasynth.model("pjm_load", zone="PS")
118
+ load.set(month=7, Tmax=86, Tmin=70, weekday=True, holiday=False)
119
+ samples = load.sample(n=100, seed=0) # (100, 24) ndarray, MW
120
+ ```
121
+
122
+ That's the whole interface for generation. Training a new model, if you want to reproduce or extend the results below:
123
+
124
+ ```bash
125
+ enerdatasynth preprocess # raw CSVs -> tensors
126
+ enerdatasynth train # transformer, validated defaults
127
+ enerdatasynth calibrate --run-id transformer_T1000_H64_L2
128
+ ```
129
+
130
+ See [`docs/quickstart.md`](docs/quickstart.md) for the full walkthrough and [`examples/`](examples/) for runnable scripts.
131
+
132
+ ## 🏗️ Architecture
133
+
134
+ ```mermaid
135
+ flowchart LR
136
+ A[Raw PJM load CSVs\n+ NOAA temperature] --> B[Preprocessing\nclean · pivot · normalize]
137
+ B --> C["Conditioning vector z\n(month, dow, holiday, Tmax, Tmin, spread)"]
138
+ C --> D["Transformer denoiser\n(DDPM, corrected ancestral sampler)"]
139
+ D --> E[Scalar bias\ncalibration]
140
+ E --> F["ResourceModel.sample()\nreal MW output"]
141
+
142
+ style D fill:#4c6ef5,color:#fff
143
+ style E fill:#f59f00,color:#fff
144
+ ```
145
+
146
+ Full detail — why transformer over MLP/CNN/attention, why the ancestral sampler over DDIM, why calibration is scalar not per-hour — is in [`docs/architecture.md`](docs/architecture.md), with every claim traced to a specific result file.
147
+
148
+ ## 📊 Evaluation Pipeline
149
+
150
+ Every comparison in this project uses the same 7-metric suite (`Hourly`, `FirstDiff`, `ACF`, `RMSE`, `Peak`, `Energy`, `Ramp` — all lower-is-better, defined in [`docs/evaluation.md`](docs/evaluation.md)), computed by `enerdatasynth.evaluation.metrics.compute_metrics`. The same suite backs the automatic failure-mode detector (`evaluation.failure_analysis`) and the benchmark script below.
151
+
152
+ <div align="center">
153
+ <img src="data/reports/figures/hourly_distribution_comparison_T100_H256_L4.png" alt="Hourly distribution comparison, real vs generated, currently-shipped model" width="600">
154
+ <br><sub>Per-hour distribution, real vs. generated — the currently auto-discovered default model (see Benchmark Results).</sub>
155
+ </div>
156
+
157
+ ## 🏆 Benchmark Results
158
+
159
+ Run yourself: `python examples/run_benchmark.py --run-id <run_id>`. Numbers below are a real run against the full 600-day validation set, using the model `enerdatasynth.model("pjm_load")` actually auto-discovers today (the best currently-trained checkpoint under this package's own preprocessing pipeline):
160
+
161
+ | Model | Hourly↓ | FirstDiff↓ | ACF↓ | RMSE↓ | Peak↓ | Energy↓ | Ramp↓ |
162
+ |---|---|---|---|---|---|---|---|
163
+ | AR | 1.3533 | 0.4216 | 0.1458 | 0.1802 | 0.7557 | 4.2780 | 0.1242 |
164
+ | ARMA | 1.3581 | 0.4218 | **0.1081** | 0.1864 | 0.7557 | 4.3916 | 0.1239 |
165
+ | ARIMA | 1.3540 | 0.1698 | 0.3125 | 0.1813 | 0.3451 | 4.2945 | **0.0074** |
166
+ | SARIMA | 1.3625 | 0.6389 | 0.2489 | 0.1894 | 0.6834 | 4.4981 | 0.2969 |
167
+ | **enerdatasynth (default)** | **0.0590** | **0.0792** | 0.1347 | **0.0484** | **0.0362** | **0.8099** | 0.0647 |
168
+
169
+ Wins outright on 5 of 7 metrics against every classical baseline. ARMA edges it out on ACF, ARIMA on Ramp — reported honestly rather than rounded up to "wins everything." A separate, larger comparison against the validated transformer architecture (trained on a different conditioning scheme — see the caveat in [`docs/architecture.md`](docs/architecture.md)) shows an even larger margin; see [`docs/research_notes.md`](docs/research_notes.md).
170
+
171
+ ## 📚 Documentation
172
+
173
+ | Page | Contents |
174
+ |---|---|
175
+ | [`docs/installation.md`](docs/installation.md) | Install paths, extras, reproducing exact results, GPU |
176
+ | [`docs/quickstart.md`](docs/quickstart.md) | Full walkthrough: preprocess → train → calibrate → sample |
177
+ | [`docs/architecture.md`](docs/architecture.md) | Model, sampler, schedule, calibration — with evidence |
178
+ | [`docs/api_reference.md`](docs/api_reference.md) | The full `model()`/`.set()`/`.sample()` contract |
179
+ | [`docs/evaluation.md`](docs/evaluation.md) | The 7-metric suite, calibration, benchmarking |
180
+ | [`docs/temperature_data.md`](docs/temperature_data.md) | NOAA temperature ingestion, zone-station mapping |
181
+ | [`docs/training.md`](docs/training.md) | Training new checkpoints, sweeps, configs |
182
+ | [`docs/packaging.md`](docs/packaging.md) | How this package is built, versioned, and verified |
183
+ | [`docs/developer_guide.md`](docs/developer_guide.md) | Project layout, testing, contribution standards |
184
+ | [`docs/extending.md`](docs/extending.md) | Adding a new resource type (e.g. wind) |
185
+ | [`docs/faq.md`](docs/faq.md) | Common questions |
186
+ | [`MODEL_CARD.md`](MODEL_CARD.md) | Training data, limitations, intended/out-of-scope use |
187
+ | [`MIGRATION.md`](MIGRATION.md) | Full audit trail behind every default in this package |
188
+
189
+ ## 🧪 Examples
190
+
191
+ ```bash
192
+ python examples/generate_samples.py # LoadDiffusionModel round trip
193
+ python examples/run_full_pipeline.py # tiny end-to-end demo
194
+ python examples/exploratory_data_analysis.py # inspecting the processed dataset
195
+ ```
196
+
197
+ ## 🔬 Research Summary
198
+
199
+ - **A critical sampler bug, found and fixed.** An earlier sampler silently corrupted every diffusion result (values pinned near clamp bounds). Diagnosed and fixed; the corrected ancestral sampler is the only one shipped.
200
+ - **Architecture selected by controlled comparison, not assumption.** MLP vs CNN vs attention vs transformer, identical data/conditioning/schedule/seed. Transformer won on generated-profile quality at ~6x fewer parameters than the MLP it replaced (110,401 vs 688,408).
201
+ - **A methodological finding:** in that comparison, the *lowest validation loss* (CNN) was not the *best generated quality* (transformer) — a caution against judging generative models by training loss alone.
202
+ - **Calibration, evidence-checked rather than assumed better.** A more elaborate per-hour bias correction was tested and found *worse* on two metrics than the simpler scalar correction that's actually shipped.
203
+
204
+ Full detail, entirely traceable to specific files, is in [`docs/research_notes.md`](docs/research_notes.md) and [`MIGRATION.md`](MIGRATION.md).
205
+
206
+ ## 📄 Citation
207
+
208
+ No accompanying paper has been published yet. If you use this software, please cite the repository:
209
+
210
+ ```bibtex
211
+ @software{enerdatasynth,
212
+ title = {enerdatasynth: A Research SDK for Synthetic PJM Energy Resource Profiles},
213
+ author = {Sharma, Sonakshi},
214
+ year = {2026},
215
+ note = {Software}
216
+ }
217
+ ```
218
+
219
+ ## ⚖️ License
220
+
221
+ [MIT](LICENSE) © 2026 Sonakshi Sharma
222
+
223
+ ## 🗺️ Roadmap
224
+
225
+ - [ ] Re-validate the transformer/calibration pipeline under the package's current one-hot conditioning (see the caveat in [`docs/architecture.md`](docs/architecture.md))
226
+ - [ ] Multi-zone support (17 zones explored in `data/research_history/scripts/zone_expansion.py`, not yet promoted)
227
+ - [ ] A friendlier `set()` for `spread` (currently defaults to the training-set mean; see [`docs/api_reference.md`](docs/api_reference.md))
228
+ - [ ] Wind generation, once real data/research exists to back it (see [`docs/extending.md`](docs/extending.md))
229
+ - [ ] Re-sweep hyperparameters for the transformer architecture specifically (the current defaults are inherited from `data/research_history/`, never re-swept against this package's own pipeline)
230
+
231
+ Tracked in detail in [`data/reports/future_experiments.md`](data/reports/future_experiments.md) and [`MIGRATION.md`](MIGRATION.md).
232
+
233
+ ---
234
+
235
+ <div align="center">
236
+ <sub>Implementation details, folder structure, and the full conditioning-vector spec have moved to <a href="docs/developer_guide.md">docs/developer_guide.md</a> — this file is a landing page, not a developer reference.</sub>
237
+ </div>
@@ -0,0 +1,197 @@
1
+ <div align="center">
2
+
3
+ # ⚡ enerdatasynth
4
+
5
+ ### A research SDK for synthetic PJM energy resource profiles
6
+
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
8
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
9
+ [![Tests: 59 passing](https://img.shields.io/badge/tests-59%20passing-brightgreen.svg)](tests/)
10
+ [![Packaging: verified](https://img.shields.io/badge/packaging-pip%20install%20verified-blue.svg)](MIGRATION.md)
11
+
12
+ **Generate realistic synthetic electric-load profiles from a fitted diffusion model — no ML expertise required.**
13
+
14
+ ```python
15
+ import enerdatasynth
16
+
17
+ load = enerdatasynth.model("pjm_load", zone="PS")
18
+ load.set(month=7, Tmax=86, Tmin=70, weekday=True)
19
+ samples = load.sample(n=100) # (100, 24) — real MW, ready to use
20
+ ```
21
+
22
+ [Quick Start](#-quick-start) · [Documentation](#-documentation) · [Model Card](MODEL_CARD.md) · [Benchmark Results](#-benchmark-results) · [Citation](#-citation)
23
+
24
+ </div>
25
+
26
+ <br>
27
+
28
+ <div align="center">
29
+ <img src="data/research_history/reports/week7/profiles_headtohead.png" alt="Mean daily load profile: enerdatasynth's transformer baseline vs. classical time-series baselines vs. real data" width="720">
30
+
31
+ <sub>Mean daily profile — the validated transformer architecture (data/research_history/) vs. classical baselines vs. real PJM load. See <a href="#-benchmark-results">Benchmark Results</a> for numbers from the currently-shipped pipeline.</sub>
32
+ </div>
33
+
34
+ <br>
35
+
36
+ ---
37
+
38
+ ## Why this exists
39
+
40
+ Utilities, grid operators, and energy researchers frequently need realistic synthetic load data — for privacy-preserving data sharing, stress-testing forecasting models, or scenario planning — without exposing real customer or grid data. `enerdatasynth` wraps a validated conditional diffusion pipeline behind an SDK that hides every ML implementation detail: you describe a day (season, temperature, calendar type), and get back a realistic profile.
41
+
42
+ ## ✨ Features
43
+
44
+ - **A fluent, implementation-hiding API** — `model()` → `.set()` → `.sample()`. No tensors, no checkpoint paths, no architecture classes ever surface.
45
+ - **A validated transformer architecture** — selected via a controlled 4-way architecture comparison (MLP/CNN/attention/transformer), not the first thing that was tried. See [Research Summary](#-research-summary).
46
+ - **A corrected diffusion sampler** — an earlier DDIM-style sampler was found to silently corrupt every result it touched; the fix is the only sampler shipped today.
47
+ - **Automatic bias calibration** — a systematic energy-error offset, diagnosed and corrected, applied automatically at generation time.
48
+ - **A common generator interface** (`BaseGenerator`) designed so a future resource type (e.g. wind) can be added through the same SDK contract — see [Extending](docs/extending.md).
49
+ - **Config-driven, YAML-first** hyperparameters (`data/configs/`), a real test suite (`tests/`, ~2–20s), and a full audit trail of every design decision ([MIGRATION.md](MIGRATION.md)) — nothing here is asserted without a file backing it.
50
+
51
+ ## 🔌 Supported Generators
52
+
53
+ | Resource | Status | Zones | Entry point |
54
+ |---|---|---|---|
55
+ | Electric load | ✅ Available | `PS` (PJM/PSE&G, NJ) | `enerdatasynth.model("pjm_load", zone="PS")` |
56
+ | Wind | 🧩 Interface-ready, not implemented | — | See [`docs/extending.md`](docs/extending.md) — no wind data, model, or results exist in this project |
57
+
58
+ ## 📦 Installation
59
+
60
+ ```bash
61
+ pip install . # from a local clone
62
+ # or, once built:
63
+ pip install dist/*.whl
64
+ ```
65
+
66
+ Both install paths — plus `pip install -e .` for development — are verified end-to-end (see [MIGRATION.md](MIGRATION.md) for what "verified" means here, including a real packaging bug that was only caught by actually installing the built wheel).
67
+
68
+ ```bash
69
+ pip install ".[viz,dev]" # + seaborn, + pytest/ruff/mypy/build/twine
70
+ ```
71
+
72
+ ## 🚀 Quick Start
73
+
74
+ ```python
75
+ import enerdatasynth
76
+
77
+ load = enerdatasynth.model("pjm_load", zone="PS")
78
+ load.set(month=7, Tmax=86, Tmin=70, weekday=True, holiday=False)
79
+ samples = load.sample(n=100, seed=0) # (100, 24) ndarray, MW
80
+ ```
81
+
82
+ That's the whole interface for generation. Training a new model, if you want to reproduce or extend the results below:
83
+
84
+ ```bash
85
+ enerdatasynth preprocess # raw CSVs -> tensors
86
+ enerdatasynth train # transformer, validated defaults
87
+ enerdatasynth calibrate --run-id transformer_T1000_H64_L2
88
+ ```
89
+
90
+ See [`docs/quickstart.md`](docs/quickstart.md) for the full walkthrough and [`examples/`](examples/) for runnable scripts.
91
+
92
+ ## 🏗️ Architecture
93
+
94
+ ```mermaid
95
+ flowchart LR
96
+ A[Raw PJM load CSVs\n+ NOAA temperature] --> B[Preprocessing\nclean · pivot · normalize]
97
+ B --> C["Conditioning vector z\n(month, dow, holiday, Tmax, Tmin, spread)"]
98
+ C --> D["Transformer denoiser\n(DDPM, corrected ancestral sampler)"]
99
+ D --> E[Scalar bias\ncalibration]
100
+ E --> F["ResourceModel.sample()\nreal MW output"]
101
+
102
+ style D fill:#4c6ef5,color:#fff
103
+ style E fill:#f59f00,color:#fff
104
+ ```
105
+
106
+ Full detail — why transformer over MLP/CNN/attention, why the ancestral sampler over DDIM, why calibration is scalar not per-hour — is in [`docs/architecture.md`](docs/architecture.md), with every claim traced to a specific result file.
107
+
108
+ ## 📊 Evaluation Pipeline
109
+
110
+ Every comparison in this project uses the same 7-metric suite (`Hourly`, `FirstDiff`, `ACF`, `RMSE`, `Peak`, `Energy`, `Ramp` — all lower-is-better, defined in [`docs/evaluation.md`](docs/evaluation.md)), computed by `enerdatasynth.evaluation.metrics.compute_metrics`. The same suite backs the automatic failure-mode detector (`evaluation.failure_analysis`) and the benchmark script below.
111
+
112
+ <div align="center">
113
+ <img src="data/reports/figures/hourly_distribution_comparison_T100_H256_L4.png" alt="Hourly distribution comparison, real vs generated, currently-shipped model" width="600">
114
+ <br><sub>Per-hour distribution, real vs. generated — the currently auto-discovered default model (see Benchmark Results).</sub>
115
+ </div>
116
+
117
+ ## 🏆 Benchmark Results
118
+
119
+ Run yourself: `python examples/run_benchmark.py --run-id <run_id>`. Numbers below are a real run against the full 600-day validation set, using the model `enerdatasynth.model("pjm_load")` actually auto-discovers today (the best currently-trained checkpoint under this package's own preprocessing pipeline):
120
+
121
+ | Model | Hourly↓ | FirstDiff↓ | ACF↓ | RMSE↓ | Peak↓ | Energy↓ | Ramp↓ |
122
+ |---|---|---|---|---|---|---|---|
123
+ | AR | 1.3533 | 0.4216 | 0.1458 | 0.1802 | 0.7557 | 4.2780 | 0.1242 |
124
+ | ARMA | 1.3581 | 0.4218 | **0.1081** | 0.1864 | 0.7557 | 4.3916 | 0.1239 |
125
+ | ARIMA | 1.3540 | 0.1698 | 0.3125 | 0.1813 | 0.3451 | 4.2945 | **0.0074** |
126
+ | SARIMA | 1.3625 | 0.6389 | 0.2489 | 0.1894 | 0.6834 | 4.4981 | 0.2969 |
127
+ | **enerdatasynth (default)** | **0.0590** | **0.0792** | 0.1347 | **0.0484** | **0.0362** | **0.8099** | 0.0647 |
128
+
129
+ Wins outright on 5 of 7 metrics against every classical baseline. ARMA edges it out on ACF, ARIMA on Ramp — reported honestly rather than rounded up to "wins everything." A separate, larger comparison against the validated transformer architecture (trained on a different conditioning scheme — see the caveat in [`docs/architecture.md`](docs/architecture.md)) shows an even larger margin; see [`docs/research_notes.md`](docs/research_notes.md).
130
+
131
+ ## 📚 Documentation
132
+
133
+ | Page | Contents |
134
+ |---|---|
135
+ | [`docs/installation.md`](docs/installation.md) | Install paths, extras, reproducing exact results, GPU |
136
+ | [`docs/quickstart.md`](docs/quickstart.md) | Full walkthrough: preprocess → train → calibrate → sample |
137
+ | [`docs/architecture.md`](docs/architecture.md) | Model, sampler, schedule, calibration — with evidence |
138
+ | [`docs/api_reference.md`](docs/api_reference.md) | The full `model()`/`.set()`/`.sample()` contract |
139
+ | [`docs/evaluation.md`](docs/evaluation.md) | The 7-metric suite, calibration, benchmarking |
140
+ | [`docs/temperature_data.md`](docs/temperature_data.md) | NOAA temperature ingestion, zone-station mapping |
141
+ | [`docs/training.md`](docs/training.md) | Training new checkpoints, sweeps, configs |
142
+ | [`docs/packaging.md`](docs/packaging.md) | How this package is built, versioned, and verified |
143
+ | [`docs/developer_guide.md`](docs/developer_guide.md) | Project layout, testing, contribution standards |
144
+ | [`docs/extending.md`](docs/extending.md) | Adding a new resource type (e.g. wind) |
145
+ | [`docs/faq.md`](docs/faq.md) | Common questions |
146
+ | [`MODEL_CARD.md`](MODEL_CARD.md) | Training data, limitations, intended/out-of-scope use |
147
+ | [`MIGRATION.md`](MIGRATION.md) | Full audit trail behind every default in this package |
148
+
149
+ ## 🧪 Examples
150
+
151
+ ```bash
152
+ python examples/generate_samples.py # LoadDiffusionModel round trip
153
+ python examples/run_full_pipeline.py # tiny end-to-end demo
154
+ python examples/exploratory_data_analysis.py # inspecting the processed dataset
155
+ ```
156
+
157
+ ## 🔬 Research Summary
158
+
159
+ - **A critical sampler bug, found and fixed.** An earlier sampler silently corrupted every diffusion result (values pinned near clamp bounds). Diagnosed and fixed; the corrected ancestral sampler is the only one shipped.
160
+ - **Architecture selected by controlled comparison, not assumption.** MLP vs CNN vs attention vs transformer, identical data/conditioning/schedule/seed. Transformer won on generated-profile quality at ~6x fewer parameters than the MLP it replaced (110,401 vs 688,408).
161
+ - **A methodological finding:** in that comparison, the *lowest validation loss* (CNN) was not the *best generated quality* (transformer) — a caution against judging generative models by training loss alone.
162
+ - **Calibration, evidence-checked rather than assumed better.** A more elaborate per-hour bias correction was tested and found *worse* on two metrics than the simpler scalar correction that's actually shipped.
163
+
164
+ Full detail, entirely traceable to specific files, is in [`docs/research_notes.md`](docs/research_notes.md) and [`MIGRATION.md`](MIGRATION.md).
165
+
166
+ ## 📄 Citation
167
+
168
+ No accompanying paper has been published yet. If you use this software, please cite the repository:
169
+
170
+ ```bibtex
171
+ @software{enerdatasynth,
172
+ title = {enerdatasynth: A Research SDK for Synthetic PJM Energy Resource Profiles},
173
+ author = {Sharma, Sonakshi},
174
+ year = {2026},
175
+ note = {Software}
176
+ }
177
+ ```
178
+
179
+ ## ⚖️ License
180
+
181
+ [MIT](LICENSE) © 2026 Sonakshi Sharma
182
+
183
+ ## 🗺️ Roadmap
184
+
185
+ - [ ] Re-validate the transformer/calibration pipeline under the package's current one-hot conditioning (see the caveat in [`docs/architecture.md`](docs/architecture.md))
186
+ - [ ] Multi-zone support (17 zones explored in `data/research_history/scripts/zone_expansion.py`, not yet promoted)
187
+ - [ ] A friendlier `set()` for `spread` (currently defaults to the training-set mean; see [`docs/api_reference.md`](docs/api_reference.md))
188
+ - [ ] Wind generation, once real data/research exists to back it (see [`docs/extending.md`](docs/extending.md))
189
+ - [ ] Re-sweep hyperparameters for the transformer architecture specifically (the current defaults are inherited from `data/research_history/`, never re-swept against this package's own pipeline)
190
+
191
+ Tracked in detail in [`data/reports/future_experiments.md`](data/reports/future_experiments.md) and [`MIGRATION.md`](MIGRATION.md).
192
+
193
+ ---
194
+
195
+ <div align="center">
196
+ <sub>Implementation details, folder structure, and the full conditioning-vector spec have moved to <a href="docs/developer_guide.md">docs/developer_guide.md</a> — this file is a landing page, not a developer reference.</sub>
197
+ </div>
@@ -0,0 +1,64 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "enerdatasynth"
7
+ dynamic = ["version"]
8
+ description = "A research SDK for synthetic PJM energy resource profiles (electric load today; a common interface for future resource types)"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [
12
+ { name = "Sonakshi Sharma", email = "s.sonakshi2006@gmail.com" },
13
+ ]
14
+ requires-python = ">=3.9"
15
+ keywords = ["diffusion-model", "synthetic-data", "electric-load", "pjm", "time-series", "energy", "generative-model", "research-sdk"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
25
+ ]
26
+
27
+ dependencies = [
28
+ "numpy",
29
+ "pandas",
30
+ "torch",
31
+ "scikit-learn",
32
+ "statsmodels",
33
+ "holidays",
34
+ "matplotlib",
35
+ "requests",
36
+ "tqdm",
37
+ "pyarrow",
38
+ "pyyaml",
39
+ ]
40
+
41
+ [project.optional-dependencies]
42
+ viz = [
43
+ "seaborn>=0.13",
44
+ ]
45
+ dev = [
46
+ "pytest",
47
+ "pytest-cov",
48
+ "ruff",
49
+ "mypy",
50
+ "build",
51
+ "twine",
52
+ ]
53
+
54
+ [project.scripts]
55
+ enerdatasynth = "enerdatasynth.cli:main"
56
+
57
+ [tool.setuptools.dynamic]
58
+ version = { attr = "enerdatasynth.__version__" }
59
+
60
+ [tool.setuptools.packages.find]
61
+ where = ["src"]
62
+
63
+ [tool.pytest.ini_options]
64
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,71 @@
1
+ """
2
+ enerdatasynth
3
+ ================
4
+ A research SDK for synthetic PJM energy resource profiles. Today that
5
+ means electric load; the public interface is designed so a future
6
+ resource type (e.g. wind) can be added through the same contract —
7
+ see `docs/extending.md`.
8
+
9
+ Primary entry point — you should not need anything else for everyday
10
+ generation:
11
+
12
+ import enerdatasynth
13
+
14
+ load = enerdatasynth.model("pjm_load", zone="PS")
15
+ load.set(month=7, Tmax=86, Tmin=70, weekday=True, holiday=False)
16
+ samples = load.sample(n=100) # (100, 24) array, MW
17
+
18
+ `enerdatasynth.model(...)` never exposes tensors, architecture
19
+ classes, checkpoint paths, or the fact that a DDPM/transformer is
20
+ involved at all. See `docs/api_reference.md` for the full contract.
21
+
22
+ For research/development use — training new checkpoints, computing
23
+ evaluation metrics, running calibration — this top-level namespace
24
+ also re-exports the lower-level building blocks:
25
+
26
+ from enerdatasynth import RunConfig, train_one_config, LoadDiffusionModel
27
+
28
+ For anything not re-exported here, import directly from the relevant
29
+ submodule (`enerdatasynth.data`, `enerdatasynth.models`,
30
+ `enerdatasynth.diffusion`, `enerdatasynth.training`,
31
+ `enerdatasynth.sampling`, `enerdatasynth.evaluation`,
32
+ `enerdatasynth.visualization`, `enerdatasynth.generators`,
33
+ `enerdatasynth.utils`).
34
+
35
+ Production code in this package never imports from `research_history/`
36
+ — that directory exists only for reproducibility/provenance and is
37
+ not installed as part of the package.
38
+ """
39
+
40
+ from enerdatasynth.sdk import model, ResourceModel
41
+ from enerdatasynth.utils.config import RunConfig, set_seed, DEFAULT_ARCHITECTURE
42
+ from enerdatasynth.models import build_denoiser, TransformerDenoiser, SimpleMLPDenoiser
43
+ from enerdatasynth.diffusion.ddpm import DDPM
44
+ from enerdatasynth.data.preprocess import run_preprocessing
45
+ from enerdatasynth.training.train import train_one_config
46
+ from enerdatasynth.sampling.generate import generate_for_run
47
+ from enerdatasynth.evaluation.metrics import compute_metrics
48
+ from enerdatasynth.evaluation.calibration import calibrate_checkpoint
49
+ from enerdatasynth.generators import BaseGenerator, LoadDiffusionModel
50
+
51
+ __version__ = "0.4.1"
52
+
53
+ __all__ = [
54
+ "__version__",
55
+ "model",
56
+ "ResourceModel",
57
+ "BaseGenerator",
58
+ "LoadDiffusionModel",
59
+ "RunConfig",
60
+ "set_seed",
61
+ "DEFAULT_ARCHITECTURE",
62
+ "build_denoiser",
63
+ "TransformerDenoiser",
64
+ "SimpleMLPDenoiser",
65
+ "DDPM",
66
+ "run_preprocessing",
67
+ "train_one_config",
68
+ "generate_for_run",
69
+ "calibrate_checkpoint",
70
+ "compute_metrics",
71
+ ]