├── .gitignore ├── .landscape.yaml ├── .travis.yml ├── LICENSE.txt ├── README.md ├── generator ├── __init__.py ├── epw.idd ├── generator.py ├── helper.py ├── iddparser.py ├── main.py └── templates │ ├── class.py │ ├── epw.py │ └── test_class.py ├── pyepw ├── __init__.py ├── calc.py └── epw.py ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── data └── USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw ├── test_comments_1.py ├── test_comments_2.py ├── test_data_periods.py ├── test_design_conditions.py ├── test_ground_temperatures.py ├── test_holidays_or_daylight_savings.py ├── test_location.py ├── test_read.py └── test_typical_or_extreme_periods.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /.landscape.yaml: -------------------------------------------------------------------------------- 1 | doc-warnings: yes 2 | ignore-paths: 3 | - generator 4 | - tests 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.2" 5 | - "3.3" 6 | - "3.4" 7 | # command to install dependencies 8 | install: 9 | - "pip install pytest" 10 | - "pip install coveralls" 11 | - "pip install -e ." 12 | script: 13 | - nosetests 14 | - coverage run --source=pyepw --omit='*/generator/*,*/tests/*' -m nose 15 | after_success: 16 | - coveralls 17 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2014 René Buffat 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #pyepw 2 | 3 | Python library to read, modify and create EnergyPlus Weather (EPW) files 4 | 5 | [![Build Status](https://travis-ci.org/rbuffat/pyepw.svg?branch=master)](https://travis-ci.org/rbuffat/pyepw) 6 | [![Coverage Status](https://img.shields.io/coveralls/rbuffat/pyepw.svg)](https://coveralls.io/r/rbuffat/pyepw?branch=master) 7 | [![Code Health](https://landscape.io/github/rbuffat/pyepw/master/landscape.svg)](https://landscape.io/github/rbuffat/pyepw/master) 8 | 9 | **This is a work in progress, do NOT expect it to actually work! As this is an early work, changes in the API are very likely.** 10 | 11 | The aim of this project is to create Python data structures to read, modify and generate EPW files. The necessary data structures are generated by parsing a modified EPW idd file from the document Auxiliary EnergyPlus Programs - Extra programs for EnergyPlus, Date: November 22, 2013. 12 | 13 | 14 | ##Installation: 15 | 16 | 17 | ###pip: 18 | ``` 19 | pip install pyepw 20 | ``` 21 | 22 | ###manual 23 | ``` 24 | git clone https://github.com/rbuffat/pyepw.git 25 | cd pyepw 26 | python setup.py install 27 | ``` 28 | 29 | ##Usage: 30 | 31 | 32 | Reading data from an EPW file: 33 | ```python 34 | from pyepw.epw import EPW 35 | epw = EPW() 36 | epw.read(r"USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw") 37 | ``` 38 | 39 | Print the dry bulb temperature for every weather record: 40 | ```python 41 | for wd in epw.weatherdata: 42 | print wd.year, wd.month, wd.day, wd.hour, wd.minute, wd.dry_bulb_temperature 43 | ``` 44 | 45 | Modifying the dry bulb temperature for every weather record: 46 | ```python 47 | for wd in epw.weatherdata: 48 | wd.dry_bulb_temperature += 1.0 49 | ``` 50 | 51 | Creating an EPW File 52 | ```python 53 | epw.save(r"new_file.epw") 54 | ``` 55 | 56 | ##Notes: 57 | 58 | The script to parse the IDD definitions and generate epw.py is located in the generator package. To read, modify and generate EPW files only epw.py need to be used. epw.py is generated by executing generator/main.py. It requires jinja2, autopep8 and docformatter. 59 | -------------------------------------------------------------------------------- /generator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbuffat/pyepw/373d4d3c8386c8d35789f086ac5f6018c2711745/generator/__init__.py -------------------------------------------------------------------------------- /generator/epw.idd: -------------------------------------------------------------------------------- 1 | !ESP(r)/EnergyPlus Weather Format 2 | !April 2002 3 | 4 | LOCATION, 5 | A1, \field city 6 | \type alpha 7 | A2, \field State Province Region 8 | \type alpha 9 | A3, \field Country 10 | \type alpha 11 | A4, \field Source 12 | \type alpha 13 | A5, \field WMO 14 | \note usually a 6 digit field. Used as alpha in EnergyPlus 15 | \type alpha 16 | N2 , \field Latitude 17 | \units deg 18 | \minimum -90.0 19 | \maximum +90.0 20 | \default 0.0 21 | \note + is North, - is South, degree minutes represented in decimal (i.e. 30 minutes is .5) 22 | \type real 23 | N3 , \field Longitude 24 | \units deg 25 | \minimum -180.0 26 | \maximum +180.0 27 | \default 0.0 28 | \note - is West, + is East, degree minutes represented in decimal (i.e. 30 minutes is .5) 29 | \type real 30 | N4 , \field TimeZone 31 | \units hr - not on standard units list??? 32 | \minimum -12.0 33 | \maximum +12.0 34 | \default 0.0 35 | \note Time relative to GMT. 36 | \type real 37 | N5 ; \field Elevation 38 | \units m 39 | \minimum -1000.0 40 | \maximum< +9999.9 41 | \default 0.0 42 | \type real 43 | 44 | DESIGN CONDITION, 45 | A1, \field Title of Design Condition 46 | \type alpha 47 | A2, \field Unkown field 48 | \type alpha 49 | \note Empty field in data 50 | A3, \field Design Stat Heating 51 | \type choice 52 | \key Heating 53 | \default Heating 54 | N1, \field ColdestMonth 55 | \type integer 56 | \minimum 1 57 | \maximum 12 58 | N2, \field DB996 59 | \note Dry-bulb temperature corresponding to 99.6% annual cumulative 60 | \note frequency of occurrence (cold conditions) 61 | \units C 62 | \type real 63 | N3, \field DB990 64 | \note Dry-bulb temperature corresponding to 90.0% annual cumulative 65 | \note frequency of occurrence (cold conditions) 66 | \units C 67 | \type real 68 | N4, \field DP996 69 | \note Dew-point temperature corresponding to 99.6% annual cumulative 70 | \note frequency of occurrence (cold conditions) 71 | \units C 72 | \type real 73 | N5, \field HR_DP996 74 | \note humidity ratio, calculated at standard atmospheric pressure 75 | \note at elevation of station, corresponding to 76 | \note Dew-point temperature corresponding to 99.6% annual cumulative 77 | \note frequency of occurrence (cold conditions) 78 | \type real 79 | N6, \field DB_DP996 80 | \note mean coincident drybulb temperature corresponding to 81 | \note Dew-point temperature corresponding to 99.6% annual cumulative 82 | \note frequency of occurrence (cold conditions) 83 | \units C 84 | \type real 85 | N7, \field DP990 86 | \note Dew-point temperature corresponding to 90.0% annual cumulative 87 | \note frequency of occurrence (cold conditions) 88 | \units C 89 | \type real 90 | N8, \field HR_DP990 91 | \note humidity ratio, calculated at standard atmospheric pressure 92 | \note at elevation of station, corresponding to 93 | \note Dew-point temperature corresponding to 90.0% annual cumulative 94 | \note frequency of occurrence (cold conditions) 95 | \type real 96 | N9, \field DB_DP990 97 | \note mean coincident drybulb temperature corresponding to 98 | \note Dew-point temperature corresponding to 90.0% annual cumulative 99 | \note frequency of occurrence (cold conditions) 100 | \units C 101 | \type real 102 | N10, \field WS004c 103 | \units m/s 104 | \type real 105 | N11, \field DB_WS004c 106 | \note Mean coincident dry-bulb temperature to wind speed corresponding to 0.40% cumulative frequency for coldest month 107 | \units C 108 | \type real 109 | N12, \field WS010c 110 | \note Wind speed corresponding to 1.0% cumulative frequency 111 | \note of occurrence for coldest month; 112 | \units m/s 113 | \type real 114 | N13, \field DB_WS010c 115 | \note Mean coincident dry-bulb temperature to wind speed corresponding to 1.0% cumulative frequency for coldest month 116 | \units C 117 | \type real 118 | N14, \field WS_DB996 119 | \note Mean wind speed coincident with 99.6% dry-bulb temperature 120 | \units m/s 121 | \type real 122 | N15, \field WD_DB996 123 | \note most frequent wind direction corresponding to mean wind speed coincident with 99.6% dry-bulb temperature 124 | \note degrees from north (east = 90 deg) 125 | \units deg 126 | \type real 127 | A4, \field Design Stat Cooling 128 | \type choice 129 | \key Cooling 130 | \default Cooling 131 | N16, \field HottestMonth 132 | \type integer 133 | \minimum 1 134 | \maximum 12 135 | N17, \field DBR 136 | \note Daily temperature range for hottest month 137 | \note [defined as mean of the difference between daily maximum 138 | \note and daily minimum dry-bulb temperatures for hottest month] 139 | \units C 140 | \type real 141 | N18, \field DB004 142 | \note Dry-bulb temperature corresponding to 0.4% annual cumulative frequency of occurrence (warm conditions) 143 | \units C 144 | \type real 145 | N19, \field WB_DB004 146 | \note mean coincident wet-bulb temperature to 147 | \note Dry-bulb temperature corresponding to 0.4% annual cumulative frequency of occurrence (warm conditions) 148 | \units C 149 | \type real 150 | N20, \field DB010 151 | \note Dry-bulb temperature corresponding to 1.0% annual cumulative frequency of occurrence (warm conditions) 152 | \units C 153 | \type real 154 | N21, \field WB_DB010 155 | \note mean coincident wet-bulb temperature to 156 | \note Dry-bulb temperature corresponding to 1.0% annual cumulative frequency of occurrence (warm conditions) 157 | \units C 158 | \type real 159 | N22, \field DB020 160 | \note mean coincident wet-bulb temperature to 161 | \note Dry-bulb temperature corresponding to 2.0% annual cumulative frequency of occurrence (warm conditions) 162 | \units C 163 | \type real 164 | N23, \field WB_DB020 165 | \note mean coincident wet-bulb temperature to 166 | \note Dry-bulb temperature corresponding to 2.0% annual cumulative frequency of occurrence (warm conditions) 167 | \units C 168 | \type real 169 | N24, \field WB004 170 | \note Wet-bulb temperature corresponding to 0.4% annual cumulative frequency of occurrence 171 | \units C 172 | \type real 173 | N25, \field DB_WB004 174 | \note mean coincident dry-bulb temperature to 175 | \note Wet-bulb temperature corresponding to 0.4% annual cumulative frequency of occurrence 176 | \units C 177 | \type real 178 | N26, \field WB010 179 | \note Wet-bulb temperature corresponding to 1.0% annual cumulative frequency of occurrence 180 | \units C 181 | \type real 182 | N27, \field DB_WB010 183 | \note mean coincident dry-bulb temperature to 184 | \note Wet-bulb temperature corresponding to 1.0% annual cumulative frequency of occurrence 185 | \units C 186 | \type real 187 | N28, \field WB020 188 | \note Wet-bulb temperature corresponding to 02.0% annual cumulative frequency of occurrence 189 | \units C 190 | \type real 191 | N29, \field DB_WB020 192 | \note mean coincident dry-bulb temperature to 193 | \note Wet-bulb temperature corresponding to 2.0% annual cumulative frequency of occurrence 194 | \units C 195 | \type real 196 | N30, \field WS_DB004 197 | \note Mean wind speed coincident with 0.4% dry-bulb temperature 198 | \units m/s 199 | \type real 200 | N31, \field WD_DB004 201 | \note corresponding most frequent wind direction 202 | \note Mean wind speed coincident with 0.4% dry-bulb temperature 203 | \note degrees true from north (east = 90 deg) 204 | \units deg 205 | \type real 206 | N32, \field DP004 207 | \note Dew-point temperature corresponding to 0.4% annual cumulative frequency of occurrence 208 | \units C 209 | \type real 210 | N33, \field HR_DP004 211 | \note humidity ratio corresponding to 212 | \note Dew-point temperature corresponding to 0.4% annual cumulative frequency of occurrence 213 | \units 214 | \type real 215 | N34, \field DB_DP004 216 | \note mean coincident dry-bulb temperature to 217 | \note Dew-point temperature corresponding to 0.4% annual cumulative frequency of occurrence 218 | \units C 219 | \type real 220 | N35, \field DP010 221 | \note Dew-point temperature corresponding to 1.0% annual cumulative frequency of occurrence 222 | \units C 223 | \type real 224 | N36, \field HR_DP010 225 | \note humidity ratio corresponding to 226 | \note Dew-point temperature corresponding to 1.0,% annual cumulative frequency of occurrence 227 | \note calculated at the standard atmospheric pressure at elevation of station 228 | \units 229 | \type real 230 | N37, \field DB_DP010 231 | \note mean coincident dry-bulb temperature to 232 | \note Dew-point temperature corresponding to 1.0% annual cumulative frequency of occurrence 233 | \units C 234 | \type real 235 | N38, \field DP020 236 | \note Dew-point temperature corresponding to 2.0% annual cumulative frequency of occurrence 237 | \units C 238 | \type real 239 | N39, \field HR_DP020 240 | \note humidity ratio corresponding to 241 | \note Dew-point temperature corresponding to 2.0% annual cumulative frequency of occurrence 242 | \note calculated at the standard atmospheric pressure at elevation of station 243 | \units 244 | \type real 245 | N40, \field DB_DP020 246 | \note mean coincident dry-bulb temperature to 247 | \note Dew-point temperature corresponding to 2.0% annual cumulative frequency of occurrence 248 | \units C 249 | \type real 250 | N41, \field EN004 251 | \note mean coincident dry-bulb temperature to 252 | \note Enthalpy corresponding to 0.4% annual cumulative frequency of occurrence 253 | \units kJ/kg 254 | \type real 255 | N42, \field DB_EN004 256 | \note mean coincident dry-bulb temperature to 257 | \note Enthalpy corresponding to 0.4% annual cumulative frequency of occurrence 258 | \units C 259 | \type real 260 | N43, \field EN010 261 | \note mean coincident dry-bulb temperature to 262 | \note Enthalpy corresponding to 1.0% annual cumulative frequency of occurrence 263 | \units kJ/kg 264 | \type real 265 | N44, \field DB_EN010 266 | \note mean coincident dry-bulb temperature to 267 | \note Enthalpy corresponding to 1.0% annual cumulative frequency of occurrence 268 | \units C 269 | \type real 270 | N45, \field EN020 271 | \note mean coincident dry-bulb temperature to 272 | \note Enthalpy corresponding to 2.0% annual cumulative frequency of occurrence 273 | \units kJ/kg 274 | \type real 275 | N46, \field DB_EN020 276 | \note mean coincident dry-bulb temperature to 277 | \note Enthalpy corresponding to 2.0% annual cumulative frequency of occurrence 278 | \units C 279 | \type real 280 | N47, \field Hrs_8-4_&_DB-12.8/20.6 281 | \note Number of hours between 8 AM and 4 PM (inclusive) with dry-bulb temperature between 12.8 and 20.6 C 282 | \type real 283 | A5, \field Design Stat Extremes 284 | \type choice 285 | \key Extremes 286 | \default Extremes 287 | N48, \field WS010 288 | \note Wind speed corresponding to 1.0% annual cumulative frequency of occurrence 289 | \units m/s 290 | \type real 291 | N49, \field WS025 292 | \note Wind speed corresponding to 2.5% annual cumulative frequency of occurrence 293 | \units m/s 294 | \type real 295 | N50, \field WS050 296 | \note Wind speed corresponding 5.0% annual cumulative frequency of occurrence 297 | \units m/s 298 | \type real 299 | N51, \field WBmax 300 | \note Extreme maximum wet-bulb temperature 301 | \units C 302 | \type real 303 | N52, \field DBmin_mean 304 | \note Mean of extreme annual minimum dry-bulb temperature 305 | \units C 306 | \type real 307 | N53, \field DBmax_mean 308 | \note Mean of extreme annual maximum dry-bulb temperature 309 | \units C 310 | \type real 311 | N54, \field DBmin_stddev 312 | \note Standard deviation of extreme annual minimum dry-bulb temperature 313 | \units C 314 | \type real 315 | N55, \field DBmax_stddev 316 | \note Standard deviation of extreme annual maximum dry-bulb temperature 317 | \units C 318 | \type real 319 | N56, \field DBmin05years 320 | \note 5-year return period values for minimum extreme dry-bulb temperature 321 | \units C 322 | \type real 323 | N57, \field DBmax05years 324 | \note 5-year return period values for maximum extreme dry-bulb temperature 325 | \units C 326 | \type real 327 | N58, \field DBmin10years 328 | \note 10-year return period values for minimum extreme dry-bulb temperature 329 | \units C 330 | \type real 331 | N59, \field DBmax10years 332 | \note 10-year return period values for maximum extreme dry-bulb temperature 333 | \units C 334 | \type real 335 | N60, \field DBmin20years 336 | \note 20-year return period values for minimum extreme dry-bulb temperature 337 | \units C 338 | \type real 339 | N61, \field DBmax20years 340 | \note 20-year return period values for maximum extreme dry-bulb temperature 341 | \units C 342 | \type real 343 | N62, \field DBmin50years 344 | \note 50-year return period values for minimum extreme dry-bulb temperature 345 | \units C 346 | \type real 347 | N63, \field DBmax50years 348 | \note 50-year return period values for maximum extreme dry-bulb temperature 349 | \units C 350 | \type real 351 | 352 | 353 | DESIGN CONDITIONS, 354 | L1; \list DESIGN CONDITION 355 | 356 | 357 | TYPICAL/EXTREME PERIOD, 358 | A1, \field Typical/Extreme Period Name 359 | A2, \field Typical/Extreme Period Type 360 | A3, \field Period Start Day 361 | A4, \field Period End Day 362 | 363 | TYPICAL/EXTREME PERIODS, 364 | L1; \list TYPICAL/EXTREME PERIOD 365 | 366 | GROUND TEMPERATURE, 367 | N2, \field Ground Temperature Depth 368 | \units m 369 | N3, \field Depth Soil Conductivity 370 | \units W/m-K, 371 | N4, \field Depth Soil Density 372 | \units kg/m3 373 | N5, \field Depth Soil Specific Heat 374 | \units J/kg-K, 375 | N6, \field Depth January Average Ground Temperature 376 | \units C 377 | N7, \field Depth February Average Ground Temperature 378 | \units C 379 | N8, \field Depth March Average Ground Temperature 380 | \units C 381 | N9, \field Depth April Average Ground Temperature 382 | \units C 383 | N10, \field Depth May Average Ground Temperature 384 | \units C 385 | N11, \field Depth June Average Ground Temperature 386 | \units C 387 | N12, \field Depth July Average Ground Temperature 388 | \units C 389 | N13, \field Depth August Average Ground Temperature 390 | \units C 391 | N14, \field Depth September Average Ground Temperature 392 | \units C 393 | N15, \field Depth October Average Ground Temperature 394 | \units C 395 | N16, \field Depth November Average Ground Temperature 396 | \units C 397 | N17, \field Depth December Average Ground Temperature 398 | \units C 399 | 400 | GROUND TEMPERATURES, 401 | L1; \list GROUND TEMPERATURE 402 | 403 | HOLIDAY, 404 | A4, \field Holiday Name 405 | A5, \field Holiday Day 406 | 407 | HOLIDAYS/DAYLIGHT SAVINGS, 408 | A1, \field LeapYear Observed 409 | \type choice 410 | \key Yes 411 | \key No 412 | \note Yes if Leap Year will be observed for this file 413 | \note No if Leap Year days (29 Feb) should be ignored in this file 414 | A2, \field Daylight Saving Start Day 415 | A3, \field Daylight Saving End Day 416 | L1; \list HOLIDAY 417 | 418 | COMMENTS 1, 419 | A1, \field Comments_1 420 | 421 | COMMENTS 2, 422 | A1, \field Comments_2 423 | 424 | DATA PERIOD, 425 | N2, \field Number of Records per hour 426 | \type integer 427 | A1, \field Data Period Name/Description 428 | A2, \field Data Period Start Day of Week 429 | \type choice 430 | \key Sunday 431 | \key Monday 432 | \key Tuesday 433 | \key Wednesday 434 | \key Thursday 435 | \key Friday 436 | \key Saturday 437 | A3, \field Data Period Start Day 438 | A4, \field Data Period End Day 439 | 440 | DATA PERIODS, 441 | L1; \list DATA PERIOD 442 | 443 | WEATHER DATA, 444 | N1, \field Year 445 | \type integer 446 | N2, \field Month 447 | \type integer 448 | \minimum 1 449 | \maximum 12 450 | N3, \field Day 451 | \type integer 452 | \minimum 1 453 | \maximum 31 454 | N4, \field Hour 455 | \type integer 456 | \minimum 1 457 | \maximum 24 458 | N5, \field Minute 459 | \type integer 460 | \minimum 0 461 | \maximum 60 462 | A1, \field Data Source and Uncertainty Flags 463 | \note Initial day of weather file is checked by EnergyPlus for validity (as shown below) 464 | \note Each field is checked for "missing" as shown below. Reasonable values, calculated 465 | \note values or the last "good" value is substituted. 466 | N6, \field Dry Bulb Temperature 467 | \units C 468 | \minimum> -70 469 | \maximum< 70 470 | \type real 471 | \missing 99.9 472 | N7, \field Dew Point Temperature 473 | \units C 474 | \minimum> -70 475 | \maximum< 70 476 | \missing 99.9 477 | \type real 478 | N8, \field Relative Humidity 479 | \missing 999 480 | \minimum 0 481 | \maximum 110 482 | \type integer 483 | N9, \field Atmospheric Station Pressure 484 | \units Pa 485 | \missing 999999 486 | \minimum> 31000 487 | \maximum< 120000 488 | \type integer 489 | N10, \field Extraterrestrial Horizontal Radiation 490 | \units Wh/m2 491 | \missing 9999. 492 | \minimum 0 493 | N11, \field Extraterrestrial Direct Normal Radiation 494 | \units Wh/m2 495 | \missing 9999. 496 | \minimum 0 497 | N12, \field Horizontal Infrared Radiation Intensity 498 | \units Wh/m2 499 | \missing 9999. 500 | \minimum 0 501 | N13, \field Global Horizontal Radiation 502 | \units Wh/m2 503 | \missing 9999. 504 | \minimum 0 505 | N14, \field Direct Normal Radiation 506 | \units Wh/m2 507 | \missing 9999. 508 | \minimum 0 509 | N15, \field Diffuse Horizontal Radiation 510 | \units Wh/m2 511 | \missing 9999. 512 | \minimum 0 513 | N16, \field Global Horizontal Illuminance 514 | \units lux 515 | \missing 999999. 516 | \note will be missing if >= 999900 517 | \minimum 0 518 | N17, \field Direct Normal Illuminance 519 | \units lux 520 | \missing 999999. 521 | \note will be missing if >= 999900 522 | \minimum 0 523 | N18, \field Diffuse Horizontal Illuminance 524 | \units lux 525 | \missing 999999. 526 | \note will be missing if >= 999900 527 | \minimum 0 528 | N19, \field Zenith Luminance 529 | \units Cd/m2 530 | \missing 9999. 531 | \note will be missing if >= 9999 532 | \minimum 0 533 | N20, \field Wind Direction 534 | \units degrees 535 | \missing 999. 536 | \minimum 0 537 | \maximum 360 538 | N21, \field Wind Speed 539 | \units m/s 540 | \missing 999. 541 | \minimum 0 542 | \maximum 40 543 | N22, \field Total Sky Cover 544 | \note This is the value for total sky cover (tenths of coverage). (i.e. 1 is 1/10 covered. 10 is total coverage). 545 | \note (Amount of sky dome in tenths covered by clouds or obscuring phenomena at the hour indicated at the time indicated.) 546 | \missing 99 547 | \minimum 0 548 | \maximum 10 549 | N23, \field Opaque Sky Cover 550 | \note This is the value for opaque sky cover (tenths of coverage). (i.e. 1 is 1/10 covered. 10 is total coverage). 551 | \note (Amount of sky dome in tenths covered by clouds or obscuring phenomena that prevent observing the sky or higher cloud layers at the time indicated.) 552 | \note This is not used unless the field for Horizontal Infrared Radiation Intensity is missing and then it is used to calculate Horizontal Infrared Radiation Intensity 553 | \missing 99 554 | \minimum 0 555 | \maximum 10 556 | N24, \field Visibility 557 | \note This is the value for visibility in km. (Horizontal visibility at the time indicated.) 558 | \units km 559 | \missing 9999 560 | N25, \field Ceiling Height 561 | \note This is the value for ceiling height in m. 562 | \note (77777 is unlimited ceiling height. 88888 is cirroform ceiling.) It is not currently used in EnergyPlus calculations. 563 | \units m 564 | \missing 99999 565 | N26, \field Present Weather Observation 566 | \note If the value of the field is 0, then the observed weather codes are taken from the following field. 567 | \note If the value of the field is 9, then "missing" weather is assumed. Since the primary use of these fields (Present Weather Observation and Present Weather Codes) is for rain/wet surfaces, a missing observation field or a missing weather code implies "no rain". 568 | \type integer 569 | N27, \field Present Weather Codes 570 | \type integer 571 | N28, \field Precipitable Water 572 | \units mm 573 | \missing 999 574 | N29, \field Aerosol Optical Depth 575 | \units thousandths 576 | \missing .999 577 | N30, \field Snow Depth 578 | \units cm 579 | \missing 999 580 | N31, \field Days Since Last Snowfall 581 | \missing 99 582 | \type integer 583 | N32, \field Albedo 584 | \missing 999 585 | N33, \field Liquid Precipitation Depth 586 | \units mm 587 | \missing 999 588 | N34; \field Liquid Precipitation Quantity 589 | \units hr 590 | \missing 99 591 | 592 | -------------------------------------------------------------------------------- /generator/generator.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | from jinja2 import Environment, PackageLoader 3 | 4 | env = Environment(loader=PackageLoader('generator', 'templates')) 5 | 6 | 7 | def generate_class(obj): 8 | template = env.get_template('class.py') 9 | 10 | context = {} 11 | context["internal_name"] = obj.internal_name 12 | context["class_name"] = obj.class_name 13 | context["field_count"] = len(obj.fields) 14 | context["fields"] = obj.fields 15 | return template.render(context) 16 | 17 | 18 | def generate_epw(objs): 19 | list_objs = [] 20 | for obj in objs: 21 | for field in obj.fields: 22 | if field.is_list: 23 | list_objs.append(field.internal_name) 24 | not_list_objs = [] 25 | for obj in objs: 26 | if obj.internal_name not in list_objs: 27 | not_list_objs.append(obj) 28 | 29 | template = env.get_template('epw.py') 30 | context = {} 31 | classes = [] 32 | for obj in objs: 33 | classes.append(generate_class(obj)) 34 | context["datadicts"] = classes 35 | context["generation_date"] = date.today() 36 | context["objs"] = not_list_objs[:-1] 37 | return template.render(context) 38 | 39 | 40 | def generate_testclass(obj, objs): 41 | _objs = {} 42 | for o in objs: 43 | _objs[o.class_name] = o 44 | template = env.get_template('test_class.py') 45 | context = {} 46 | context["obj"] = obj 47 | context["objs"] = _objs 48 | return template.render(context) 49 | -------------------------------------------------------------------------------- /generator/helper.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Oct 30, 2014 3 | 4 | @author: rene 5 | ''' 6 | import string 7 | import re 8 | 9 | 10 | def normalize_field_name(internal_name): 11 | 12 | name = internal_name.strip() 13 | name = name.replace('/', ' or ').replace('&', ' and ') 14 | name = name.replace('.', ' ').replace(',', ' ') 15 | name = name.lower() 16 | name = name.replace(' ', '_') 17 | name = re.sub(r'[^a-zA-Z0-9_]', '', name) 18 | name = re.sub(r'_+', '_', name) 19 | return name 20 | 21 | 22 | def normalize_object_name(internal_name): 23 | name = internal_name.replace('/', ' or ').strip() 24 | name = string.capwords(name) 25 | name = name.replace(' ', '') 26 | return name 27 | 28 | 29 | def normalize_object_var_name(internal_name): 30 | name = internal_name.strip().replace('/', ' or ').lower() 31 | name = name.replace(' ', '_') 32 | name = name.replace('(', '') 33 | name = name.replace(')', '') 34 | return name 35 | 36 | 37 | class DataObject: 38 | 39 | def __init__(self, internal_name=None): 40 | self.internal_name = internal_name 41 | self.class_name = normalize_object_name(self.internal_name) 42 | self.var_name = normalize_object_var_name(internal_name) 43 | self.fields = [] 44 | self.is_list_object = False 45 | 46 | 47 | class DataField(object): 48 | 49 | def __init__(self, internal_name, ftype): 50 | 51 | self.attributes = {} 52 | self.internal_name = internal_name 53 | self.field_name = normalize_field_name(internal_name) 54 | self.is_list = False 55 | self.ftype = ftype 56 | 57 | def value2py(self, value, ftype): 58 | if ftype == 'alpha': 59 | return str(value) 60 | if ftype == 'integer': 61 | return str(int(value)) 62 | if ftype == 'real': 63 | return str(float(value)) 64 | return value 65 | 66 | def pytype(self, ftype): 67 | if ftype == 'alpha' or ftype == 'choice': 68 | return "str" 69 | if ftype == 'integer': 70 | return "int" 71 | if ftype == 'real': 72 | return "float" 73 | return "str" 74 | 75 | def add_attribute(self, attribute_name, value): 76 | self.attributes[attribute_name] = value 77 | 78 | def conv_vals(self): 79 | # Update type if not other specified 80 | if "type" not in self.attributes: 81 | if self.ftype == "A": 82 | self.attributes["type"] = "alpha" 83 | elif self.ftype == "N": 84 | self.attributes["type"] = "real" 85 | 86 | # Convert some values to python representation 87 | for attribute_name in list(self.attributes): 88 | if attribute_name in ["default", 89 | "minimum", 90 | "minimum>", 91 | "maximum", 92 | "maximum<", 93 | "missing"]: 94 | value = self.attributes[attribute_name] 95 | self.attributes[attribute_name] = self.value2py(value, 96 | self.attributes["type"]) 97 | self.attributes["pytype"] = self.pytype(self.attributes["type"]) 98 | 99 | 100 | class ListField(DataField): 101 | 102 | def __init__(self, internal_name): 103 | super(ListField, self).__init__(internal_name, "list") 104 | self.is_list = True 105 | self.object_name = normalize_object_name(internal_name) 106 | self.field_name = normalize_field_name(internal_name) 107 | -------------------------------------------------------------------------------- /generator/iddparser.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Oct 30, 2014 3 | 4 | @author: rene 5 | ''' 6 | import re 7 | import string 8 | 9 | from helper import DataObject, DataField, ListField 10 | 11 | 12 | class IDDParser(): 13 | 14 | def _is_new_field(self, line): 15 | return re.search(r"^\s*[AN]\d+\s*[,;]", line) is not None 16 | 17 | def _is_list(self, line): 18 | return re.search(r"^\s*L\d+\s*[,;]", line) is not None 19 | 20 | def _is_field_attribute(self, line): 21 | return re.search(r"^\s*\\", line) is not None 22 | 23 | def _is_new_object(self, line): 24 | if self._is_new_field(line) or self._is_field_attribute(line) or self._is_list(line): 25 | return False 26 | return re.search(r"^\s*(.*),", line) is not None 27 | 28 | def _parse_object_name(self, line): 29 | 30 | match_obj_name = re.search(r"^\s*(.*),", line) 31 | assert match_obj_name is not None 32 | 33 | internal_name = match_obj_name.group(1) 34 | self.current_object = DataObject(internal_name) 35 | 36 | def _parse_field_name(self, line): 37 | # print "NewField:\t", line 38 | match_field_name = re.search(r"\\field\s(.*)$", line) 39 | match_field_type = re.search(r"^\s*([AN])", line) 40 | 41 | if match_field_name is None or match_field_type is None: 42 | print "Did not match field name: ", line, match_field_name, match_field_type 43 | return 44 | 45 | ftype = match_field_type.group(1) 46 | internal_name = match_field_name.group(1).strip() 47 | if len(self.current_object.fields) > 0: 48 | self.current_object.fields[-1].conv_vals() 49 | self.current_object.fields.append(DataField(internal_name, ftype)) 50 | 51 | def _parse_list(self, line): 52 | match_list_name = re.search(r"\\list\s(.*)$", line) 53 | 54 | if match_list_name is None: 55 | print "Did not match list name: ", line, match_list_name 56 | return 57 | 58 | internal_name = match_list_name.group(1).strip() 59 | df = ListField(internal_name) 60 | df.is_list = True 61 | self.current_object.fields.append(df) 62 | 63 | def _parse_field_attribute(self, line): 64 | last_field = self.current_object.fields[-1] 65 | 66 | match_attribute_name = re.match(r"\s*\\([^\s]+)", line) 67 | if match_attribute_name is not None: 68 | attribute_name = match_attribute_name.group(1).strip() 69 | 70 | no_value_attributes = ["required-field"] 71 | 72 | if attribute_name in no_value_attributes: 73 | last_field.add_attribute(attribute_name, None) 74 | 75 | match_value = re.search(r"\s*\\[^\s]+\s?(.*)", line) 76 | if match_value is not None: 77 | value = match_value.group(1).strip() 78 | 79 | multiple_value_attributes = ["key", 80 | "note"] 81 | 82 | if attribute_name in multiple_value_attributes: 83 | if attribute_name not in last_field.attributes: 84 | last_field.add_attribute(attribute_name, []) 85 | last_field.attributes[attribute_name].append(value) 86 | else: 87 | last_field.add_attribute(attribute_name, value) 88 | else: 89 | print "found no field value for: ", line, attribute_name, match_value 90 | else: 91 | print "found no field attribute for: ", line, match_attribute_name 92 | 93 | def __init__(self): 94 | self.current_object = None 95 | self.objects = [] 96 | 97 | def parse(self, path): 98 | 99 | with open(path, mode='r') as f: 100 | for line in f: 101 | if line[0] == '!': 102 | continue 103 | line = line.strip() 104 | print line 105 | 106 | if self._is_new_object(line): 107 | # print "New Object! ", line 108 | if self.current_object is not None: 109 | if len(self.current_object.fields) > 0: 110 | self.current_object.fields[-1].conv_vals() 111 | self.objects.append(self.current_object) 112 | self.current_object = None 113 | 114 | self._parse_object_name(line) 115 | 116 | elif self._is_list(line): 117 | self._parse_list(line) 118 | 119 | elif self._is_new_field(line): 120 | 121 | assert self.current_object is not None 122 | 123 | self._parse_field_name(line) 124 | 125 | elif self._is_field_attribute(line): 126 | self._parse_field_attribute(line) 127 | else: 128 | print "No detect:", line 129 | 130 | if self.current_object is not None: 131 | self.objects.append(self.current_object) 132 | 133 | list_objs = [] 134 | for obj in self.objects: 135 | for field in obj.fields: 136 | field.conv_vals() 137 | if field.is_list: 138 | list_objs.append(field.internal_name) 139 | 140 | for obj in self.objects: 141 | if obj.internal_name in list_objs: 142 | obj.is_list_object = True 143 | 144 | return self.objects 145 | # 146 | # for o in self.objects: 147 | # print o.name, len(o.fields), [i.name for i in o.fields] 148 | 149 | 150 | if __name__ == '__main__': 151 | parser = IDDParser() 152 | objects = parser.parse("epw.idd") 153 | -------------------------------------------------------------------------------- /generator/main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 18, 2014 3 | 4 | @author: rene 5 | ''' 6 | import autopep8 7 | from docformatter import format_code 8 | 9 | from generator import generate_epw, generate_testclass 10 | from iddparser import IDDParser 11 | 12 | 13 | if __name__ == '__main__': 14 | parser = IDDParser() 15 | objs = parser.parse("epw.idd") 16 | 17 | source_file = generate_epw(objs) 18 | source_file = autopep8.fix_code( 19 | source_file, options=autopep8.parse_args(['--aggressive', 20 | '--aggressive', 21 | '--aggressive', 22 | ''])) 23 | source_file = format_code(source_file) 24 | 25 | with open("../pyepw/epw.py", 'w') as f: 26 | f.write(source_file) 27 | 28 | for obj in objs[:-1]: 29 | if not obj.is_list_object: 30 | source_file = generate_testclass(obj, objs) 31 | source_file = autopep8.fix_code( 32 | source_file, options=autopep8.parse_args(['--aggressive', 33 | '--aggressive', 34 | '--aggressive', 35 | ''])) 36 | source_file = format_code(source_file) 37 | with open("../tests/test_{}.py".format(obj.var_name), 'w') as f: 38 | f.write(source_file) 39 | 40 | -------------------------------------------------------------------------------- /generator/templates/class.py: -------------------------------------------------------------------------------- 1 | class {{ class_name }}(object): 2 | """ Corresponds to EPW IDD object `{{ internal_name }}` 3 | """ 4 | _internal_name = "{{ internal_name }}" 5 | field_count = {{ field_count}} 6 | 7 | def __init__(self): 8 | """ Init data dictionary object for EPW IDD `{{ internal_name }}` 9 | """ 10 | {%- for field in fields %} 11 | {%- if field.is_list %} 12 | self._{{field.field_name}}s = [] 13 | {%- else %} 14 | self._{{field.field_name}} = None 15 | {%- endif %} 16 | {%- endfor %} 17 | 18 | def read(self, vals): 19 | """ Read values 20 | 21 | Args: 22 | vals (list): list of strings representing values 23 | """ 24 | i = 0 25 | {%- for field in fields %} 26 | {%- if field.is_list %} 27 | count = int(vals[i]) 28 | i += 1 29 | for _ in range(count): 30 | obj = {{field.object_name}}() 31 | obj.read(vals[i:i + obj.field_count]) 32 | self.add_{{field.field_name}}(obj) 33 | i += obj.field_count 34 | {%- else %} 35 | if len(vals[i]) == 0: 36 | self.{{field.field_name}} = None 37 | else: 38 | self.{{field.field_name}} = vals[i] 39 | i += 1 40 | {%- endif %} 41 | {%- endfor %} 42 | 43 | {%- for field in fields %} 44 | {%- if field.is_list %} 45 | 46 | @property 47 | def {{field.field_name}}s(self): 48 | """Get {{field.field_name}}s 49 | 50 | Returns: 51 | A list of {{ field.object_name }} objects 52 | """ 53 | return self._{{field.field_name}}s 54 | 55 | def add_{{field.field_name}}(self, value): 56 | """Add {{field.field_name}} 57 | 58 | Args: 59 | {{ field.object_name }}: new value to add to `{{field.field_name}}s` 60 | """ 61 | self._{{field.field_name}}s.append(value) 62 | {%- else %} 63 | 64 | @property 65 | def {{field.field_name}}(self): 66 | """Get {{ field.field_name }} 67 | 68 | Returns: 69 | {{ field.attributes.pytype }}: the value of `{{field.field_name}}` or None if not set 70 | """ 71 | return self._{{field.field_name}} 72 | 73 | @{{field.field_name}}.setter 74 | def {{field.field_name}}(self, value={%if field.attributes.default and not (field.attributes.type=="alpha" or field.attributes.type=="choice") %}{{field.attributes.default}} {% elif field.attributes.default and (field.attributes.type=="alpha" or field.attributes.type=="choice") %}"{{field.attributes.default}}"{%elif not field.attributes.default and field.attributes.missing %}{{field.attributes.missing}}{% else %}None{% endif %}): 75 | """ Corresponds to IDD Field `{{field.field_name}}` 76 | 77 | {%- for comment in field.attributes.note %} 78 | {{comment}} 79 | {%- endfor %} 80 | 81 | Args: 82 | value ({{ field.attributes.pytype }}): value for IDD Field `{{field.field_name}}` 83 | {%- if field.attributes.type == "choice" %} 84 | Accepted values are: 85 | {%- for k in field.attributes.key %} 86 | - {{ k }} 87 | {%- endfor %} 88 | {%- endif %} 89 | {%- if field.attributes.units %} 90 | Unit: {{ field.attributes.units }} 91 | {%- endif %} 92 | {%- if field.attributes.default %} 93 | Default value: {{ field.attributes.default }} 94 | {%- endif %} 95 | {%- if field.attributes.minimum %} 96 | value >= {{ field.attributes.minimum }} 97 | {%- endif %} 98 | {%- if field.attributes['minimum>'] %} 99 | value > {{ field.attributes['minimum>'] }} 100 | {%- endif %} 101 | {%- if field.attributes.maximum %} 102 | value <= {{ field.attributes.maximum }} 103 | {%- endif %} 104 | {%- if field.attributes['maximum<'] %} 105 | value < {{ field.attributes['maximum<'] }} 106 | {%- endif %} 107 | {%- if field.attributes.missing %} 108 | Missing value: {{ field.attributes.missing }} 109 | {%- endif %} 110 | if `value` is None it will not be checked against the 111 | specification and is assumed to be a missing value 112 | 113 | Raises: 114 | ValueError: if `value` is not a valid value 115 | """ 116 | {%- if field.attributes|length > 0 %} 117 | if value is not None: 118 | {%- endif %} 119 | try: 120 | value = {{ field.attributes.pytype }}(value) 121 | except ValueError: 122 | raise ValueError('value {} need to be of type {{ field.attributes.pytype }} ' 123 | 'for field `{{field.field_name}}`'.format(value)) 124 | {%- if field.attributes.pytype == "str" %} 125 | if ',' in value: 126 | raise ValueError('value should not contain a comma ' 127 | 'for field `{{field.field_name}}`') 128 | {%- endif %} 129 | {%- if field.attributes.minimum %} 130 | if value < {{ field.attributes.minimum }}: 131 | raise ValueError('value need to be greater or equal {{ field.attributes.minimum }} ' 132 | 'for field `{{field.field_name}}`') 133 | {%- endif %} 134 | {%- if field.attributes['minimum>'] %} 135 | if value <= {{ field.attributes['minimum>'] }}: 136 | raise ValueError('value need to be greater {{ field.attributes["minimum>"] }} ' 137 | 'for field `{{field.field_name}}`') 138 | {%- endif %} 139 | {%- if field.attributes.maximum %} 140 | if value > {{ field.attributes.maximum }}: 141 | raise ValueError('value need to be smaller {{ field.attributes.maximum }} ' 142 | 'for field `{{field.field_name}}`') 143 | {%- endif %} 144 | {%- if field.attributes['maximum<'] %} 145 | if value >= {{ field.attributes['maximum<'] }}: 146 | raise ValueError('value need to be smaller {{ field.attributes["maximum<"] }} ' 147 | 'for field `{{field.field_name}}`') 148 | {%- endif %} 149 | {%- if field.attributes.type == "choice" %} 150 | vals = set() 151 | {%- for k in field.attributes.key %} 152 | vals.add("{{k}}") 153 | {%- endfor %} 154 | if value not in vals: 155 | raise ValueError('value {} is not an accepted value for ' 156 | 'field `{{field.field_name}}`'.format(value)) 157 | {%- endif %} 158 | 159 | self._{{field.field_name}} = value 160 | {%- endif %} 161 | {%- endfor %} 162 | 163 | @classmethod 164 | def _to_str(cls, value): 165 | """ Represents values either as string or None values as empty string 166 | 167 | Args: 168 | value: a value 169 | """ 170 | if value is None: 171 | return '' 172 | else: 173 | return str(value) 174 | 175 | def export(self, top=True): 176 | """ Exports object to its string representation 177 | 178 | Args: 179 | top (bool): if True appends `internal_name` before values. 180 | All non list objects should be exported with value top=True, 181 | all list objects, that are embedded in as fields inlist objects 182 | should be exported with `top`=False 183 | 184 | Returns: 185 | str: The objects string representation 186 | """ 187 | out = [] 188 | if top: 189 | out.append(self._internal_name) 190 | {%- for field in fields %} 191 | {%- if field.is_list %} 192 | out.append(str(len(self.{{field.field_name}}s))) 193 | for obj in self.{{field.field_name}}s: 194 | out.append(obj.export(top=False)) 195 | {%- else %} 196 | out.append(self._to_str(self.{{field.field_name}})) 197 | {%- endif %} 198 | {%- endfor %} 199 | return ",".join(out) 200 | 201 | def __str__(self): 202 | return self.export(True) 203 | -------------------------------------------------------------------------------- /generator/templates/epw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ 3 | WARNING: This is an automatically generated file. 4 | It is based on the EPW IDD specification given in the document 5 | Auxiliary EnergyPlus Programs - Extra programs for EnergyPlus, 6 | Date: November 22 2013 7 | 8 | Do not expect that it actually works! 9 | 10 | Generation date: {{ generation_date}} 11 | 12 | """ 13 | from collections import OrderedDict 14 | import re 15 | 16 | {% for datadict in datadicts %} 17 | {{ datadict }} 18 | {% endfor %} 19 | 20 | class EPW(object): 21 | """ Represens a EnergyPlus EPW weather data file 22 | """ 23 | 24 | def __init__( 25 | self, 26 | {%- for obj in objs %} 27 | {{obj.var_name}}=None, 28 | {%- endfor %} 29 | weatherdata=None): 30 | """ Inits EPW with no data dictionary set.""" 31 | self._data = OrderedDict() 32 | {%- for obj in objs %} 33 | self._data["{{obj.internal_name}}"] = {{ obj.var_name }} 34 | {%- endfor %} 35 | if weatherdata is None: 36 | weatherdata = [] 37 | 38 | self._data["WEATHER DATA"] = weatherdata 39 | 40 | {%- for obj in objs %} 41 | @property 42 | def {{obj.var_name}}(self): 43 | """Get {{obj.var_name}} data dictionary object 44 | 45 | Returns: 46 | Object of type {{obj.class_name}} or None if not yet set 47 | """ 48 | return self._data["{{obj.internal_name}}"] 49 | 50 | @{{obj.var_name}}.setter 51 | def {{obj.var_name}}(self, value): 52 | """Set {{obj.var_name}} data dictionary object 53 | 54 | Args: 55 | value ({{obj.class_name}}): sets data dictionary for IDD {{obj.internal_name}} 56 | """ 57 | self._data["{{obj.internal_name}}"] = value 58 | 59 | {%- endfor %} 60 | 61 | @property 62 | def weatherdata(self): 63 | """Get list of WeatherData data dictionary objects 64 | 65 | Returns: 66 | list of WeatherData objects 67 | """ 68 | return self._data["WEATHER DATA"] 69 | 70 | @weatherdata.setter 71 | def weatherdata(self, weatherdata): 72 | """Set list of WeatherData data dictionary objects 73 | 74 | Args: 75 | weatherdata (list): list of WeatherData objects 76 | """ 77 | self._data["WEATHER DATA"] = weatherdata 78 | 79 | def add_weatherdata(self, data): 80 | """ Appends weather data 81 | 82 | Args: 83 | data (WeatherData): weather data object 84 | """ 85 | if not isinstance(data, WeatherData): 86 | raise ValueError('Weather data need to be of type WeatherData') 87 | self._data["WEATHER DATA"].append(data) 88 | 89 | def save(self, path, check=True): 90 | """ Save WeatherData in EPW format to path 91 | 92 | Args: 93 | path (str): path where EPW file should be saved 94 | """ 95 | with open(path, 'w') as f: 96 | if check: 97 | {%- for obj in objs %} 98 | if ("{{obj.internal_name}}" not in self._data or 99 | self._data["{{obj.internal_name}}"] is None): 100 | raise ValueError('{{ obj.var_name }} is not valid.') 101 | {%- endfor %} 102 | {%- for obj in objs %} 103 | if ("{{obj.internal_name}}" in self._data and 104 | self._data["{{obj.internal_name}}"] is not None): 105 | f.write(self._data["{{obj.internal_name}}"].export() + "\n") 106 | {%- endfor %} 107 | for item in self._data["WEATHER DATA"]: 108 | f.write(item.export(False) + "\n") 109 | 110 | @classmethod 111 | def _create_datadict(cls, internal_name): 112 | """ Creates an object depending on `internal_name` 113 | 114 | Args: 115 | internal_name (str): IDD name 116 | 117 | Raises: 118 | ValueError: if `internal_name` cannot be matched to a data dictionary object 119 | """ 120 | {%- for obj in objs %} 121 | if internal_name == "{{ obj.internal_name }}": 122 | return {{ obj.class_name }}() 123 | {%- endfor %} 124 | raise ValueError("No DataDictionary known for {}".format(internal_name)) 125 | 126 | def read(self, path): 127 | """Read EPW weather data from path 128 | 129 | Args: 130 | path (str): path to read weather data from 131 | """ 132 | with open(path, "r") as f: 133 | for line in f: 134 | line = line.strip() 135 | match_obj_name = re.search(r"^([A-Z][A-Z/ \d]+),", line) 136 | if match_obj_name is not None: 137 | internal_name = match_obj_name.group(1) 138 | if internal_name in self._data: 139 | self._data[internal_name] = self._create_datadict(internal_name) 140 | data_line = line[len(internal_name) + 1:] 141 | vals = data_line.strip().split(',') 142 | self._data[internal_name].read(vals) 143 | else: 144 | wd = WeatherData() 145 | wd.read(line.strip().split(',')) 146 | self.add_weatherdata(wd) 147 | -------------------------------------------------------------------------------- /generator/templates/test_class.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import {{ obj.class_name }},{% for field in obj.fields %}{% if field.is_list %}{{field.object_name}} ,{% endif %}{% endfor %}EPW 5 | 6 | 7 | class Test{{ obj.class_name }}(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_{{ obj.var_name }}(self): 16 | 17 | obj = {{ obj.class_name }}() 18 | {%- for field in obj.fields %} 19 | {%- if not field.is_list %} 20 | 21 | {%- if field.attributes.pytype == "str" %} 22 | {%- if field.attributes.type == "choice" %} 23 | var_{{field.field_name}} = "{{ field.attributes.key[0] }}" 24 | {%- else %} 25 | var_{{field.field_name}} = "{{field.field_name }}" 26 | {%- endif %} 27 | {%- elif field.attributes.pytype == "float" %} 28 | {%- if (field.attributes['maximum<'] or field.attributes.maximum) and (field.attributes['minimum>'] or field.attributes.minimum) %} 29 | var_{{field.field_name}} = ({%if field.attributes['maximum<'] %} ({{ field.attributes["maximum<"] }} - 1.0 ) {%- else %} {{ field.attributes.maximum }} {%- endif %} + {%if field.attributes['minimum>'] %} ({{ field.attributes["minimum>"] }} + 1.0 ) {%- else %} {{ field.attributes.minimum }} {%- endif %}) * 0.5 30 | {%- elif (field.attributes['maximum<'] or field.attributes.maximum) and not (field.attributes['minimum>'] or field.attributes.minimum) %} 31 | var_{{field.field_name}} = {%if field.attributes['maximum<'] %} ({{ field.attributes["maximum<"] }} - 1.0 ) {%- else %} {{ field.attributes.maximum }} {%- endif %} 32 | {%- else %} 33 | var_{{field.field_name}} = {{loop.index}}.{{loop.index}} 34 | {%- endif %} 35 | {%- elif field.attributes.pytype == "int" %} 36 | {%- if (field.attributes['maximum<'] or field.attributes.maximum) and (field.attributes['minimum>'] or field.attributes.minimum) %} 37 | var_{{field.field_name}} = int(({%if field.attributes['maximum<'] %} ({{ field.attributes["maximum<"] }} - 1) {%- else %} {{ field.attributes.maximum }} {%- endif %} + {%if field.attributes['minimum>'] %} ({{ field.attributes["minimum>"] }} + 1) {%- else %} {{ field.attributes.minimum }} {%- endif %}) * 0.5) 38 | {%- elif (field.attributes['maximum<'] or field.attributes.maximum) and not (field.attributes['minimum>'] or field.attributes.minimum) %} 39 | var_{{field.field_name}} = {%if field.attributes['maximum<'] %} ({{ field.attributes["maximum<"] }} - 1 ) {%- else %} {{ field.attributes.maximum }} {%- endif %} 40 | {%- else %} 41 | var_{{field.field_name}} = {{loop.index}} 42 | {%- endif %} 43 | {%- endif %} 44 | obj.{{field.field_name}} = var_{{field.field_name}} 45 | {%- else %} 46 | {{field.field_name}}_obj = {{objs[field.object_name].class_name}}() 47 | {%- for field2 in objs[field.object_name].fields %} 48 | {%- if field2.attributes.pytype == "str" %} 49 | {%- if field2.attributes.type == "choice" %} 50 | var_{{field.field_name}}_{{field2.field_name}} = "{{ field2.attributes.key[0] }}" 51 | {%- else %} 52 | var_{{field.field_name}}_{{field2.field_name}} = "{{field2.field_name }}" 53 | {%- endif %} 54 | {%- elif field2.attributes.pytype == "float" %} 55 | {%- if (field2.attributes['maximum<'] or field2.attributes.maximum) and (field2.attributes['minimum>'] or field2.attributes.minimum) %} 56 | var_{{field.field_name}}_{{field2.field_name}} = ({%if field2.attributes['maximum<'] %} ({{ field2.attributes["maximum<"] }} - 1.0 ) {%- else %} {{ field2.attributes.maximum }} {%- endif %} + {%if field2.attributes['minimum>'] %} ({{ field2.attributes["minimum>"] }} + 1.0 ) {%- else %} {{ field2.attributes.minimum }} {%- endif %}) * 0.5 57 | {%- elif (field.attributes['maximum<'] or field.attributes.maximum) and not (field.attributes['minimum>'] or field.attributes.minimum) %} 58 | var_{{field.field_name}}_{{field2.field_name}} = {%if field2.attributes['maximum<'] %} ({{ field2.attributes["maximum<"] }} - 1.0 ) {%- else %} {{ field2.attributes.maximum }} {%- endif %} 59 | {%- else %} 60 | var_{{field.field_name}}_{{field2.field_name}} = {{loop.index}}.{{loop.index}} 61 | {%- endif %} 62 | {%- elif field2.attributes.pytype == "int" %} 63 | {%- if (field2.attributes['maximum<'] or field2.attributes.maximum) and (field2.attributes['minimum>'] or field2.attributes.minimum) %} 64 | var_{{field.field_name}}_{{field2.field_name}} = int(({%if field2.attributes['maximum<'] %} ({{ field2.attributes["maximum<"] }} - 1) {%- else %} {{ field2.attributes.maximum }} {%- endif %} + {%if field2.attributes['minimum>'] %} ({{ field2.attributes["minimum>"] }} + 1) {%- else %} {{ field2.attributes.minimum }} {%- endif %}) * 0.5) 65 | {%- elif (field2.attributes['maximum<'] or field2.attributes.maximum) and not (field2.attributes['minimum>'] or field2.attributes.minimum) %} 66 | var_{{field.field_name}}_{{field2.field_name}} = {%if field2.attributes['maximum<'] %} ({{ field2.attributes["maximum<"] }} - 1 ) {%- else %} {{ field2.attributes.maximum }} {%- endif %} 67 | {%- else %} 68 | var_{{field.field_name}}_{{field2.field_name}} = {{loop.index}} 69 | {%- endif %} 70 | {%- endif %} 71 | {{field.field_name}}_obj.{{field2.field_name}} = var_{{field.field_name}}_{{field2.field_name}} 72 | {%- endfor %} 73 | obj.add_{{field.field_name}}({{field.field_name}}_obj) 74 | {%- endif %} 75 | {%- endfor %} 76 | 77 | epw = EPW({{ obj.var_name }}=obj) 78 | epw.save(self.path, check=False) 79 | 80 | epw2 = EPW() 81 | epw2.read(self.path) 82 | {%- for field in obj.fields %} 83 | {%- if not field.is_list %} 84 | {%- if field.attributes.pytype == "str" %} 85 | self.assertEqual(epw2.{{obj.var_name}}.{{field.field_name}}, var_{{field.field_name}}) 86 | {%- elif field.attributes.pytype == "int" %} 87 | self.assertEqual(epw2.{{obj.var_name}}.{{field.field_name}}, var_{{field.field_name}}) 88 | {%- elif field.attributes.pytype == "float" %} 89 | self.assertAlmostEqual(epw2.{{obj.var_name}}.{{field.field_name}}, var_{{field.field_name}}) 90 | {%- endif %} 91 | {%- else %} 92 | {%- for field2 in objs[field.object_name].fields %} 93 | {%- if field2.attributes.pytype == "str" %} 94 | self.assertEqual(epw2.{{obj.var_name}}.{{field.field_name}}s[0].{{field2.field_name}}, var_{{field.field_name}}_{{field2.field_name}}) 95 | {%- elif field2.attributes.pytype == "int" %} 96 | self.assertEqual(epw2.{{obj.var_name}}.{{field.field_name}}s[0].{{field2.field_name}}, var_{{field.field_name}}_{{field2.field_name}}) 97 | {%- elif field2.attributes.pytype == "float" %} 98 | self.assertAlmostEqual(epw2.{{obj.var_name}}.{{field.field_name}}s[0].{{field2.field_name}}, var_{{field.field_name}}_{{field2.field_name}}) 99 | {%- endif %} 100 | {%- endfor %} 101 | {%- endif %} 102 | {%- endfor %} -------------------------------------------------------------------------------- /pyepw/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbuffat/pyepw/373d4d3c8386c8d35789f086ac5f6018c2711745/pyepw/__init__.py -------------------------------------------------------------------------------- /pyepw/calc.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Nov 24, 2014 3 | 4 | @author: rene 5 | ''' 6 | from scipy.constants.constants import sigma, C2K 7 | import math 8 | 9 | 10 | def calc_horizontal_infrared_radiation_intensity(weatherdata): 11 | """ Estimates the global horizontal infrared radiation intensity based 12 | on drybulb, dewpoint and opaque sky cover. 13 | References: 14 | Walton, G. N. 1983. Thermal Analysis Research Program Reference Manual. NBSSIR 83- 15 | 2655. National Bureau of Standards, p. 21. 16 | Clark, G. and C. Allen, "The Estimation of Atmospheric Radiation for Clear and Cloudy 17 | Skies," Proceedings 2nd National Passive Solar Conference (AS/ISES), 1978, pp. 675-678. 18 | """ 19 | temp_drybulb_K = C2K(weatherdata._dry_bulb_temperature) 20 | temp_dew_K = C2K(weatherdata.dew_point_temperature) 21 | N = weatherdata.opaque_sky_cover 22 | sky_emissivity = (0.787 + 0.764 * math.log(temp_dew_K / C2K(0.0)) * 23 | (1.0 + 0.0224 * N - 0.0035 * N ** 2 + 0.00028 * N ** 3)) 24 | hor_id = sky_emissivity * sigma * temp_drybulb_K ** 4 25 | weatherdata.horizontal_infrared_radiation_intensity = hor_id 26 | 27 | 28 | if __name__ == '__main__': 29 | pass 30 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name='pyepw', 4 | version='0.2-dev', 5 | description='Python library to read, modify and create EnergyPlus Weather (EPW) files', 6 | url='https://github.com/rbuffat/pyepw', 7 | download_url='https://github.com/rbuffat/pyepw/tarball/0.1', 8 | author='Rene Buffat', 9 | author_email='buffat@gmail.com', 10 | license='Apache License 2.0', 11 | classifiers=['Development Status :: 2 - Pre-Alpha'], 12 | packages=['pyepw'], 13 | keywords=['EnergyPlus', 'EPW']) 14 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbuffat/pyepw/373d4d3c8386c8d35789f086ac5f6018c2711745/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_comments_1.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import Comments1, EPW 5 | 6 | 7 | class TestComments1(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_comments_1(self): 16 | 17 | obj = Comments1() 18 | var_comments_1 = "comments_1" 19 | obj.comments_1 = var_comments_1 20 | 21 | epw = EPW(comments_1=obj) 22 | epw.save(self.path, check=False) 23 | 24 | epw2 = EPW() 25 | epw2.read(self.path) 26 | self.assertEqual(epw2.comments_1.comments_1, var_comments_1) 27 | -------------------------------------------------------------------------------- /tests/test_comments_2.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import Comments2, EPW 5 | 6 | 7 | class TestComments2(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_comments_2(self): 16 | 17 | obj = Comments2() 18 | var_comments_2 = "comments_2" 19 | obj.comments_2 = var_comments_2 20 | 21 | epw = EPW(comments_2=obj) 22 | epw.save(self.path, check=False) 23 | 24 | epw2 = EPW() 25 | epw2.read(self.path) 26 | self.assertEqual(epw2.comments_2.comments_2, var_comments_2) 27 | -------------------------------------------------------------------------------- /tests/test_data_periods.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import DataPeriods, DataPeriod, EPW 5 | 6 | 7 | class TestDataPeriods(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_data_periods(self): 16 | 17 | obj = DataPeriods() 18 | data_period_obj = DataPeriod() 19 | var_data_period_number_of_records_per_hour = 1 20 | data_period_obj.number_of_records_per_hour = var_data_period_number_of_records_per_hour 21 | var_data_period_data_period_name_or_description = "data_period_name_or_description" 22 | data_period_obj.data_period_name_or_description = var_data_period_data_period_name_or_description 23 | var_data_period_data_period_start_day_of_week = "Sunday" 24 | data_period_obj.data_period_start_day_of_week = var_data_period_data_period_start_day_of_week 25 | var_data_period_data_period_start_day = "data_period_start_day" 26 | data_period_obj.data_period_start_day = var_data_period_data_period_start_day 27 | var_data_period_data_period_end_day = "data_period_end_day" 28 | data_period_obj.data_period_end_day = var_data_period_data_period_end_day 29 | obj.add_data_period(data_period_obj) 30 | 31 | epw = EPW(data_periods=obj) 32 | epw.save(self.path, check=False) 33 | 34 | epw2 = EPW() 35 | epw2.read(self.path) 36 | self.assertEqual( 37 | epw2.data_periods.data_periods[0].number_of_records_per_hour, 38 | var_data_period_number_of_records_per_hour) 39 | self.assertEqual( 40 | epw2.data_periods.data_periods[0].data_period_name_or_description, 41 | var_data_period_data_period_name_or_description) 42 | self.assertEqual( 43 | epw2.data_periods.data_periods[0].data_period_start_day_of_week, 44 | var_data_period_data_period_start_day_of_week) 45 | self.assertEqual( 46 | epw2.data_periods.data_periods[0].data_period_start_day, 47 | var_data_period_data_period_start_day) 48 | self.assertEqual( 49 | epw2.data_periods.data_periods[0].data_period_end_day, 50 | var_data_period_data_period_end_day) 51 | -------------------------------------------------------------------------------- /tests/test_design_conditions.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import DesignConditions, DesignCondition, EPW 5 | 6 | 7 | class TestDesignConditions(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_design_conditions(self): 16 | 17 | obj = DesignConditions() 18 | design_condition_obj = DesignCondition() 19 | var_design_condition_title_of_design_condition = "title_of_design_condition" 20 | design_condition_obj.title_of_design_condition = var_design_condition_title_of_design_condition 21 | var_design_condition_unkown_field = "unkown_field" 22 | design_condition_obj.unkown_field = var_design_condition_unkown_field 23 | var_design_condition_design_stat_heating = "Heating" 24 | design_condition_obj.design_stat_heating = var_design_condition_design_stat_heating 25 | var_design_condition_coldestmonth = int((12 + 1) * 0.5) 26 | design_condition_obj.coldestmonth = var_design_condition_coldestmonth 27 | var_design_condition_db996 = 5.5 28 | design_condition_obj.db996 = var_design_condition_db996 29 | var_design_condition_db990 = 6.6 30 | design_condition_obj.db990 = var_design_condition_db990 31 | var_design_condition_dp996 = 7.7 32 | design_condition_obj.dp996 = var_design_condition_dp996 33 | var_design_condition_hr_dp996 = 8.8 34 | design_condition_obj.hr_dp996 = var_design_condition_hr_dp996 35 | var_design_condition_db_dp996 = 9.9 36 | design_condition_obj.db_dp996 = var_design_condition_db_dp996 37 | var_design_condition_dp990 = 10.10 38 | design_condition_obj.dp990 = var_design_condition_dp990 39 | var_design_condition_hr_dp990 = 11.11 40 | design_condition_obj.hr_dp990 = var_design_condition_hr_dp990 41 | var_design_condition_db_dp990 = 12.12 42 | design_condition_obj.db_dp990 = var_design_condition_db_dp990 43 | var_design_condition_ws004c = 13.13 44 | design_condition_obj.ws004c = var_design_condition_ws004c 45 | var_design_condition_db_ws004c = 14.14 46 | design_condition_obj.db_ws004c = var_design_condition_db_ws004c 47 | var_design_condition_ws010c = 15.15 48 | design_condition_obj.ws010c = var_design_condition_ws010c 49 | var_design_condition_db_ws010c = 16.16 50 | design_condition_obj.db_ws010c = var_design_condition_db_ws010c 51 | var_design_condition_ws_db996 = 17.17 52 | design_condition_obj.ws_db996 = var_design_condition_ws_db996 53 | var_design_condition_wd_db996 = 18.18 54 | design_condition_obj.wd_db996 = var_design_condition_wd_db996 55 | var_design_condition_design_stat_cooling = "Cooling" 56 | design_condition_obj.design_stat_cooling = var_design_condition_design_stat_cooling 57 | var_design_condition_hottestmonth = int((12 + 1) * 0.5) 58 | design_condition_obj.hottestmonth = var_design_condition_hottestmonth 59 | var_design_condition_dbr = 21.21 60 | design_condition_obj.dbr = var_design_condition_dbr 61 | var_design_condition_db004 = 22.22 62 | design_condition_obj.db004 = var_design_condition_db004 63 | var_design_condition_wb_db004 = 23.23 64 | design_condition_obj.wb_db004 = var_design_condition_wb_db004 65 | var_design_condition_db010 = 24.24 66 | design_condition_obj.db010 = var_design_condition_db010 67 | var_design_condition_wb_db010 = 25.25 68 | design_condition_obj.wb_db010 = var_design_condition_wb_db010 69 | var_design_condition_db020 = 26.26 70 | design_condition_obj.db020 = var_design_condition_db020 71 | var_design_condition_wb_db020 = 27.27 72 | design_condition_obj.wb_db020 = var_design_condition_wb_db020 73 | var_design_condition_wb004 = 28.28 74 | design_condition_obj.wb004 = var_design_condition_wb004 75 | var_design_condition_db_wb004 = 29.29 76 | design_condition_obj.db_wb004 = var_design_condition_db_wb004 77 | var_design_condition_wb010 = 30.30 78 | design_condition_obj.wb010 = var_design_condition_wb010 79 | var_design_condition_db_wb010 = 31.31 80 | design_condition_obj.db_wb010 = var_design_condition_db_wb010 81 | var_design_condition_wb020 = 32.32 82 | design_condition_obj.wb020 = var_design_condition_wb020 83 | var_design_condition_db_wb020 = 33.33 84 | design_condition_obj.db_wb020 = var_design_condition_db_wb020 85 | var_design_condition_ws_db004 = 34.34 86 | design_condition_obj.ws_db004 = var_design_condition_ws_db004 87 | var_design_condition_wd_db004 = 35.35 88 | design_condition_obj.wd_db004 = var_design_condition_wd_db004 89 | var_design_condition_dp004 = 36.36 90 | design_condition_obj.dp004 = var_design_condition_dp004 91 | var_design_condition_hr_dp004 = 37.37 92 | design_condition_obj.hr_dp004 = var_design_condition_hr_dp004 93 | var_design_condition_db_dp004 = 38.38 94 | design_condition_obj.db_dp004 = var_design_condition_db_dp004 95 | var_design_condition_dp010 = 39.39 96 | design_condition_obj.dp010 = var_design_condition_dp010 97 | var_design_condition_hr_dp010 = 40.40 98 | design_condition_obj.hr_dp010 = var_design_condition_hr_dp010 99 | var_design_condition_db_dp010 = 41.41 100 | design_condition_obj.db_dp010 = var_design_condition_db_dp010 101 | var_design_condition_dp020 = 42.42 102 | design_condition_obj.dp020 = var_design_condition_dp020 103 | var_design_condition_hr_dp020 = 43.43 104 | design_condition_obj.hr_dp020 = var_design_condition_hr_dp020 105 | var_design_condition_db_dp020 = 44.44 106 | design_condition_obj.db_dp020 = var_design_condition_db_dp020 107 | var_design_condition_en004 = 45.45 108 | design_condition_obj.en004 = var_design_condition_en004 109 | var_design_condition_db_en004 = 46.46 110 | design_condition_obj.db_en004 = var_design_condition_db_en004 111 | var_design_condition_en010 = 47.47 112 | design_condition_obj.en010 = var_design_condition_en010 113 | var_design_condition_db_en010 = 48.48 114 | design_condition_obj.db_en010 = var_design_condition_db_en010 115 | var_design_condition_en020 = 49.49 116 | design_condition_obj.en020 = var_design_condition_en020 117 | var_design_condition_db_en020 = 50.50 118 | design_condition_obj.db_en020 = var_design_condition_db_en020 119 | var_design_condition_hrs_84_and_db12_8_or_20_6 = 51.51 120 | design_condition_obj.hrs_84_and_db12_8_or_20_6 = var_design_condition_hrs_84_and_db12_8_or_20_6 121 | var_design_condition_design_stat_extremes = "Extremes" 122 | design_condition_obj.design_stat_extremes = var_design_condition_design_stat_extremes 123 | var_design_condition_ws010 = 53.53 124 | design_condition_obj.ws010 = var_design_condition_ws010 125 | var_design_condition_ws025 = 54.54 126 | design_condition_obj.ws025 = var_design_condition_ws025 127 | var_design_condition_ws050 = 55.55 128 | design_condition_obj.ws050 = var_design_condition_ws050 129 | var_design_condition_wbmax = 56.56 130 | design_condition_obj.wbmax = var_design_condition_wbmax 131 | var_design_condition_dbmin_mean = 57.57 132 | design_condition_obj.dbmin_mean = var_design_condition_dbmin_mean 133 | var_design_condition_dbmax_mean = 58.58 134 | design_condition_obj.dbmax_mean = var_design_condition_dbmax_mean 135 | var_design_condition_dbmin_stddev = 59.59 136 | design_condition_obj.dbmin_stddev = var_design_condition_dbmin_stddev 137 | var_design_condition_dbmax_stddev = 60.60 138 | design_condition_obj.dbmax_stddev = var_design_condition_dbmax_stddev 139 | var_design_condition_dbmin05years = 61.61 140 | design_condition_obj.dbmin05years = var_design_condition_dbmin05years 141 | var_design_condition_dbmax05years = 62.62 142 | design_condition_obj.dbmax05years = var_design_condition_dbmax05years 143 | var_design_condition_dbmin10years = 63.63 144 | design_condition_obj.dbmin10years = var_design_condition_dbmin10years 145 | var_design_condition_dbmax10years = 64.64 146 | design_condition_obj.dbmax10years = var_design_condition_dbmax10years 147 | var_design_condition_dbmin20years = 65.65 148 | design_condition_obj.dbmin20years = var_design_condition_dbmin20years 149 | var_design_condition_dbmax20years = 66.66 150 | design_condition_obj.dbmax20years = var_design_condition_dbmax20years 151 | var_design_condition_dbmin50years = 67.67 152 | design_condition_obj.dbmin50years = var_design_condition_dbmin50years 153 | var_design_condition_dbmax50years = 68.68 154 | design_condition_obj.dbmax50years = var_design_condition_dbmax50years 155 | obj.add_design_condition(design_condition_obj) 156 | 157 | epw = EPW(design_conditions=obj) 158 | epw.save(self.path, check=False) 159 | 160 | epw2 = EPW() 161 | epw2.read(self.path) 162 | self.assertEqual( 163 | epw2.design_conditions.design_conditions[0].title_of_design_condition, 164 | var_design_condition_title_of_design_condition) 165 | self.assertEqual( 166 | epw2.design_conditions.design_conditions[0].unkown_field, 167 | var_design_condition_unkown_field) 168 | self.assertEqual( 169 | epw2.design_conditions.design_conditions[0].design_stat_heating, 170 | var_design_condition_design_stat_heating) 171 | self.assertEqual( 172 | epw2.design_conditions.design_conditions[0].coldestmonth, 173 | var_design_condition_coldestmonth) 174 | self.assertAlmostEqual( 175 | epw2.design_conditions.design_conditions[0].db996, 176 | var_design_condition_db996) 177 | self.assertAlmostEqual( 178 | epw2.design_conditions.design_conditions[0].db990, 179 | var_design_condition_db990) 180 | self.assertAlmostEqual( 181 | epw2.design_conditions.design_conditions[0].dp996, 182 | var_design_condition_dp996) 183 | self.assertAlmostEqual( 184 | epw2.design_conditions.design_conditions[0].hr_dp996, 185 | var_design_condition_hr_dp996) 186 | self.assertAlmostEqual( 187 | epw2.design_conditions.design_conditions[0].db_dp996, 188 | var_design_condition_db_dp996) 189 | self.assertAlmostEqual( 190 | epw2.design_conditions.design_conditions[0].dp990, 191 | var_design_condition_dp990) 192 | self.assertAlmostEqual( 193 | epw2.design_conditions.design_conditions[0].hr_dp990, 194 | var_design_condition_hr_dp990) 195 | self.assertAlmostEqual( 196 | epw2.design_conditions.design_conditions[0].db_dp990, 197 | var_design_condition_db_dp990) 198 | self.assertAlmostEqual( 199 | epw2.design_conditions.design_conditions[0].ws004c, 200 | var_design_condition_ws004c) 201 | self.assertAlmostEqual( 202 | epw2.design_conditions.design_conditions[0].db_ws004c, 203 | var_design_condition_db_ws004c) 204 | self.assertAlmostEqual( 205 | epw2.design_conditions.design_conditions[0].ws010c, 206 | var_design_condition_ws010c) 207 | self.assertAlmostEqual( 208 | epw2.design_conditions.design_conditions[0].db_ws010c, 209 | var_design_condition_db_ws010c) 210 | self.assertAlmostEqual( 211 | epw2.design_conditions.design_conditions[0].ws_db996, 212 | var_design_condition_ws_db996) 213 | self.assertAlmostEqual( 214 | epw2.design_conditions.design_conditions[0].wd_db996, 215 | var_design_condition_wd_db996) 216 | self.assertEqual( 217 | epw2.design_conditions.design_conditions[0].design_stat_cooling, 218 | var_design_condition_design_stat_cooling) 219 | self.assertEqual( 220 | epw2.design_conditions.design_conditions[0].hottestmonth, 221 | var_design_condition_hottestmonth) 222 | self.assertAlmostEqual( 223 | epw2.design_conditions.design_conditions[0].dbr, 224 | var_design_condition_dbr) 225 | self.assertAlmostEqual( 226 | epw2.design_conditions.design_conditions[0].db004, 227 | var_design_condition_db004) 228 | self.assertAlmostEqual( 229 | epw2.design_conditions.design_conditions[0].wb_db004, 230 | var_design_condition_wb_db004) 231 | self.assertAlmostEqual( 232 | epw2.design_conditions.design_conditions[0].db010, 233 | var_design_condition_db010) 234 | self.assertAlmostEqual( 235 | epw2.design_conditions.design_conditions[0].wb_db010, 236 | var_design_condition_wb_db010) 237 | self.assertAlmostEqual( 238 | epw2.design_conditions.design_conditions[0].db020, 239 | var_design_condition_db020) 240 | self.assertAlmostEqual( 241 | epw2.design_conditions.design_conditions[0].wb_db020, 242 | var_design_condition_wb_db020) 243 | self.assertAlmostEqual( 244 | epw2.design_conditions.design_conditions[0].wb004, 245 | var_design_condition_wb004) 246 | self.assertAlmostEqual( 247 | epw2.design_conditions.design_conditions[0].db_wb004, 248 | var_design_condition_db_wb004) 249 | self.assertAlmostEqual( 250 | epw2.design_conditions.design_conditions[0].wb010, 251 | var_design_condition_wb010) 252 | self.assertAlmostEqual( 253 | epw2.design_conditions.design_conditions[0].db_wb010, 254 | var_design_condition_db_wb010) 255 | self.assertAlmostEqual( 256 | epw2.design_conditions.design_conditions[0].wb020, 257 | var_design_condition_wb020) 258 | self.assertAlmostEqual( 259 | epw2.design_conditions.design_conditions[0].db_wb020, 260 | var_design_condition_db_wb020) 261 | self.assertAlmostEqual( 262 | epw2.design_conditions.design_conditions[0].ws_db004, 263 | var_design_condition_ws_db004) 264 | self.assertAlmostEqual( 265 | epw2.design_conditions.design_conditions[0].wd_db004, 266 | var_design_condition_wd_db004) 267 | self.assertAlmostEqual( 268 | epw2.design_conditions.design_conditions[0].dp004, 269 | var_design_condition_dp004) 270 | self.assertAlmostEqual( 271 | epw2.design_conditions.design_conditions[0].hr_dp004, 272 | var_design_condition_hr_dp004) 273 | self.assertAlmostEqual( 274 | epw2.design_conditions.design_conditions[0].db_dp004, 275 | var_design_condition_db_dp004) 276 | self.assertAlmostEqual( 277 | epw2.design_conditions.design_conditions[0].dp010, 278 | var_design_condition_dp010) 279 | self.assertAlmostEqual( 280 | epw2.design_conditions.design_conditions[0].hr_dp010, 281 | var_design_condition_hr_dp010) 282 | self.assertAlmostEqual( 283 | epw2.design_conditions.design_conditions[0].db_dp010, 284 | var_design_condition_db_dp010) 285 | self.assertAlmostEqual( 286 | epw2.design_conditions.design_conditions[0].dp020, 287 | var_design_condition_dp020) 288 | self.assertAlmostEqual( 289 | epw2.design_conditions.design_conditions[0].hr_dp020, 290 | var_design_condition_hr_dp020) 291 | self.assertAlmostEqual( 292 | epw2.design_conditions.design_conditions[0].db_dp020, 293 | var_design_condition_db_dp020) 294 | self.assertAlmostEqual( 295 | epw2.design_conditions.design_conditions[0].en004, 296 | var_design_condition_en004) 297 | self.assertAlmostEqual( 298 | epw2.design_conditions.design_conditions[0].db_en004, 299 | var_design_condition_db_en004) 300 | self.assertAlmostEqual( 301 | epw2.design_conditions.design_conditions[0].en010, 302 | var_design_condition_en010) 303 | self.assertAlmostEqual( 304 | epw2.design_conditions.design_conditions[0].db_en010, 305 | var_design_condition_db_en010) 306 | self.assertAlmostEqual( 307 | epw2.design_conditions.design_conditions[0].en020, 308 | var_design_condition_en020) 309 | self.assertAlmostEqual( 310 | epw2.design_conditions.design_conditions[0].db_en020, 311 | var_design_condition_db_en020) 312 | self.assertAlmostEqual( 313 | epw2.design_conditions.design_conditions[0].hrs_84_and_db12_8_or_20_6, 314 | var_design_condition_hrs_84_and_db12_8_or_20_6) 315 | self.assertEqual( 316 | epw2.design_conditions.design_conditions[0].design_stat_extremes, 317 | var_design_condition_design_stat_extremes) 318 | self.assertAlmostEqual( 319 | epw2.design_conditions.design_conditions[0].ws010, 320 | var_design_condition_ws010) 321 | self.assertAlmostEqual( 322 | epw2.design_conditions.design_conditions[0].ws025, 323 | var_design_condition_ws025) 324 | self.assertAlmostEqual( 325 | epw2.design_conditions.design_conditions[0].ws050, 326 | var_design_condition_ws050) 327 | self.assertAlmostEqual( 328 | epw2.design_conditions.design_conditions[0].wbmax, 329 | var_design_condition_wbmax) 330 | self.assertAlmostEqual( 331 | epw2.design_conditions.design_conditions[0].dbmin_mean, 332 | var_design_condition_dbmin_mean) 333 | self.assertAlmostEqual( 334 | epw2.design_conditions.design_conditions[0].dbmax_mean, 335 | var_design_condition_dbmax_mean) 336 | self.assertAlmostEqual( 337 | epw2.design_conditions.design_conditions[0].dbmin_stddev, 338 | var_design_condition_dbmin_stddev) 339 | self.assertAlmostEqual( 340 | epw2.design_conditions.design_conditions[0].dbmax_stddev, 341 | var_design_condition_dbmax_stddev) 342 | self.assertAlmostEqual( 343 | epw2.design_conditions.design_conditions[0].dbmin05years, 344 | var_design_condition_dbmin05years) 345 | self.assertAlmostEqual( 346 | epw2.design_conditions.design_conditions[0].dbmax05years, 347 | var_design_condition_dbmax05years) 348 | self.assertAlmostEqual( 349 | epw2.design_conditions.design_conditions[0].dbmin10years, 350 | var_design_condition_dbmin10years) 351 | self.assertAlmostEqual( 352 | epw2.design_conditions.design_conditions[0].dbmax10years, 353 | var_design_condition_dbmax10years) 354 | self.assertAlmostEqual( 355 | epw2.design_conditions.design_conditions[0].dbmin20years, 356 | var_design_condition_dbmin20years) 357 | self.assertAlmostEqual( 358 | epw2.design_conditions.design_conditions[0].dbmax20years, 359 | var_design_condition_dbmax20years) 360 | self.assertAlmostEqual( 361 | epw2.design_conditions.design_conditions[0].dbmin50years, 362 | var_design_condition_dbmin50years) 363 | self.assertAlmostEqual( 364 | epw2.design_conditions.design_conditions[0].dbmax50years, 365 | var_design_condition_dbmax50years) 366 | -------------------------------------------------------------------------------- /tests/test_ground_temperatures.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import GroundTemperatures, GroundTemperature, EPW 5 | 6 | 7 | class TestGroundTemperatures(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_ground_temperatures(self): 16 | 17 | obj = GroundTemperatures() 18 | ground_temperature_obj = GroundTemperature() 19 | var_ground_temperature_ground_temperature_depth = 1.1 20 | ground_temperature_obj.ground_temperature_depth = var_ground_temperature_ground_temperature_depth 21 | var_ground_temperature_depth_soil_conductivity = 2.2 22 | ground_temperature_obj.depth_soil_conductivity = var_ground_temperature_depth_soil_conductivity 23 | var_ground_temperature_depth_soil_density = 3.3 24 | ground_temperature_obj.depth_soil_density = var_ground_temperature_depth_soil_density 25 | var_ground_temperature_depth_soil_specific_heat = 4.4 26 | ground_temperature_obj.depth_soil_specific_heat = var_ground_temperature_depth_soil_specific_heat 27 | var_ground_temperature_depth_january_average_ground_temperature = 5.5 28 | ground_temperature_obj.depth_january_average_ground_temperature = var_ground_temperature_depth_january_average_ground_temperature 29 | var_ground_temperature_depth_february_average_ground_temperature = 6.6 30 | ground_temperature_obj.depth_february_average_ground_temperature = var_ground_temperature_depth_february_average_ground_temperature 31 | var_ground_temperature_depth_march_average_ground_temperature = 7.7 32 | ground_temperature_obj.depth_march_average_ground_temperature = var_ground_temperature_depth_march_average_ground_temperature 33 | var_ground_temperature_depth_april_average_ground_temperature = 8.8 34 | ground_temperature_obj.depth_april_average_ground_temperature = var_ground_temperature_depth_april_average_ground_temperature 35 | var_ground_temperature_depth_may_average_ground_temperature = 9.9 36 | ground_temperature_obj.depth_may_average_ground_temperature = var_ground_temperature_depth_may_average_ground_temperature 37 | var_ground_temperature_depth_june_average_ground_temperature = 10.10 38 | ground_temperature_obj.depth_june_average_ground_temperature = var_ground_temperature_depth_june_average_ground_temperature 39 | var_ground_temperature_depth_july_average_ground_temperature = 11.11 40 | ground_temperature_obj.depth_july_average_ground_temperature = var_ground_temperature_depth_july_average_ground_temperature 41 | var_ground_temperature_depth_august_average_ground_temperature = 12.12 42 | ground_temperature_obj.depth_august_average_ground_temperature = var_ground_temperature_depth_august_average_ground_temperature 43 | var_ground_temperature_depth_september_average_ground_temperature = 13.13 44 | ground_temperature_obj.depth_september_average_ground_temperature = var_ground_temperature_depth_september_average_ground_temperature 45 | var_ground_temperature_depth_october_average_ground_temperature = 14.14 46 | ground_temperature_obj.depth_october_average_ground_temperature = var_ground_temperature_depth_october_average_ground_temperature 47 | var_ground_temperature_depth_november_average_ground_temperature = 15.15 48 | ground_temperature_obj.depth_november_average_ground_temperature = var_ground_temperature_depth_november_average_ground_temperature 49 | var_ground_temperature_depth_december_average_ground_temperature = 16.16 50 | ground_temperature_obj.depth_december_average_ground_temperature = var_ground_temperature_depth_december_average_ground_temperature 51 | obj.add_ground_temperature(ground_temperature_obj) 52 | 53 | epw = EPW(ground_temperatures=obj) 54 | epw.save(self.path, check=False) 55 | 56 | epw2 = EPW() 57 | epw2.read(self.path) 58 | self.assertAlmostEqual( 59 | epw2.ground_temperatures.ground_temperatures[0].ground_temperature_depth, 60 | var_ground_temperature_ground_temperature_depth) 61 | self.assertAlmostEqual( 62 | epw2.ground_temperatures.ground_temperatures[0].depth_soil_conductivity, 63 | var_ground_temperature_depth_soil_conductivity) 64 | self.assertAlmostEqual( 65 | epw2.ground_temperatures.ground_temperatures[0].depth_soil_density, 66 | var_ground_temperature_depth_soil_density) 67 | self.assertAlmostEqual( 68 | epw2.ground_temperatures.ground_temperatures[0].depth_soil_specific_heat, 69 | var_ground_temperature_depth_soil_specific_heat) 70 | self.assertAlmostEqual( 71 | epw2.ground_temperatures.ground_temperatures[0].depth_january_average_ground_temperature, 72 | var_ground_temperature_depth_january_average_ground_temperature) 73 | self.assertAlmostEqual( 74 | epw2.ground_temperatures.ground_temperatures[0].depth_february_average_ground_temperature, 75 | var_ground_temperature_depth_february_average_ground_temperature) 76 | self.assertAlmostEqual( 77 | epw2.ground_temperatures.ground_temperatures[0].depth_march_average_ground_temperature, 78 | var_ground_temperature_depth_march_average_ground_temperature) 79 | self.assertAlmostEqual( 80 | epw2.ground_temperatures.ground_temperatures[0].depth_april_average_ground_temperature, 81 | var_ground_temperature_depth_april_average_ground_temperature) 82 | self.assertAlmostEqual( 83 | epw2.ground_temperatures.ground_temperatures[0].depth_may_average_ground_temperature, 84 | var_ground_temperature_depth_may_average_ground_temperature) 85 | self.assertAlmostEqual( 86 | epw2.ground_temperatures.ground_temperatures[0].depth_june_average_ground_temperature, 87 | var_ground_temperature_depth_june_average_ground_temperature) 88 | self.assertAlmostEqual( 89 | epw2.ground_temperatures.ground_temperatures[0].depth_july_average_ground_temperature, 90 | var_ground_temperature_depth_july_average_ground_temperature) 91 | self.assertAlmostEqual( 92 | epw2.ground_temperatures.ground_temperatures[0].depth_august_average_ground_temperature, 93 | var_ground_temperature_depth_august_average_ground_temperature) 94 | self.assertAlmostEqual( 95 | epw2.ground_temperatures.ground_temperatures[0].depth_september_average_ground_temperature, 96 | var_ground_temperature_depth_september_average_ground_temperature) 97 | self.assertAlmostEqual( 98 | epw2.ground_temperatures.ground_temperatures[0].depth_october_average_ground_temperature, 99 | var_ground_temperature_depth_october_average_ground_temperature) 100 | self.assertAlmostEqual( 101 | epw2.ground_temperatures.ground_temperatures[0].depth_november_average_ground_temperature, 102 | var_ground_temperature_depth_november_average_ground_temperature) 103 | self.assertAlmostEqual( 104 | epw2.ground_temperatures.ground_temperatures[0].depth_december_average_ground_temperature, 105 | var_ground_temperature_depth_december_average_ground_temperature) 106 | -------------------------------------------------------------------------------- /tests/test_holidays_or_daylight_savings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import HolidaysOrDaylightSavings, Holiday, EPW 5 | 6 | 7 | class TestHolidaysOrDaylightSavings(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_holidays_or_daylight_savings(self): 16 | 17 | obj = HolidaysOrDaylightSavings() 18 | var_leapyear_observed = "Yes" 19 | obj.leapyear_observed = var_leapyear_observed 20 | var_daylight_saving_start_day = "daylight_saving_start_day" 21 | obj.daylight_saving_start_day = var_daylight_saving_start_day 22 | var_daylight_saving_end_day = "daylight_saving_end_day" 23 | obj.daylight_saving_end_day = var_daylight_saving_end_day 24 | holiday_obj = Holiday() 25 | var_holiday_holiday_name = "holiday_name" 26 | holiday_obj.holiday_name = var_holiday_holiday_name 27 | var_holiday_holiday_day = "holiday_day" 28 | holiday_obj.holiday_day = var_holiday_holiday_day 29 | obj.add_holiday(holiday_obj) 30 | 31 | epw = EPW(holidays_or_daylight_savings=obj) 32 | epw.save(self.path, check=False) 33 | 34 | epw2 = EPW() 35 | epw2.read(self.path) 36 | self.assertEqual( 37 | epw2.holidays_or_daylight_savings.leapyear_observed, 38 | var_leapyear_observed) 39 | self.assertEqual( 40 | epw2.holidays_or_daylight_savings.daylight_saving_start_day, 41 | var_daylight_saving_start_day) 42 | self.assertEqual( 43 | epw2.holidays_or_daylight_savings.daylight_saving_end_day, 44 | var_daylight_saving_end_day) 45 | self.assertEqual( 46 | epw2.holidays_or_daylight_savings.holidays[0].holiday_name, 47 | var_holiday_holiday_name) 48 | self.assertEqual( 49 | epw2.holidays_or_daylight_savings.holidays[0].holiday_day, 50 | var_holiday_holiday_day) 51 | -------------------------------------------------------------------------------- /tests/test_location.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import Location, EPW 5 | 6 | 7 | class TestLocation(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_location(self): 16 | 17 | obj = Location() 18 | var_city = "city" 19 | obj.city = var_city 20 | var_state_province_region = "state_province_region" 21 | obj.state_province_region = var_state_province_region 22 | var_country = "country" 23 | obj.country = var_country 24 | var_source = "source" 25 | obj.source = var_source 26 | var_wmo = "wmo" 27 | obj.wmo = var_wmo 28 | var_latitude = (90.0 + -90.0) * 0.5 29 | obj.latitude = var_latitude 30 | var_longitude = (180.0 + -180.0) * 0.5 31 | obj.longitude = var_longitude 32 | var_timezone = (12.0 + -12.0) * 0.5 33 | obj.timezone = var_timezone 34 | var_elevation = ((9999.9 - 1.0) + -1000.0) * 0.5 35 | obj.elevation = var_elevation 36 | 37 | epw = EPW(location=obj) 38 | epw.save(self.path, check=False) 39 | 40 | epw2 = EPW() 41 | epw2.read(self.path) 42 | self.assertEqual(epw2.location.city, var_city) 43 | self.assertEqual( 44 | epw2.location.state_province_region, 45 | var_state_province_region) 46 | self.assertEqual(epw2.location.country, var_country) 47 | self.assertEqual(epw2.location.source, var_source) 48 | self.assertEqual(epw2.location.wmo, var_wmo) 49 | self.assertAlmostEqual(epw2.location.latitude, var_latitude) 50 | self.assertAlmostEqual(epw2.location.longitude, var_longitude) 51 | self.assertAlmostEqual(epw2.location.timezone, var_timezone) 52 | self.assertAlmostEqual(epw2.location.elevation, var_elevation) 53 | -------------------------------------------------------------------------------- /tests/test_read.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import EPW 5 | 6 | 7 | class TestReadEPW(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_read_epw(self): 16 | 17 | epw = EPW() 18 | epw.read(r"tests/data/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw") 19 | 20 | epw.save(self.path) 21 | epw2 = EPW() 22 | epw2.read(self.path) 23 | -------------------------------------------------------------------------------- /tests/test_typical_or_extreme_periods.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import unittest 4 | from pyepw.epw import TypicalOrExtremePeriods, TypicalOrExtremePeriod, EPW 5 | 6 | 7 | class TestTypicalOrExtremePeriods(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.fd, self.path = tempfile.mkstemp() 11 | 12 | def tearDown(self): 13 | os.remove(self.path) 14 | 15 | def test_create_typical_or_extreme_periods(self): 16 | 17 | obj = TypicalOrExtremePeriods() 18 | typical_or_extreme_period_obj = TypicalOrExtremePeriod() 19 | var_typical_or_extreme_period_typical_or_extreme_period_name = "typical_or_extreme_period_name" 20 | typical_or_extreme_period_obj.typical_or_extreme_period_name = var_typical_or_extreme_period_typical_or_extreme_period_name 21 | var_typical_or_extreme_period_typical_or_extreme_period_type = "typical_or_extreme_period_type" 22 | typical_or_extreme_period_obj.typical_or_extreme_period_type = var_typical_or_extreme_period_typical_or_extreme_period_type 23 | var_typical_or_extreme_period_period_start_day = "period_start_day" 24 | typical_or_extreme_period_obj.period_start_day = var_typical_or_extreme_period_period_start_day 25 | var_typical_or_extreme_period_period_end_day = "period_end_day" 26 | typical_or_extreme_period_obj.period_end_day = var_typical_or_extreme_period_period_end_day 27 | obj.add_typical_or_extreme_period(typical_or_extreme_period_obj) 28 | 29 | epw = EPW(typical_or_extreme_periods=obj) 30 | epw.save(self.path, check=False) 31 | 32 | epw2 = EPW() 33 | epw2.read(self.path) 34 | self.assertEqual( 35 | epw2.typical_or_extreme_periods.typical_or_extreme_periods[0].typical_or_extreme_period_name, 36 | var_typical_or_extreme_period_typical_or_extreme_period_name) 37 | self.assertEqual( 38 | epw2.typical_or_extreme_periods.typical_or_extreme_periods[0].typical_or_extreme_period_type, 39 | var_typical_or_extreme_period_typical_or_extreme_period_type) 40 | self.assertEqual( 41 | epw2.typical_or_extreme_periods.typical_or_extreme_periods[0].period_start_day, 42 | var_typical_or_extreme_period_period_start_day) 43 | self.assertEqual( 44 | epw2.typical_or_extreme_periods.typical_or_extreme_periods[0].period_end_day, 45 | var_typical_or_extreme_period_period_end_day) 46 | --------------------------------------------------------------------------------