pyepwmorph 3.1.0__tar.gz → 3.3.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.
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/.gitignore +4 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/PKG-INFO +25 -2
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/README.md +24 -1
- pyepwmorph-3.3.0/pyepwmorph/data/amy_diffuse_table.parquet +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/models/ch2025.py +2 -4
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/morph/procedures.py +11 -2
- pyepwmorph-3.3.0/pyepwmorph/tools/amy.py +867 -0
- pyepwmorph-3.3.0/pyepwmorph/tools/amy_meteoswiss.py +123 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/tools/configuration.py +53 -20
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/tools/io.py +75 -5
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyproject.toml +1 -1
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/LICENSE +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/__init__.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/data/__init__.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/data/ch2025_monthly.parquet +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/data/ch2025_stations.parquet +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/models/__init__.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/models/access.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/models/assemble.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/models/coordinate.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/models/custom.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/morph/__init__.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/tools/__init__.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/tools/cache.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/tools/psychrometrics.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/tools/solar.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/tools/utilities.py +0 -0
- {pyepwmorph-3.1.0 → pyepwmorph-3.3.0}/pyepwmorph/tools/workflow.py +0 -0
|
@@ -159,3 +159,7 @@ cython_debug/
|
|
|
159
159
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
160
160
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
161
161
|
.idea/
|
|
162
|
+
|
|
163
|
+
# Example inputs and outputs of examples/run_amy_meteoswiss.py
|
|
164
|
+
examples/amy_data/
|
|
165
|
+
examples/amy_result/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyepwmorph
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: A python package to enable simple and easy gathering of climate model data and morphing of EPW files
|
|
5
5
|
Project-URL: Homepage, https://github.com/justinfmccarty/pyepwmorph
|
|
6
6
|
Project-URL: Issues, https://github.com/justinfmccarty/pyepwmorph/issues
|
|
@@ -148,10 +148,33 @@ morphed = results["gwl2.0"]["50"]
|
|
|
148
148
|
|
|
149
149
|
The site is matched to the nearest station that has the requested variables (or, with `ch2025_full_coverage=True`, the nearest station that has every CH2025 variable), with an elevation penalty so a much higher station is not chosen just because it is close on the map. Supported variables are temperature, humidity, dew point, wind, and radiation (global, diffuse, and direct). Pressure and cloud cover are not in CH2025; requesting them raises an error. Sky cover is left at the EPW's baseline values when radiation is morphed, so longwave sky temperature in EnergyPlus does not follow the shortwave change.
|
|
150
150
|
|
|
151
|
-
The signal is the change from the 1991-2020 reference climate to the chosen warming level. Each model chain's change is computed first, over the chains that reach that warming level, and the percentiles are taken of those changes.
|
|
151
|
+
The signal is the change from the 1991-2020 reference climate to the chosen warming level. Each model chain's change is computed first, over the chains that reach that warming level, and the percentiles are taken of those changes. The EPW's period is read from the `Period of Record` in its header. If that is not exactly 1991-2020, a `UserWarning` recommends a TMY built from 1991-2020 data. If the header states no period, a second warning says the period could not be detected, and the mismatch warning still fires if the data rows' years fall outside 1991-2020. The signal is applied to the file as it stands either way. `tools.io.write_period_of_record` adds the statement to files built without one. The same caveats are listed in `MorphConfig.ch2025_notes` and the matched station in `MorphConfig.ch2025_station`.
|
|
152
152
|
|
|
153
153
|
CH2025 data: MeteoSwiss & ETH Zurich (2025), Climate CH2025 - Daily Datasets, CC-BY 4.0, https://doi.org/10.18751/climate/scenarios/ch2025/data/1.0/
|
|
154
154
|
|
|
155
|
+
### Measured data (actual meteorological year)
|
|
156
|
+
|
|
157
|
+
Turn one real year of station measurements, hourly or 10-minute, into an EPW
|
|
158
|
+
for calibration. The table needs fixed column names and hour-ending
|
|
159
|
+
timestamps; `pyepwmorph.tools.amy.describe_columns()` lists them.
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from pyepwmorph.tools import amy, amy_meteoswiss
|
|
163
|
+
|
|
164
|
+
table = amy_meteoswiss.read_meteoswiss_ogd("ogd-smn_sma_t_historical_2020-2029.csv") # UTC, hour-ending
|
|
165
|
+
location = amy_meteoswiss.meteoswiss_location("ogd-smn_meta_stations.csv", "SMA")
|
|
166
|
+
report = amy.station_table_to_epw(
|
|
167
|
+
table, location, 2025, "zurich_fluntern_2025.epw",
|
|
168
|
+
source_name="MeteoSwiss Zurich-Fluntern", attribution=amy_meteoswiss.ATTRIBUTION,
|
|
169
|
+
)
|
|
170
|
+
print(report.to_text())
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Other networks only need a table with the documented columns and
|
|
174
|
+
`timestamp_label` / `table_utc_offset` set to match their clock. Direct and
|
|
175
|
+
diffuse irradiance and sky cover are derived when the station does not measure
|
|
176
|
+
them (the report and the EPW header say how).
|
|
177
|
+
|
|
155
178
|
## Climate scenarios
|
|
156
179
|
|
|
157
180
|
| Scenario | SSP | Description | Expected warming |
|
|
@@ -109,10 +109,33 @@ morphed = results["gwl2.0"]["50"]
|
|
|
109
109
|
|
|
110
110
|
The site is matched to the nearest station that has the requested variables (or, with `ch2025_full_coverage=True`, the nearest station that has every CH2025 variable), with an elevation penalty so a much higher station is not chosen just because it is close on the map. Supported variables are temperature, humidity, dew point, wind, and radiation (global, diffuse, and direct). Pressure and cloud cover are not in CH2025; requesting them raises an error. Sky cover is left at the EPW's baseline values when radiation is morphed, so longwave sky temperature in EnergyPlus does not follow the shortwave change.
|
|
111
111
|
|
|
112
|
-
The signal is the change from the 1991-2020 reference climate to the chosen warming level. Each model chain's change is computed first, over the chains that reach that warming level, and the percentiles are taken of those changes.
|
|
112
|
+
The signal is the change from the 1991-2020 reference climate to the chosen warming level. Each model chain's change is computed first, over the chains that reach that warming level, and the percentiles are taken of those changes. The EPW's period is read from the `Period of Record` in its header. If that is not exactly 1991-2020, a `UserWarning` recommends a TMY built from 1991-2020 data. If the header states no period, a second warning says the period could not be detected, and the mismatch warning still fires if the data rows' years fall outside 1991-2020. The signal is applied to the file as it stands either way. `tools.io.write_period_of_record` adds the statement to files built without one. The same caveats are listed in `MorphConfig.ch2025_notes` and the matched station in `MorphConfig.ch2025_station`.
|
|
113
113
|
|
|
114
114
|
CH2025 data: MeteoSwiss & ETH Zurich (2025), Climate CH2025 - Daily Datasets, CC-BY 4.0, https://doi.org/10.18751/climate/scenarios/ch2025/data/1.0/
|
|
115
115
|
|
|
116
|
+
### Measured data (actual meteorological year)
|
|
117
|
+
|
|
118
|
+
Turn one real year of station measurements, hourly or 10-minute, into an EPW
|
|
119
|
+
for calibration. The table needs fixed column names and hour-ending
|
|
120
|
+
timestamps; `pyepwmorph.tools.amy.describe_columns()` lists them.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from pyepwmorph.tools import amy, amy_meteoswiss
|
|
124
|
+
|
|
125
|
+
table = amy_meteoswiss.read_meteoswiss_ogd("ogd-smn_sma_t_historical_2020-2029.csv") # UTC, hour-ending
|
|
126
|
+
location = amy_meteoswiss.meteoswiss_location("ogd-smn_meta_stations.csv", "SMA")
|
|
127
|
+
report = amy.station_table_to_epw(
|
|
128
|
+
table, location, 2025, "zurich_fluntern_2025.epw",
|
|
129
|
+
source_name="MeteoSwiss Zurich-Fluntern", attribution=amy_meteoswiss.ATTRIBUTION,
|
|
130
|
+
)
|
|
131
|
+
print(report.to_text())
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Other networks only need a table with the documented columns and
|
|
135
|
+
`timestamp_label` / `table_utc_offset` set to match their clock. Direct and
|
|
136
|
+
diffuse irradiance and sky cover are derived when the station does not measure
|
|
137
|
+
them (the report and the EPW header say how).
|
|
138
|
+
|
|
116
139
|
## Climate scenarios
|
|
117
140
|
|
|
118
141
|
| Scenario | SSP | Description | Expected warming |
|
|
Binary file
|
|
@@ -93,10 +93,8 @@ def reference_key(state: str) -> str:
|
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
def baseline_matches(baseline_range) -> bool:
|
|
96
|
-
"""Return whether an EPW baseline
|
|
97
|
-
|
|
98
|
-
ref_start, ref_end = CH2025_BASELINE_RANGE
|
|
99
|
-
return ref_start <= start and end <= ref_end
|
|
96
|
+
"""Return whether an EPW baseline is exactly the CH2025 reference period."""
|
|
97
|
+
return tuple(int(year) for year in baseline_range) == CH2025_BASELINE_RANGE
|
|
100
98
|
|
|
101
99
|
|
|
102
100
|
def in_switzerland(latitude: float, longitude: float) -> bool:
|
|
@@ -7,6 +7,8 @@ Container for all of the individual morphing calculations which can be traced ba
|
|
|
7
7
|
55 514–524 ISSN 0960-1481 URL https://www.sciencedirect.com/science/article/pii/S0960148113000232
|
|
8
8
|
|
|
9
9
|
"""
|
|
10
|
+
import calendar
|
|
11
|
+
|
|
10
12
|
import numpy as np
|
|
11
13
|
import pandas as pd
|
|
12
14
|
|
|
@@ -33,10 +35,17 @@ def _index_of(*candidates):
|
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def _year_of(index):
|
|
36
|
-
"""Return
|
|
38
|
+
"""Return a non-leap year to build solar geometry for the data in *index*.
|
|
39
|
+
|
|
40
|
+
EPW files always hold 8760 hours with no 29 February, so their calendar
|
|
41
|
+
is a non-leap year's even when the first data row says 2016. Building
|
|
42
|
+
daily series for a leap year would add an empty 29 February and shift
|
|
43
|
+
every later day by one, so a leap year is replaced by the year before.
|
|
44
|
+
"""
|
|
37
45
|
if index is None or len(index) == 0:
|
|
38
46
|
return morph_solar_utils.DEFAULT_SOLAR_YEAR
|
|
39
|
-
|
|
47
|
+
year = int(index[0].year)
|
|
48
|
+
return year - 1 if calendar.isleap(year) else year
|
|
40
49
|
|
|
41
50
|
|
|
42
51
|
def _as_series(values, index, name, decimals=2):
|