smhi2epw 1.1.0__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.
- smhi2epw-1.1.0/.readthedocs.yaml +18 -0
- smhi2epw-1.1.0/LICENSE +21 -0
- smhi2epw-1.1.0/MANIFEST.in +9 -0
- smhi2epw-1.1.0/PKG-INFO +502 -0
- smhi2epw-1.1.0/README.md +455 -0
- smhi2epw-1.1.0/docs/api.rst +48 -0
- smhi2epw-1.1.0/docs/bibliography.rst +36 -0
- smhi2epw-1.1.0/docs/cli.rst +68 -0
- smhi2epw-1.1.0/docs/conf.py +66 -0
- smhi2epw-1.1.0/docs/energyplus.rst +106 -0
- smhi2epw-1.1.0/docs/index.rst +48 -0
- smhi2epw-1.1.0/docs/installation.rst +74 -0
- smhi2epw-1.1.0/docs/internals.rst +80 -0
- smhi2epw-1.1.0/docs/limitations.rst +63 -0
- smhi2epw-1.1.0/docs/notebooks/00_getting_started.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/01_inspect_an_epw.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/02_compare_locations.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/03_compare_years.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/04_identify_heatwaves.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/05_compare_amy_to_tmy.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/06_data_quality_and_gap_filling.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/07_solar_components.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/08_batch_generation.nblink +1 -0
- smhi2epw-1.1.0/docs/notebooks/09_run_energyplus.nblink +3 -0
- smhi2epw-1.1.0/docs/pipeline.rst +109 -0
- smhi2epw-1.1.0/docs/provenance.rst +105 -0
- smhi2epw-1.1.0/docs/python_api.rst +73 -0
- smhi2epw-1.1.0/docs/quickstart.rst +75 -0
- smhi2epw-1.1.0/docs/troubleshooting.rst +74 -0
- smhi2epw-1.1.0/docs/tutorials.rst +33 -0
- smhi2epw-1.1.0/docs/weather_recovery.rst +371 -0
- smhi2epw-1.1.0/examples/00_getting_started.ipynb +151 -0
- smhi2epw-1.1.0/examples/01_inspect_an_epw.ipynb +215 -0
- smhi2epw-1.1.0/examples/02_compare_locations.ipynb +171 -0
- smhi2epw-1.1.0/examples/03_compare_years.ipynb +168 -0
- smhi2epw-1.1.0/examples/04_identify_heatwaves.ipynb +154 -0
- smhi2epw-1.1.0/examples/05_compare_amy_to_tmy.ipynb +204 -0
- smhi2epw-1.1.0/examples/06_data_quality_and_gap_filling.ipynb +166 -0
- smhi2epw-1.1.0/examples/07_solar_components.ipynb +190 -0
- smhi2epw-1.1.0/examples/08_batch_generation.ipynb +289 -0
- smhi2epw-1.1.0/examples/09_run_energyplus.ipynb +218 -0
- smhi2epw-1.1.0/examples/README.md +77 -0
- smhi2epw-1.1.0/examples/data/completeness_2026-10-05.json +316 -0
- smhi2epw-1.1.0/examples/data/pvlib_validation_2026-10-06.json +1183 -0
- smhi2epw-1.1.0/examples/data/solar_validation_2026-10-06.json +839 -0
- smhi2epw-1.1.0/examples/support/energyplus.py +187 -0
- smhi2epw-1.1.0/examples/support/single_zone.idf +460 -0
- smhi2epw-1.1.0/pyproject.toml +92 -0
- smhi2epw-1.1.0/scripts/install_energyplus.sh +50 -0
- smhi2epw-1.1.0/setup.cfg +4 -0
- smhi2epw-1.1.0/src/smhi2epw/__init__.py +46 -0
- smhi2epw-1.1.0/src/smhi2epw/automatic.py +445 -0
- smhi2epw-1.1.0/src/smhi2epw/cli.py +203 -0
- smhi2epw-1.1.0/src/smhi2epw/compiler.py +651 -0
- smhi2epw-1.1.0/src/smhi2epw/constants.py +140 -0
- smhi2epw-1.1.0/src/smhi2epw/errors.py +45 -0
- smhi2epw-1.1.0/src/smhi2epw/export.py +524 -0
- smhi2epw-1.1.0/src/smhi2epw/gap_recovery.py +415 -0
- smhi2epw-1.1.0/src/smhi2epw/ingestion.py +1083 -0
- smhi2epw-1.1.0/src/smhi2epw/processing.py +951 -0
- smhi2epw-1.1.0/src/smhi2epw/provenance.py +79 -0
- smhi2epw-1.1.0/src/smhi2epw/py.typed +0 -0
- smhi2epw-1.1.0/src/smhi2epw/reader.py +339 -0
- smhi2epw-1.1.0/src/smhi2epw/reanalysis.py +120 -0
- smhi2epw-1.1.0/src/smhi2epw/solar.py +222 -0
- smhi2epw-1.1.0/src/smhi2epw.egg-info/PKG-INFO +502 -0
- smhi2epw-1.1.0/src/smhi2epw.egg-info/SOURCES.txt +88 -0
- smhi2epw-1.1.0/src/smhi2epw.egg-info/dependency_links.txt +1 -0
- smhi2epw-1.1.0/src/smhi2epw.egg-info/entry_points.txt +2 -0
- smhi2epw-1.1.0/src/smhi2epw.egg-info/requires.txt +30 -0
- smhi2epw-1.1.0/src/smhi2epw.egg-info/top_level.txt +1 -0
- smhi2epw-1.1.0/tests/fixtures/__init__.py +1 -0
- smhi2epw-1.1.0/tests/fixtures/provider_replay.py +326 -0
- smhi2epw-1.1.0/tests/test_automatic.py +379 -0
- smhi2epw-1.1.0/tests/test_automatic_physics.py +366 -0
- smhi2epw-1.1.0/tests/test_cli_cache.py +78 -0
- smhi2epw-1.1.0/tests/test_cloud_units.py +158 -0
- smhi2epw-1.1.0/tests/test_documentation.py +228 -0
- smhi2epw-1.1.0/tests/test_energyplus.py +157 -0
- smhi2epw-1.1.0/tests/test_gap_recovery.py +494 -0
- smhi2epw-1.1.0/tests/test_ingestion_metadata.py +124 -0
- smhi2epw-1.1.0/tests/test_network.py +100 -0
- smhi2epw-1.1.0/tests/test_pipeline.py +717 -0
- smhi2epw-1.1.0/tests/test_provenance.py +230 -0
- smhi2epw-1.1.0/tests/test_provider_replay.py +103 -0
- smhi2epw-1.1.0/tests/test_reader.py +260 -0
- smhi2epw-1.1.0/tests/test_solar_decomposition.py +93 -0
- smhi2epw-1.1.0/tests/test_solar_geometry.py +103 -0
- smhi2epw-1.1.0/tests/test_solar_sources.py +183 -0
- smhi2epw-1.1.0/tests/test_target_pressure.py +103 -0
smhi2epw-1.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 smhi2epw contributors
|
|
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,9 @@
|
|
|
1
|
+
include .readthedocs.yaml
|
|
2
|
+
recursive-include docs *.nblink *.py *.rst
|
|
3
|
+
recursive-include examples *.ipynb *.md
|
|
4
|
+
include examples/data/completeness_2026-10-05.json
|
|
5
|
+
recursive-include examples/support *.py *.idf
|
|
6
|
+
include scripts/install_energyplus.sh
|
|
7
|
+
include examples/data/solar_validation_2026-10-06.json
|
|
8
|
+
recursive-include tests/fixtures *.py
|
|
9
|
+
include examples/data/pvlib_validation_2026-10-06.json
|
smhi2epw-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smhi2epw
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Build complete-year EnergyPlus weather files from SMHI observations, with documented gap recovery.
|
|
5
|
+
Author: smhi2epw contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/SB-Chalmers/smhi2epw
|
|
8
|
+
Project-URL: Repository, https://github.com/SB-Chalmers/smhi2epw.git
|
|
9
|
+
Project-URL: Issues, https://github.com/SB-Chalmers/smhi2epw/issues
|
|
10
|
+
Keywords: smhi,epw,energyplus,weather,amy,strang,metobs
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Requires-Dist: pandas>=1.5.3
|
|
22
|
+
Requires-Dist: requests>=2.28
|
|
23
|
+
Requires-Dist: pvlib<0.17,>=0.16.1
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
26
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
27
|
+
Requires-Dist: pandas-stubs>=2.2; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
29
|
+
Requires-Dist: ruff>=0.9; extra == "dev"
|
|
30
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
31
|
+
Requires-Dist: types-requests>=2.28; extra == "dev"
|
|
32
|
+
Provides-Extra: test
|
|
33
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
34
|
+
Provides-Extra: docs
|
|
35
|
+
Requires-Dist: nbsphinx>=0.9; extra == "docs"
|
|
36
|
+
Requires-Dist: nbsphinx-link>=1.3; extra == "docs"
|
|
37
|
+
Requires-Dist: sphinx>=7.2; extra == "docs"
|
|
38
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
|
|
39
|
+
Provides-Extra: tutorials
|
|
40
|
+
Requires-Dist: ipykernel>=6.25; extra == "tutorials"
|
|
41
|
+
Requires-Dist: ipython>=8.12; extra == "tutorials"
|
|
42
|
+
Requires-Dist: jupyterlab>=4.0; extra == "tutorials"
|
|
43
|
+
Requires-Dist: matplotlib>=3.7; extra == "tutorials"
|
|
44
|
+
Requires-Dist: nbclient>=0.9; extra == "tutorials"
|
|
45
|
+
Requires-Dist: nbformat>=5.9; extra == "tutorials"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
# smhi2epw
|
|
49
|
+
|
|
50
|
+
Build a complete-year EnergyPlus weather file (`.epw`) from Swedish weather
|
|
51
|
+
observations. `smhi2epw` combines SMHI station measurements with STRÅNG solar
|
|
52
|
+
data, checks gaps, and can recover missing hours with nearby stations, bounded
|
|
53
|
+
time profiles, and same-year ERA5 data. It writes a provenance receipt so you
|
|
54
|
+
can see which sources and recovery steps contributed to each file.
|
|
55
|
+
|
|
56
|
+
Use it from the command line or Python for building-energy simulations,
|
|
57
|
+
weather-year comparisons, and reproducible research. It supports completed
|
|
58
|
+
years from 1999 onward and writes 8,760 rows for common years or 8,784 for leap
|
|
59
|
+
years. Automatic recovery is the default; strict mode is available when missing
|
|
60
|
+
source data should stop the build.
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
Install the latest published version from PyPI:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python -m pip install smhi2epw
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Python 3.11 or newer is required. The package installs its solar and data
|
|
71
|
+
processing dependencies. Building a new weather file fetches data from SMHI and
|
|
72
|
+
may use Open-Meteo ERA5 when automatic recovery needs it. Reading and analysing
|
|
73
|
+
an existing EPW file works offline.
|
|
74
|
+
|
|
75
|
+
For the notebooks and development tools, clone the repository and install the
|
|
76
|
+
extras:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone https://github.com/SB-Chalmers/smhi2epw.git
|
|
80
|
+
cd smhi2epw
|
|
81
|
+
python -m pip install -e ".[tutorials]"
|
|
82
|
+
jupyter lab examples/
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
See the [installation guide](docs/installation.rst) for environment setup and
|
|
86
|
+
the [online documentation](https://smhi2epw.readthedocs.io/) for the full API,
|
|
87
|
+
methods, and tutorials. For contributors, install `.[dev,docs,tutorials]`.
|
|
88
|
+
|
|
89
|
+
## Usage
|
|
90
|
+
|
|
91
|
+
### Command line
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Explicit station id:
|
|
95
|
+
smhi2epw 2023 gothenburg_2023.epw --station 71420 --city Gothenburg --utc-offset 1
|
|
96
|
+
|
|
97
|
+
# Or auto-select the nearest qualifying station from coordinates:
|
|
98
|
+
smhi2epw 2023 gothenburg_2023.epw --lat 57.7156 --lon 11.9924 --city Gothenburg
|
|
99
|
+
|
|
100
|
+
# Force a fresh fetch, ignoring the on-disk cache:
|
|
101
|
+
smhi2epw 2023 gothenburg_2023.epw --station 71420 --refresh
|
|
102
|
+
|
|
103
|
+
# Retain source-coverage and gap failures:
|
|
104
|
+
smhi2epw 2023 gothenburg_2023.epw --station 71420 --weather-policy strict
|
|
105
|
+
|
|
106
|
+
# Change the 50 km automatic pyranometer limit:
|
|
107
|
+
smhi2epw 2023 gothenburg_2023.epw --station 71420 \
|
|
108
|
+
--radiation-station-max-distance 25
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The positional arguments are `year` and `output`. Provide either `--station`,
|
|
112
|
+
or both `--lat` and `--lon` (which also become the STRÅNG solar query point).
|
|
113
|
+
|
|
114
|
+
### Python API
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from smhi2epw import compile_epw
|
|
118
|
+
from smhi2epw.compiler import EPWConfig
|
|
119
|
+
|
|
120
|
+
result = compile_epw(
|
|
121
|
+
EPWConfig(
|
|
122
|
+
year=2023,
|
|
123
|
+
output_path="gothenburg_2023.epw",
|
|
124
|
+
station_id=71420, # or omit and pass latitude/longitude instead
|
|
125
|
+
city="Gothenburg",
|
|
126
|
+
latitude=57.7156, # nearest-station search + solar query point
|
|
127
|
+
longitude=11.9924,
|
|
128
|
+
utc_offset=1.0, # Local Standard Time; DST ignored
|
|
129
|
+
cache_dir=".smhi_cache",
|
|
130
|
+
refresh=False, # set True to bypass the cache
|
|
131
|
+
weather_policy="automatic", # default; strict disables ERA5 recovery
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
print(result.rows, "rows,", f"{result.interpolated_fraction:.2%} interpolated")
|
|
136
|
+
if result.coordinate_distance_km is not None:
|
|
137
|
+
print(f"{result.coordinate_distance_km:.1f} km from the primary station")
|
|
138
|
+
print("cloud data available:", result.report.cloud_available)
|
|
139
|
+
print(
|
|
140
|
+
"max solar energy-balance residual:",
|
|
141
|
+
result.report.energy_balance_max_residual,
|
|
142
|
+
"W/m^2",
|
|
143
|
+
)
|
|
144
|
+
print("diurnally filled hours:", result.report.diurnal_filled_hours)
|
|
145
|
+
print("recovery warnings:", result.report.warnings)
|
|
146
|
+
# Automatic policy writes gothenburg_2023.epw.json by default.
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Reading and analysing EPW files
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from smhi2epw import read_epw
|
|
153
|
+
|
|
154
|
+
weather = read_epw("gothenburg_2023.epw")
|
|
155
|
+
print(weather[["dry_bulb", "ghi", "wind_speed"]].describe())
|
|
156
|
+
print(weather.attrs["location"])
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`read_epw()` names all 35 fields, recognizes field-specific missing tokens, and
|
|
160
|
+
accepts both chronological AMYs and composite-year TMYs. The original eight
|
|
161
|
+
headers remain available in `weather.attrs["header"]`.
|
|
162
|
+
|
|
163
|
+
## Tutorials and documentation
|
|
164
|
+
|
|
165
|
+
The [numbered notebook curriculum](examples/README.md) starts with one minimal
|
|
166
|
+
weather file, then covers EPW inspection, location/year comparisons, heat-wave
|
|
167
|
+
detection, AMY-versus-TMY analysis, gap filling, solar components, and batch
|
|
168
|
+
generation. Network and external-data requirements are stated at the top of
|
|
169
|
+
every notebook.
|
|
170
|
+
|
|
171
|
+
### Build and view the documentation locally
|
|
172
|
+
|
|
173
|
+
From a source checkout, install the documentation and tutorial dependencies
|
|
174
|
+
into your active virtual environment:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
python -m pip install -e ".[docs,tutorials]"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Build the complete Sphinx site, including the API reference and rendered
|
|
181
|
+
notebooks, with warnings treated as errors:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
python -m sphinx -W --keep-going -b html docs docs/_build/html
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The generated home page is `docs/_build/html/index.html`. The published site is
|
|
188
|
+
available at [smhi2epw.readthedocs.io](https://smhi2epw.readthedocs.io/). For the most reliable
|
|
189
|
+
navigation and search behavior, serve the directory over a local HTTP server:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
python -m http.server 8000 --directory docs/_build/html
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Then open [http://localhost:8000](http://localhost:8000) in a browser. Stop the
|
|
196
|
+
server with <kbd>Ctrl</kbd>+<kbd>C</kbd>. You can also open the HTML file directly
|
|
197
|
+
with `open docs/_build/html/index.html` on macOS, `xdg-open
|
|
198
|
+
docs/_build/html/index.html` on Linux, or `start docs\_build\html\index.html` in
|
|
199
|
+
Windows Command Prompt.
|
|
200
|
+
|
|
201
|
+
Notebook outputs are not executed during the documentation build, so building
|
|
202
|
+
the site does not contact SMHI, Open-Meteo or OneBuilding. The generated
|
|
203
|
+
`docs/_build/` tree is local-only and ignored by Git.
|
|
204
|
+
|
|
205
|
+
## Pipeline
|
|
206
|
+
|
|
207
|
+
| Layer | Responsibility |
|
|
208
|
+
| --- | --- |
|
|
209
|
+
| Ingestion | Concurrent primary-source retrieval, year-aware station metadata, quality filtering, request retries, buffered hourly UTC grid, local caching, same-year ERA5 fallback |
|
|
210
|
+
| Processing | Short interpolation, assessed donor transfer with optional median bias correction, bounded daily profiles, circular winds, source warnings and physical checks, pressure conversion, dew point and longwave derivation, closed solar components |
|
|
211
|
+
| Export | Exact UTC→LST constant shift, hour 1–24 formatting, standards-compliant headers and 35-field rows, strict range/calendar validation, atomic 8760/8784-row output, recovery comments and JSON provenance |
|
|
212
|
+
|
|
213
|
+
## Notes
|
|
214
|
+
|
|
215
|
+
- Required gaps of 1–3 hours use interpolation, circularly for wind direction.
|
|
216
|
+
Longer gaps and unfillable short wind gaps first use assessed same-year
|
|
217
|
+
donors. Remaining gaps up to 48 hours use previous/next valid daily profiles
|
|
218
|
+
with endpoint correction and 50/50 mixing, then automatic mode uses ERA5.
|
|
219
|
+
Strict mode permits donors only when explicitly enabled and has no ERA5
|
|
220
|
+
fallback. Neither policy extends temporal filling beyond 48 hours.
|
|
221
|
+
Solar recovery has no donor stage.
|
|
222
|
+
- Observations are filtered by MetObs quality flag; only accepted grades
|
|
223
|
+
(`G`, `Y`) are used, others are treated as gaps.
|
|
224
|
+
- STRÅNG `-999` missing sentinels are removed and back-filled with a
|
|
225
|
+
diurnal-aware (same-hour, day-to-day) interpolation that preserves the solar
|
|
226
|
+
cycle.
|
|
227
|
+
- When total cloud cover (MetObs parameter 16) is available, it populates total
|
|
228
|
+
sky cover and acts as a documented proxy only in the longwave IR calculation.
|
|
229
|
+
Opaque sky cover remains missing because SMHI does not provide it. ERA5 can
|
|
230
|
+
supply missing cloud cover only at hours where it replaces required
|
|
231
|
+
meteorology or GHI; usable cloud values and other hours are preserved.
|
|
232
|
+
- Daylight Savings Time is intentionally ignored to keep solar angles
|
|
233
|
+
continuous. A small UTC buffer is ingested around each year end so the LST
|
|
234
|
+
shift uses real observations at the boundary.
|
|
235
|
+
- Raw payloads are cached on disk (`cache_dir`) so repeated compilations for the
|
|
236
|
+
same station/year are idempotent and avoid redundant API load.
|
|
237
|
+
- STRÅNG parameter semantics are resolved against the live `strang1g` v1 API:
|
|
238
|
+
`117` = global horizontal, `118` = direct *normal*, `121` = direct beam on
|
|
239
|
+
the horizontal plane; diffuse horizontal is derived as `117 − 121`.
|
|
240
|
+
Direct-horizontal parameter 121 starts on 18 April 2017; parameter 118
|
|
241
|
+
(DNI) is requested throughout the supported history from 1999. Before
|
|
242
|
+
parameter 121 is available, horizontal beam is projected from instantaneous
|
|
243
|
+
DNI before adjacent-sample averaging. The continuous Erbs-Driesse form of
|
|
244
|
+
the Erbs (1982) model is used only when usable direct components are absent;
|
|
245
|
+
see the
|
|
246
|
+
[SMHI extraction guide](https://strang.smhi.se/extraction/index.php).
|
|
247
|
+
- STRÅNG values are instantaneous irradiance at the full hour. The pipeline
|
|
248
|
+
converts them to EPW interval-averaged irradiance (preceding-hour mean) by
|
|
249
|
+
averaging adjacent samples. Supplied DNI and horizontal beam retain their
|
|
250
|
+
separate interval means, including below five degrees solar elevation.
|
|
251
|
+
The five-degree guard applies only when DNI must be inferred by dividing
|
|
252
|
+
horizontal radiation by solar geometry. Interval closure is `GHI = DHI +
|
|
253
|
+
horizontal beam`; mean DNI times a midpoint cosine is an approximation.
|
|
254
|
+
- Solar geometry uses pvlib's NREL SPA `nrel_numpy` method and geometric
|
|
255
|
+
(unrefracted) zenith. `delta_t=None` lets pvlib calculate the terrestrial-time
|
|
256
|
+
correction for each UTC year/month. Extraterrestrial irradiance uses pvlib's
|
|
257
|
+
`asce` method with a 1367 W/m² solar constant. Five-minute integration over
|
|
258
|
+
the preceding hour supplies interval geometry and caps. Both weather
|
|
259
|
+
policies use these fixed solar methods.
|
|
260
|
+
- `result.report.solar_source` reports which solar path was used:
|
|
261
|
+
`"strang"` (supplied STRÅNG DNI and supplied/projected horizontal beam),
|
|
262
|
+
`"measured+strang_partition"` (nearby Sol station GHI with the STRÅNG partition),
|
|
263
|
+
`"strang_ghi+erbs"` (Erbs-Driesse on STRÅNG GHI when direct components are absent),
|
|
264
|
+
`"measured+erbs"` (Erbs-Driesse on measured GHI), `"era5"` (ERA5 solar fallback),
|
|
265
|
+
or `"mixed"` (multiple solar sources). The `+erbs` labels retain their existing
|
|
266
|
+
spelling as Erbs-family identifiers. New JSON sidecars include top-level
|
|
267
|
+
`pvlib_version` alongside the package version and source hashes.
|
|
268
|
+
- Automatic pyranometer discovery is limited to 50 km by default. If its data
|
|
269
|
+
cannot satisfy the 48-hour policy, compilation falls back to STRÅNG.
|
|
270
|
+
Explicitly requested radiation stations fail in strict mode; automatic mode
|
|
271
|
+
records the failure and continues solar recovery.
|
|
272
|
+
- Only whole-hour Local Standard Time offsets are supported. This covers the
|
|
273
|
+
Nordic STRÅNG region without silently resampling hourly source data.
|
|
274
|
+
- A warning is logged when the solar query point is outside Sweden (~55–69.5°N,
|
|
275
|
+
10–24.5°E), where STRÅNG accuracy degrades (RMSD up to 30–40% for GHI).
|
|
276
|
+
|
|
277
|
+
## Validation of the pvlib solar methods
|
|
278
|
+
|
|
279
|
+
On **6 October 2026**, the rebuilt wheel with pvlib 0.16.1 passed **261 offline
|
|
280
|
+
checks**, including notebook 07, and **7 required EnergyPlus tests**. Notebook 09
|
|
281
|
+
also completed all **8,760 hours with zero engine warnings**. The Python 3.11
|
|
282
|
+
minimum-dependency run passed 257 checks; four optional notebook tests were
|
|
283
|
+
skipped there and executed in the full wheel environment. Static checks and the
|
|
284
|
+
HTML documentation build passed, with 68 documentation doctests and no warnings.
|
|
285
|
+
Explicit timestamp keywords keep the declared pandas 1.5.3 floor working.
|
|
286
|
+
|
|
287
|
+
Six paired engineering cases used identical provider payloads, building models,
|
|
288
|
+
and the pinned EnergyPlus engine. Five cases had identical annual heating,
|
|
289
|
+
cooling and window-solar totals. For historical 2016 data, SPA changed the
|
|
290
|
+
horizontal beam projected from instantaneous DNI: annual heating changed by
|
|
291
|
+
**−0.1154%**, cooling by **−0.1385%**, and window solar by **−0.1235%**. GHI and DNI
|
|
292
|
+
were unchanged; the largest hourly DHI change was 4 Wh/m². Separate tests compare
|
|
293
|
+
classic Erbs and Erbs-Driesse at identical geometry, confirming a maximum
|
|
294
|
+
**0.000429 diffuse-fraction difference**, below the documented 0.0005 bound.
|
|
295
|
+
|
|
296
|
+
Warm annual geometry calls on this machine took about **17 ms** for zenith and
|
|
297
|
+
**224–226 ms** for preceding-hour integration, versus about 0.8 ms and 13 ms
|
|
298
|
+
previously. The existing five-minute integration is retained without caching.
|
|
299
|
+
|
|
300
|
+
The [portable validation summary](examples/data/pvlib_validation_2026-10-06.json)
|
|
301
|
+
records wheel/source/input identities, all six cases, and runtime measurements.
|
|
302
|
+
The rebuilt wheel generated byte-identical weather for the paired comparison;
|
|
303
|
+
its consumer checks were also run separately. These are numerical and consumer
|
|
304
|
+
regressions, not independent evidence of site-weather accuracy. The archived validation summary records the source revision and test evidence;
|
|
305
|
+
GitHub-hosted release checks run again for each release tag.
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
## Completeness-run results
|
|
309
|
+
|
|
310
|
+
The EPSM national measurement-year weather runs on **5 October 2026** covered
|
|
311
|
+
requested municipality/year jobs from **2017–2024**:
|
|
312
|
+
|
|
313
|
+
| Run | Weather policy / source commit | Completed | Failed |
|
|
314
|
+
| --- | --- | ---: | ---: |
|
|
315
|
+
| v1 | Historical bounded temporal filling; Git commit unrecorded | 32 / 85 | 53 |
|
|
316
|
+
| v2 | Opt-in raw nearby observations, `4861f9e`; no ERA5 | 124 / 128 | 4 |
|
|
317
|
+
| v3 | Automatic assessed donors + ERA5, `5930c07` | **128 / 128** | **0** |
|
|
318
|
+
|
|
319
|
+
Preparation expanded the job set from 85 to 128; v2 and v3 use the same job
|
|
320
|
+
manifest. V1/v2 figures are retained historical status counts; their EPWs are
|
|
321
|
+
no longer available locally. On 6 October, all 128 v3 files were independently
|
|
322
|
+
rechecked against recorded hashes, exact ordered calendars, 35-field rows,
|
|
323
|
+
finite required fields, export ranges and dew-point consistency. They contain
|
|
324
|
+
112 common-year and 16 leap-year files: **1,121,664 exported hours**.
|
|
325
|
+
|
|
326
|
+
Completeness includes reconstruction. All 128 v3 files are
|
|
327
|
+
`mixed_reconstructed`: 101 use donor meteorology, 35 use ERA5 meteorology, and
|
|
328
|
+
126 use temporal filling. These groups overlap. **25 / 128** reconstruct more
|
|
329
|
+
than 5% of required meteorological cells; the fraction ranges from 0.0114% to
|
|
330
|
+
100%. This statistic uses five meteorological variables on the buffered UTC
|
|
331
|
+
grid and excludes solar; exported-hour source shares are reported separately.
|
|
332
|
+
Five percent is a reporting aid, not a validated acceptance threshold. Review
|
|
333
|
+
source fractions and warnings before calibration or extreme-event analysis.
|
|
334
|
+
Successful export establishes complete weather inputs, not local weather accuracy.
|
|
335
|
+
The archived outputs predate the solar and actual-year header corrections and
|
|
336
|
+
the pvlib implementation. Their counts describe the recorded source revisions.
|
|
337
|
+
Regenerate inputs in a new directory to apply the current solar methods and
|
|
338
|
+
EnergyPlus acceptance checks.
|
|
339
|
+
|
|
340
|
+
The [portable evidence summary](examples/data/completeness_2026-10-05.json)
|
|
341
|
+
includes policies, source identities, artifact hashes, warning counts and
|
|
342
|
+
validation definitions. [Notebook 08](examples/08_batch_generation.ipynb)
|
|
343
|
+
reads it offline and records reconstruction diagnostics for new batches. The
|
|
344
|
+
[recovery guide](docs/weather_recovery.rst) explains the separate earlier
|
|
345
|
+
52-of-53 failed-job replay and the current run's limitations.
|
|
346
|
+
|
|
347
|
+
## Recorded pre-pvlib sensitivity check
|
|
348
|
+
|
|
349
|
+
A recorded check on **6 October 2026**, after the solar fixes (`050d195`) and
|
|
350
|
+
before the pvlib implementation, rebuilt references and two outage cases for
|
|
351
|
+
each selected weather-year. All **9 annual
|
|
352
|
+
EnergyPlus simulations** completed with zero warnings; paired models and
|
|
353
|
+
unmasked EPW rows were identical. One fixed, illustrative 100 m² ideal-load
|
|
354
|
+
building was used. Changes below are signed differences from its corrected
|
|
355
|
+
reference, not national uncertainty bounds or HVAC electricity.
|
|
356
|
+
|
|
357
|
+
| Weather-year | 168 h solar outage: annual sensible cooling change | 48 h meteorology outage at temperature maximum: cooling change | Temperature MAE in hidden hours |
|
|
358
|
+
| --- | ---: | ---: | ---: |
|
|
359
|
+
| Gothenburg 2016 | +0.900 kWh/m² (+4.06%) | +0.102% | 0.923 °C |
|
|
360
|
+
| Luleå 2023 | +0.618 kWh/m² (+3.04%) | +0.094% | 1.142 °C |
|
|
361
|
+
| Gothenburg 2024 | +1.259 kWh/m² (+6.79%) | −0.166% | 0.856 °C |
|
|
362
|
+
|
|
363
|
+
Nighttime zeros split the solar outages into daylight gaps filled by daily
|
|
364
|
+
profiles. Meteorology used assessed fixed donors, with circular temporal wind
|
|
365
|
+
recovery where a donor was rejected. None of these six outages selected ERA5;
|
|
366
|
+
its transport and recovery are covered separately by deterministic integration
|
|
367
|
+
tests. The reference solar partition still includes modelled STRÅNG radiation.
|
|
368
|
+
These results describe the selected weather, model and masks; they do not
|
|
369
|
+
independently validate DNI/DHI or establish a universal donor accuracy.
|
|
370
|
+
|
|
371
|
+
The [portable sensitivity summary](examples/data/solar_validation_2026-10-06.json)
|
|
372
|
+
contains assumptions, recovery decisions, source/model hashes and validation
|
|
373
|
+
checks. Notebook 08 reads its results offline alongside completeness evidence.
|
|
374
|
+
Both evidence JSON files and all reported numbers retain their recorded source
|
|
375
|
+
identities. They have not been regenerated with pvlib. The earlier frozen
|
|
376
|
+
campaign is also retained with its original source identity.
|
|
377
|
+
|
|
378
|
+
## Run the weather in EnergyPlus
|
|
379
|
+
|
|
380
|
+
[Notebook 09](examples/09_run_energyplus.ipynb) runs a generated AMY in a small,
|
|
381
|
+
standalone single-zone model and inspects temperatures, solar gains, ideal loads,
|
|
382
|
+
and engine diagnostics. It uses a local EPW and requires **EnergyPlus 24.2.0
|
|
383
|
+
build 94a887817b**; set `ENERGYPLUS_EXE` to its executable if it is outside `PATH`.
|
|
384
|
+
The engine is a separate optional installation, with official platform archives
|
|
385
|
+
at the [24.2.0 bug-fix release](https://github.com/NatLabRockies/EnergyPlus/releases/tag/v24.2.0a).
|
|
386
|
+
The notebook performs no provider requests and needs no EPSM package.
|
|
387
|
+
|
|
388
|
+
CI requires deterministic provider-to-EnergyPlus tests against the built wheel,
|
|
389
|
+
including common and leap-year calendars and recovery cases. Missing engines,
|
|
390
|
+
severe/fatal errors, unexpected warnings, and incomplete hourly outputs fail the
|
|
391
|
+
gate. Release tags validate the same wheel that is published after all required
|
|
392
|
+
checks succeed. Live provider checks run separately on the weekly schedule.
|
|
393
|
+
See the [EnergyPlus validation guide](docs/energyplus.rst) for the engine pin,
|
|
394
|
+
warning policy, distribution checks, and publication setup. Engine acceptance
|
|
395
|
+
establishes consumer compatibility, not local weather accuracy or calibration.
|
|
396
|
+
|
|
397
|
+
## Development
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
pip install -e ".[dev]"
|
|
401
|
+
pytest -m "not network and not energyplus" # offline Python suite
|
|
402
|
+
pytest -m energyplus # requires the pinned engine; provider requests are offline
|
|
403
|
+
pytest -m network # live integration tests against weather-source endpoints
|
|
404
|
+
ruff check src tests examples
|
|
405
|
+
ruff format --check src tests examples
|
|
406
|
+
mypy src/smhi2epw
|
|
407
|
+
python -m build && twine check dist/*
|
|
408
|
+
pytest --doctest-modules src/smhi2epw
|
|
409
|
+
python -m sphinx -W --keep-going -b html docs docs/_build/html
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Use `pytest -m "not network and not energyplus"` for the Python-only offline
|
|
413
|
+
suite. Plain `pytest` also selects live and engine integration tests; markers
|
|
414
|
+
do not skip them by themselves.
|
|
415
|
+
Provider access and quotas apply to live SMHI and Open-Meteo requests.
|
|
416
|
+
|
|
417
|
+
## License
|
|
418
|
+
|
|
419
|
+
This project is distributed under the [MIT License](LICENSE).
|
|
420
|
+
|
|
421
|
+
### Target elevation and pressure correction
|
|
422
|
+
|
|
423
|
+
`EPWConfig.target_elevation_m` / `--target-elevation-m` sets the EPW site height;
|
|
424
|
+
omitting it uses station elevation. SMHI parameter 9 supplies sea-level QFF, so
|
|
425
|
+
the compiler now derives surface pressure using the inverse SMHI reduction.
|
|
426
|
+
`CompileResult` retains target coordinates, target elevation, the pressure method
|
|
427
|
+
and the original observation-station metadata separately. Rebuild weather files
|
|
428
|
+
when adopting this correction; see `docs/provenance.rst` for assumptions.
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
### Actual-year EPW calendar headers
|
|
432
|
+
|
|
433
|
+
Exports declare leap-day observation as `Yes` for Gregorian leap years (8784
|
|
434
|
+
hours) and `No` for common years (8760 hours). The data-period start weekday
|
|
435
|
+
matches January 1 of the measurement year. `write_epw` rejects contradictory
|
|
436
|
+
calendar headers before replacing an output file. Downstream workflows do not
|
|
437
|
+
need to patch headers after compilation. Previously exported or pinned study
|
|
438
|
+
files are not modified; regenerate in a fresh directory when migrating.
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
### Automatic recovery, warnings and provenance
|
|
442
|
+
|
|
443
|
+
`EPWConfig.weather_policy="automatic"` and CLI `--weather-policy automatic`
|
|
444
|
+
are the defaults. Preserve usable primary data, interpolate gaps up to three
|
|
445
|
+
hours, assess at most three nearby stations within 75 km for longer or unfillable
|
|
446
|
+
short wind gaps, then use bounded daily profiles through 48 hours. Same-year
|
|
447
|
+
ERA5 at the requested point recovers required hours still missing. Source failures
|
|
448
|
+
and recovery decisions are visible in `result.report.warnings` and the CLI summary.
|
|
449
|
+
Valid primary extreme temperatures are retained; physically impossible values are
|
|
450
|
+
recovered, and abrupt source transitions produce warnings. Hourly solar inputs
|
|
451
|
+
outside 0–2,000 W/m² are discarded as broadly implausible; this engineering guard
|
|
452
|
+
does not clip plausible heatwave temperatures. When ERA5 is used, overlap with
|
|
453
|
+
original observations is reported and substantial disagreement produces warnings
|
|
454
|
+
without rejecting otherwise usable primary extremes. Recovery cannot guarantee
|
|
455
|
+
an event's peak intensity or persistence. The sensitivity campaign covered
|
|
456
|
+
17 weather-years, six regions and six building profiles. Short-gap annual-load
|
|
457
|
+
errors were generally small, while long solar gaps and missing event peaks were
|
|
458
|
+
more sensitive. These are conditional comparisons against the campaign's own
|
|
459
|
+
reference weather: shared solar processing can hide a common error, and multiple
|
|
460
|
+
building profiles do not create independent weather samples. They establish
|
|
461
|
+
neither building-site accuracy nor universal donor superiority. See
|
|
462
|
+
[the method limitations](docs/limitations.rst).
|
|
463
|
+
|
|
464
|
+
Donors are checked against original same-variable overlap near each gap, using
|
|
465
|
+
held-out complete days. A scalar median offset is applied only when it improves
|
|
466
|
+
validation MAE by at least 10%; wind directions are assessed without rotation.
|
|
467
|
+
Insufficient overlap and excessive error reject a donor. These checks establish
|
|
468
|
+
agreement with a station during overlap, not building-site accuracy during the
|
|
469
|
+
outage. Model estimates may miss local microclimates and extreme intensity.
|
|
470
|
+
|
|
471
|
+
Automatic mode writes `OUTPUT.epw.json` beside the EPW. Set
|
|
472
|
+
`EPWConfig.provenance_path` or CLI `--provenance PATH.json` to choose its path.
|
|
473
|
+
The receipt records warnings, sources and recovery fractions, donor assessments,
|
|
474
|
+
reanalysis metadata, configuration, code identity, output checksum and hashes
|
|
475
|
+
of consumed responses. Cached and live payloads follow the same contract;
|
|
476
|
+
receipts are isolated for each compilation even when a client is reused.
|
|
477
|
+
`reanalysis_filled_hours` includes recovered cloud hours; `source_fractions`
|
|
478
|
+
currently describes required meteorology and GHI, excluding optional cloud cover.
|
|
479
|
+
Custom clients without scoped response receipts are marked incomplete. Paths
|
|
480
|
+
must differ and their parent directories must exist. Automatic mode returns the
|
|
481
|
+
valid EPW with a `provenance_write_failed` warning if a later sidecar-write
|
|
482
|
+
filesystem failure prevents saving its audit trail. Keep that warning visible.
|
|
483
|
+
|
|
484
|
+
Use `weather_policy="strict"` or `--weather-policy strict` for the earlier
|
|
485
|
+
failure behavior. The legacy `metobs_gap_fallback=True` / `--metobs-gap-fallback`
|
|
486
|
+
flag enables assessed donors in strict mode; automatic mode already uses them.
|
|
487
|
+
`gap_fallback_max_distance_km` and `gap_fallback_max_stations` limit donor search
|
|
488
|
+
in either policy. Strict mode writes a sidecar only when explicitly requested.
|
|
489
|
+
|
|
490
|
+
Automatic recovery still fails on invalid configuration, unknown coordinates,
|
|
491
|
+
unavailable weather from all sources, incomplete future-year data,
|
|
492
|
+
unrecoverable physical inconsistencies and filesystem errors. It never
|
|
493
|
+
substitutes another year or claims success without a complete valid calendar.
|
|
494
|
+
The Open-Meteo adapter uses the public noncommercial service; check
|
|
495
|
+
[current access limits](https://open-meteo.com/en/pricing) before batch or
|
|
496
|
+
commercial use, and acknowledge Open-Meteo and Copernicus Climate Change Service
|
|
497
|
+
ERA5. See the [Historical Weather API documentation](https://open-meteo.com/en/docs/historical-weather-api)
|
|
498
|
+
for source definitions.
|
|
499
|
+
|
|
500
|
+
See [the weather-recovery guide](docs/weather_recovery.rst) for assessment
|
|
501
|
+
thresholds, source units, diagnostic definitions, strict-mode examples,
|
|
502
|
+
historical replay evidence and limitations.
|