├── .coveragerc ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── README.rst ├── circle.yml ├── data └── swan │ ├── P1.SP1 │ ├── P1.SP2 │ ├── P1.TAB │ ├── P1b.SP1 │ ├── TBL1.tbl │ ├── TBL2.tbl │ ├── TBL2NS.tbl │ ├── a11.hot │ ├── a11.sp2 │ ├── a111uref01.sp2 │ └── test.s2d ├── docs ├── Makefile ├── conf.py ├── index.rst ├── make.bat ├── sourcecode.rst └── whatsnew.rst ├── matplotlibrc ├── notebooks └── oceanwaves.ipynb ├── oceanwaves ├── __init__.py ├── datawell.py ├── oceanwaves.py ├── plot.py ├── spectral.py ├── swan.py ├── table_units.json ├── units.py ├── utils.py └── wavedroid.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── test_datawell.py ├── test_netcdf.py ├── test_oceanwaves.py ├── test_plot.py ├── test_spectral.py ├── test_swan.py ├── test_units.py ├── test_utils.py └── test_wavedroid.py └── upload_to_pypi /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = oceanwaves 4 | 5 | [report] 6 | exclude_lines = 7 | if self.debug: 8 | pragma: no cover 9 | raise NotImplementedError 10 | if __name__ == .__main__.: 11 | ignore_errors = True 12 | omit = 13 | tests/* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | dist 4 | build 5 | cover 6 | _build 7 | _patch 8 | *.nc 9 | *.copy 10 | Untitled*ipynb 11 | .ipynb_checkpoints 12 | .coverage 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "2.7" 5 | # - "3.5" 6 | 7 | install: 8 | - conda update --yes conda 9 | - conda config --add channels soft-matter 10 | - conda create -n testenv --yes pip nose setuptools python=$TRAVIS_PYTHON_VERSION 11 | - source activate testenv 12 | - pip install -r requirements.txt 13 | - pip install -e . 14 | - mkdir -p $HOME/.config/matplotlib 15 | - "echo \"backend : Agg\" >> $HOME/.config/matplotlib/matplotlibrc" 16 | 17 | before_install: 18 | - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-3.5.5-Linux-x86_64.sh -O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-3.5.5-Linux-x86_64.sh -O miniconda.sh; fi 19 | - chmod +x miniconda.sh 20 | - ./miniconda.sh -b -p /home/travis/mc 21 | - export PATH=/home/travis/mc/bin:$PATH 22 | 23 | script: 24 | - nosetests --with-xunit --with-coverage --xunit-file=nosetests.xml --cover-package=oceanwaves --cover-xml --cover-xml-file=coverage.xml --cover-html --cover-html-dir=coverage 25 | 26 | cache: pip 27 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Bas Hoonhout, Deltares 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the license file 2 | include LICENSE.txt 3 | 4 | # Include the data files 5 | recursive-include data * 6 | 7 | # Include the example files 8 | recursive-include notebooks * 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CircleCI](https://circleci.com/gh/openearth/oceanwaves-python.svg?style=svg)](https://circleci.com/gh/openearth/oceanwaves-python) 2 | [![Codecov](https://codecov.io/gh/openearth/oceanwaves-python/branch/master/graph/badge.svg)](https://codecov.io/gh/openearth/oceanwaves-python) 3 | [![ReadTheDocs](http://readthedocs.org/projects/oceanwaves/badge/?version=latest)](http://oceanwaves.readthedocs.io/en/latest/) 4 | 5 | [![PyPI](https://img.shields.io/pypi/v/oceanwaves.svg)](https://pypi.python.org/pypi/oceanwaves) 6 | [![PyPI_versions](https://img.shields.io/pypi/pyversions/oceanwaves.svg)](https://pypi.python.org/pypi/oceanwaves) 7 | [![PyPI_status](https://img.shields.io/pypi/status/oceanwaves.svg)](https://pypi.python.org/pypi/oceanwaves) 8 | [![PyPI_format](https://img.shields.io/pypi/format/oceanwaves.svg)](https://pypi.python.org/pypi/oceanwaves) 9 | 10 | [![License](https://img.shields.io/pypi/l/oceanwaves.svg)](https://pypi.python.org/pypi/oceanwaves) 11 | 12 | # oceanwaves-python 13 | 14 | This toolbox provides a generic data storage object for ocean waves 15 | data (OceanWaves). OceanWaves is built upon the 16 | [xarray.Dataset](http://xarray.pydata.org/en/stable/generated/xarray.Dataset.html) 17 | data storage object, but defines special variables for time, location, 18 | frequency and direction. Many of its functionalities are obtained from 19 | the [pyswan](https://github.com/openearth/pyswan) toolbox, originally 20 | developed by Gerben de Boer, and the 21 | [swantools](https://pypi.python.org/pypi/swantools) toolbox, 22 | originally developed by Caio Eadi Stringari. 23 | 24 | The OceanWaves object supports various standard conversions, like: 25 | * From significant wave height to spectral 26 | * From omnidirectional to directional 27 | * From directional to omnidirectional 28 | * From spectral to significant wave height 29 | * From spectral to spectral wave period 30 | * From spectral to peak wave period 31 | * From directional to peak wave direction 32 | * From degrees to radians 33 | * Automated unit conversion 34 | 35 | The OceanWaves object supports various standard plotting methods, like: 36 | * Polar subplots for directional data on multiple locations/times 37 | * Polar subplots on a map 38 | * Plots supported by 39 | [xarray.Dataset](http://xarray.pydata.org/en/stable/generated/xarray.Dataset.html) 40 | and [Seaborn](http://seaborn.pydata.org) 41 | 42 | The OceanWaves object can be instantiated from: 43 | * Raw data 44 | * SWaN 1D/2D spectral or table files 45 | * An 46 | [xarray.Dataset](http://xarray.pydata.org/en/stable/generated/xarray.Dataset.html) 47 | object 48 | * Another OceanWaves object 49 | 50 | The OceanWaves object can be written to: 51 | * SWaN 1D/2D spectral files 52 | * Output supported by 53 | [xarray.Dataset](http://xarray.pydata.org/en/stable/generated/xarray.Dataset.html) 54 | (e.g. netcdf) 55 | 56 | Usage examples can be found in the IPython notebook 57 | [notebooks/oceanwaves.ipynb](https://github.com/openearth/oceanwaves-python/blob/master/notebooks/oceanwaves.ipynb). 58 | 59 | Source code documentation is hosted at 60 | http://oceanwaves.readthedocs.io/ 61 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | oceanwaves 2 | ========== 3 | 4 | This toolbox provides a generic data storage object for ocean waves 5 | data (OceanWaves). OceanWaves is built upon the `xarray.Dataset 6 | `_ 7 | data storage object, but defines special variables for time, location, 8 | frequency and direction. Many of its functionalities are obtained from 9 | the `pyswan `_ toolbox, 10 | originally developed by Gerben de Boer, and the `swantools 11 | `_ toolbox, originally 12 | developed by Caio Eadi Stringari. 13 | 14 | The OceanWaves object supports various standard conversions, like: 15 | 16 | * From significant wave height to spectral 17 | * From omnidirectional to directional 18 | * From directional to omnidirectional 19 | * From spectral to significant wave height 20 | * From spectral to spectral wave period 21 | * From spectral to peak wave period 22 | * From directional to peak wave direction 23 | * From degrees to radians 24 | * Automated unit conversion 25 | 26 | The OceanWaves object supports various standard plotting methods, like: 27 | 28 | * Polar subplots for directional data on multiple locations/times 29 | * Polar subplots on a map 30 | * Plots supported by `xarray.Dataset 31 | `_ 32 | and `Seaborn `_ 33 | 34 | The OceanWaves object can be instantiated from: 35 | 36 | * Raw data 37 | * SWaN 1D/2D spectral or table files 38 | * An `xarray.Dataset 39 | `_ 40 | object 41 | * Another OceanWaves object 42 | 43 | The OceanWaves object can be written to: 44 | 45 | * SWaN 1D/2D spectral files 46 | * Output supported by `xarray.Dataset 47 | `_ 48 | (e.g. netcdf) 49 | 50 | Usage examples can be found in the IPython notebook 51 | `notebooks/oceanwaves.ipynb 52 | `_. 53 | 54 | Source code documentation is hosted at 55 | `http://oceanwaves.readthedocs.io/ 56 | `_ 57 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | test: 2 | override: 3 | - nosetests --with-coverage 4 | - codecov -------------------------------------------------------------------------------- /data/swan/P1.SP1: -------------------------------------------------------------------------------- 1 | SWAN 1 Swan standard spectral file, version 2 | $ Data produced by SWAN version 40.72ABCDE 3 | $ Project: SWAN ; run number: 4 | LOCATIONS locations in x-y-space 5 | 4 number of locations 6 | 30000.0000 404500.0000 7 | 35800.0000 404500.0000 8 | 38260.0000 403451.0000 9 | 35800.0000 404000.0000 10 | AFREQ absolute frequencies in Hz 11 | 47 number of frequencies 12 | 0.0150 13 | 0.0165 14 | 0.0181 15 | 0.0200 16 | 0.0220 17 | 0.0242 18 | 0.0266 19 | 0.0292 20 | 0.0321 21 | 0.0354 22 | 0.0389 23 | 0.0428 24 | 0.0470 25 | 0.0518 26 | 0.0569 27 | 0.0626 28 | 0.0689 29 | 0.0758 30 | 0.0833 31 | 0.0917 32 | 0.1008 33 | 0.1109 34 | 0.1220 35 | 0.1342 36 | 0.1476 37 | 0.1623 38 | 0.1785 39 | 0.1964 40 | 0.2160 41 | 0.2376 42 | 0.2614 43 | 0.2875 44 | 0.3162 45 | 0.3478 46 | 0.3826 47 | 0.4208 48 | 0.4629 49 | 0.5091 50 | 0.5600 51 | 0.6160 52 | 0.6776 53 | 0.7453 54 | 0.8198 55 | 0.9017 56 | 0.9918 57 | 1.0910 58 | 1.2000 59 | QUANT 60 | 3 number of quantities in table 61 | VaDens variance densities in m2/Hz 62 | m2/Hz unit 63 | -0.9900E+02 exception value 64 | NDIR average nautical direction in degr 65 | degr unit 66 | -0.9990E+03 exception value 67 | DSPRDEGR directional spreading 68 | degr unit 69 | -0.9000E+01 exception value 70 | LOCATION 1 71 | 0.3340E-01 299.0 23.0 72 | 0.5730E-01 299.0 23.0 73 | 0.5730E-01 299.0 23.0 74 | 0.5730E-01 299.0 22.9 75 | 0.5729E-01 298.9 22.9 76 | 0.6444E-01 298.9 22.9 77 | 0.1032E+00 298.9 22.9 78 | 0.1032E+00 298.9 22.9 79 | 0.1032E+00 298.9 22.8 80 | 0.1276E+00 298.9 22.8 81 | 0.1419E+00 298.9 22.7 82 | 0.1419E+00 298.9 22.7 83 | 0.1951E+00 298.9 22.6 84 | 0.1961E+00 298.9 22.5 85 | 0.3653E+00 298.9 22.4 86 | 0.4367E+00 298.9 22.3 87 | 0.7752E+00 298.9 22.1 88 | 0.7406E+00 298.9 21.8 89 | 0.9879E+00 298.8 21.3 90 | 0.1558E+01 298.8 20.9 91 | 0.1428E+01 298.7 20.5 92 | 0.1132E+01 298.7 20.1 93 | 0.1078E+01 298.7 19.7 94 | 0.7824E+00 298.6 19.3 95 | 0.5292E+00 298.6 18.9 96 | 0.4142E+00 298.5 18.6 97 | 0.4575E+00 298.5 18.5 98 | 0.3625E+00 298.5 18.2 99 | 0.3564E+00 298.3 17.0 100 | 0.3547E+00 298.2 16.2 101 | 0.2488E+00 298.1 15.9 102 | 0.1956E+00 298.1 15.7 103 | 0.1445E+00 298.1 15.7 104 | 0.9425E-01 298.3 16.1 105 | 0.6239E-01 298.8 17.4 106 | 0.5312E-01 299.3 18.6 107 | 0.3750E-01 300.2 20.4 108 | 0.1331E-01 304.3 26.9 109 | 0.7829E-02 310.0 32.9 110 | 0.4908E-02 316.0 36.9 111 | 0.2658E-02 322.3 39.6 112 | 0.1151E-02 340.7 42.6 113 | 0.5886E-03 9.3 35.6 114 | 0.3709E-03 26.4 14.1 115 | 0.2505E-03 27.8 14.7 116 | 0.1643E-03 28.0 15.1 117 | 0.1018E-03 27.9 16.3 118 | LOCATION 2 119 | 0.3069E-02 253.2 30.2 120 | 0.5277E-02 253.3 30.2 121 | 0.5292E-02 253.3 30.2 122 | 0.5310E-02 253.4 30.1 123 | 0.5332E-02 253.4 30.1 124 | 0.6028E-02 253.5 30.1 125 | 0.9715E-02 253.7 30.1 126 | 0.9789E-02 253.8 30.1 127 | 0.9887E-02 254.0 30.0 128 | 0.1238E-01 254.2 30.0 129 | 0.1399E-01 254.4 29.9 130 | 0.1416E-01 254.7 29.9 131 | 0.1986E-01 255.1 29.8 132 | 0.2045E-01 255.6 29.7 133 | 0.3930E-01 256.2 29.6 134 | 0.4874E-01 256.9 29.4 135 | 0.9069E-01 257.8 29.2 136 | 0.9132E-01 259.1 29.0 137 | 0.1278E+00 261.2 28.7 138 | 0.2171E+00 263.5 28.2 139 | 0.2215E+00 266.2 27.5 140 | 0.2065E+00 268.7 26.5 141 | 0.2379E+00 271.9 25.1 142 | 0.2163E+00 275.5 23.6 143 | 0.1898E+00 278.8 22.0 144 | 0.1919E+00 281.8 20.9 145 | 0.2691E+00 284.3 20.2 146 | 0.2560E+00 286.9 19.9 147 | 0.2646E+00 291.9 18.6 148 | 0.2596E+00 296.2 17.3 149 | 0.2234E+00 298.1 17.4 150 | 0.1852E+00 299.0 18.5 151 | 0.1282E+00 298.7 20.6 152 | 0.9145E-01 297.6 23.7 153 | 0.7068E-01 296.4 27.4 154 | 0.5392E-01 297.5 30.8 155 | 0.3536E-01 299.6 33.0 156 | 0.2368E-01 299.0 35.4 157 | 0.1728E-01 295.7 37.1 158 | 0.1156E-01 297.9 39.0 159 | 0.7455E-02 298.9 40.7 160 | 0.5171E-02 298.3 41.2 161 | 0.3568E-02 296.3 41.5 162 | 0.2264E-02 297.3 40.8 163 | 0.1452E-02 298.5 41.7 164 | 0.1016E-02 298.4 42.3 165 | 0.7211E-03 297.7 42.0 166 | LOCATION 3 167 | 0.3092E-03 262.6 7.6 168 | 0.5338E-03 262.6 7.6 169 | 0.5379E-03 262.6 7.6 170 | 0.5428E-03 262.7 7.6 171 | 0.5488E-03 262.7 7.6 172 | 0.6256E-03 262.8 7.6 173 | 0.1018E-02 262.8 7.6 174 | 0.1039E-02 262.9 7.6 175 | 0.1064E-02 263.0 7.6 176 | 0.1354E-02 263.1 7.6 177 | 0.1562E-02 263.2 7.6 178 | 0.1629E-02 263.4 7.6 179 | 0.2358E-02 263.6 7.6 180 | 0.2522E-02 263.8 7.5 181 | 0.5071E-02 264.2 7.5 182 | 0.6642E-02 264.5 7.5 183 | 0.1316E-01 265.0 7.4 184 | 0.1414E-01 265.6 7.3 185 | 0.2083E-01 266.4 7.2 186 | 0.3729E-01 267.2 7.1 187 | 0.4047E-01 268.5 7.3 188 | 0.4150E-01 270.2 7.8 189 | 0.5140E-01 272.0 8.7 190 | 0.5047E-01 273.5 10.0 191 | 0.5080E-01 275.2 11.6 192 | 0.6584E-01 277.5 13.0 193 | 0.1270E+00 279.6 13.5 194 | 0.1588E+00 280.9 13.7 195 | 0.1956E+00 282.7 13.7 196 | 0.2075E+00 284.3 13.4 197 | 0.1780E+00 284.9 12.6 198 | 0.1358E+00 284.9 12.5 199 | 0.9302E-01 284.2 13.6 200 | 0.7426E-01 283.0 15.5 201 | 0.5689E-01 282.5 17.2 202 | 0.3869E-01 283.5 19.0 203 | 0.2603E-01 283.8 22.3 204 | 0.1880E-01 283.5 27.2 205 | 0.1550E-01 289.2 32.6 206 | 0.1082E-01 291.6 34.1 207 | 0.6768E-02 291.8 35.8 208 | 0.4791E-02 292.4 38.9 209 | 0.3456E-02 291.8 40.3 210 | 0.2252E-02 295.0 39.8 211 | 0.1464E-02 294.7 41.0 212 | 0.9941E-03 297.8 41.7 213 | 0.7308E-03 296.7 42.4 214 | LOCATION 4 215 | 0.1319E-02 269.1 22.5 216 | 0.2274E-02 269.1 22.5 217 | 0.2287E-02 269.1 22.5 218 | 0.2302E-02 269.1 22.4 219 | 0.2321E-02 269.1 22.4 220 | 0.2637E-02 269.2 22.4 221 | 0.4276E-02 269.2 22.3 222 | 0.4339E-02 269.2 22.2 223 | 0.4420E-02 269.3 22.2 224 | 0.5593E-02 269.3 22.1 225 | 0.6402E-02 269.4 21.9 226 | 0.6593E-02 269.4 21.8 227 | 0.9430E-02 269.5 21.6 228 | 0.9939E-02 269.6 21.4 229 | 0.1967E-01 269.8 21.2 230 | 0.2527E-01 269.9 20.9 231 | 0.4901E-01 270.1 20.5 232 | 0.5162E-01 270.3 20.2 233 | 0.7496E-01 270.7 19.9 234 | 0.1327E+00 271.2 19.7 235 | 0.1420E+00 272.0 19.5 236 | 0.1416E+00 273.1 19.3 237 | 0.1681E+00 274.4 19.4 238 | 0.1490E+00 275.6 19.6 239 | 0.1229E+00 277.0 20.0 240 | 0.1199E+00 279.4 21.0 241 | 0.1804E+00 283.3 21.7 242 | 0.2042E+00 287.7 21.2 243 | 0.2478E+00 292.8 20.1 244 | 0.2706E+00 296.3 19.1 245 | 0.2347E+00 298.2 19.1 246 | 0.1845E+00 299.6 20.1 247 | 0.1264E+00 299.3 21.9 248 | 0.9211E-01 298.7 24.3 249 | 0.7316E-01 297.4 27.2 250 | 0.5435E-01 299.5 30.5 251 | 0.3536E-01 300.5 33.3 252 | 0.2462E-01 298.2 35.8 253 | 0.1699E-01 297.5 37.1 254 | 0.1175E-01 298.2 39.2 255 | 0.7473E-02 298.5 41.2 256 | 0.5141E-02 298.5 42.4 257 | 0.3613E-02 297.3 41.9 258 | 0.2282E-02 298.0 42.1 259 | 0.1467E-02 299.4 43.1 260 | 0.9940E-03 298.5 43.9 261 | 0.7259E-03 298.3 43.7 262 | -------------------------------------------------------------------------------- /data/swan/P1.TAB: -------------------------------------------------------------------------------- 1 | % 2 | % 3 | % Run: Table:P1 SWAN version:41.10.1 4 | % 5 | % Xp Yp Depth Hsig Tm01 Tm_10 Tm02 RTpeak Dir Dspr X-Windv Y-Windv dHs dTm Watlev 6 | % [m] [m] [m] [m] [sec] [sec] [sec] [sec] [degr] [degr] [m/s] [m/s] [m] [sec] [m] 7 | % 8 | 30000. 404500. 5.8231 1.62218 3.8071 6.1085 3.3116 3.1624 315.041 29.3972 13.1064 -9.1772 0.003504 0.000678 0.7600 9 | 35800. 404500. 13.6437 1.77235 4.1469 4.6359 3.9118 5.6008 296.743 24.7867 13.1064 -9.1772 0.001314 0.007865 0.7600 10 | 38260. 403451. 37.6838 1.38329 3.7742 4.2234 3.4634 4.6292 284.179 21.3947 13.1064 -9.1772 0.002379 0.001152 0.7600 11 | 35800. 404000. 37.7079 1.63926 3.9689 4.3337 3.7632 4.6292 300.464 26.1455 13.1064 -9.1772 0.000659 0.002977 0.7600 12 | 36844. 402839. 13.4432 1.49694 3.8932 4.3079 3.6595 4.6292 303.292 23.5556 13.1064 -9.1772 0.002344 0.002983 0.7600 13 | 36848. 402883. 16.8322 1.52344 3.9241 4.3247 3.6914 4.6292 301.910 23.4998 13.1064 -9.1772 0.003016 0.003677 0.7600 14 | 36853. 402928. 20.4044 1.54831 3.9522 4.3390 3.7217 4.6292 300.463 23.8358 13.1064 -9.1772 0.003370 0.004712 0.7600 15 | 36858. 402973. 24.0625 1.57010 3.9710 4.3456 3.7439 4.6292 298.970 24.3552 13.1064 -9.1772 0.003364 0.005629 0.7600 16 | 36863. 403017. 26.8799 1.58243 3.9770 4.3455 3.7526 4.6292 297.742 24.8413 13.1064 -9.1772 0.003117 0.006625 0.7600 17 | 36868. 403062. 28.3898 1.58534 3.9730 4.3412 3.7493 4.6292 296.877 25.1793 13.1064 -9.1772 0.002803 0.007229 0.7600 18 | 36873. 403107. 29.6619 1.58086 3.9622 4.3351 3.7369 4.6292 296.357 25.3571 13.1064 -9.1772 0.002336 0.007481 0.7600 19 | 36878. 403152. 30.4729 1.57217 3.9496 4.3297 3.7211 4.6292 296.044 25.4157 13.1064 -9.1772 0.001637 0.007607 0.7600 20 | 36882. 403196. 31.1257 1.56422 3.9397 4.3264 3.7080 4.6292 295.725 25.3791 13.1064 -9.1772 0.000906 0.007261 0.7600 21 | 36887. 403241. 31.4631 1.55760 3.9329 4.3243 3.6985 4.6292 295.374 25.2740 13.1064 -9.1772 0.000509 0.006577 0.7600 22 | 36892. 403286. 31.6850 1.55102 3.9285 4.3226 3.6918 4.6292 295.053 25.0980 13.1064 -9.1772 0.000680 0.005385 0.7600 23 | 36897. 403330. 31.4897 1.54435 3.9251 4.3208 3.6862 4.6292 294.779 24.8667 13.1064 -9.1772 0.001009 0.003986 0.7600 24 | 36902. 403375. 31.1097 1.53897 3.9219 4.3186 3.6813 4.6292 294.556 24.5981 13.1064 -9.1772 0.000935 0.003083 0.7600 25 | 36907. 403420. 30.6695 1.53554 3.9201 4.3166 3.6789 4.6292 294.356 24.2992 13.1064 -9.1772 0.000371 0.003021 0.7600 26 | 36912. 403465. 30.1632 1.53393 3.9198 4.3150 3.6790 4.6292 294.187 23.9619 13.1064 -9.1772 0.000218 0.002813 0.7600 27 | 36917. 403510. 29.5492 1.53458 3.9213 4.3154 3.6810 4.6292 294.046 23.5829 13.1064 -9.1772 0.000704 0.002573 0.7600 28 | 36922. 403554. 28.5734 1.53638 3.9238 4.3172 3.6838 4.6292 293.899 23.1761 13.1064 -9.1772 0.001190 0.002405 0.7600 29 | 36926. 403599. 27.7016 1.53742 3.9257 4.3177 3.6860 4.6292 293.716 22.7704 13.1064 -9.1772 0.001617 0.002010 0.7600 30 | 36931. 403644. 27.3729 1.53557 3.9258 4.3156 3.6863 4.6292 293.473 22.4023 13.1064 -9.1772 0.001629 0.001118 0.7600 31 | 36936. 403689. 27.1019 1.53094 3.9238 4.3117 3.6838 4.6292 293.085 22.1110 13.1064 -9.1772 0.001528 0.000992 0.7600 32 | 36941. 403733. 26.6698 1.52533 3.9205 4.3079 3.6793 4.6292 292.513 21.8908 13.1064 -9.1772 0.000899 0.000452 0.7600 33 | 36946. 403778. 26.1240 1.51963 3.9172 4.3049 3.6742 4.6292 291.760 21.7065 13.1064 -9.1772 0.000195 0.000263 0.7600 34 | 36951. 403823. 25.3193 1.51383 3.9124 4.3020 3.6667 4.6292 290.899 21.5257 13.1064 -9.1772 0.000192 0.000781 0.7600 35 | 36956. 403867. 24.2400 1.50610 3.9059 4.2984 3.6573 4.6292 290.000 21.3186 13.1064 -9.1772 0.000699 0.001438 0.7600 36 | 36961. 403912. 22.7860 1.49727 3.8993 4.2945 3.6475 4.6292 289.115 21.0744 13.1064 -9.1772 0.001008 0.002344 0.7600 37 | 36966. 403957. 20.6831 1.49042 3.8994 4.2981 3.6448 4.6292 288.221 20.7356 13.1064 -9.1772 0.001116 0.003621 0.7600 38 | 36970. 404002. 18.6084 1.49124 3.9145 4.3209 3.6551 5.6008 287.299 20.2615 13.1064 -9.1772 0.000608 0.003680 0.7600 39 | 36975. 404046. 17.2132 1.50115 3.9451 4.3670 3.6784 5.6008 286.273 19.7357 13.1064 -9.1772 0.000080 0.003809 0.7600 40 | 36980. 404091. 16.0882 1.51156 3.9802 4.4291 3.7027 5.6008 285.049 19.2596 13.1064 -9.1772 0.000060 0.003793 0.7600 41 | 36985. 404136. 15.2382 1.50788 4.0068 4.4934 3.7142 5.6008 283.429 18.8793 13.1064 -9.1772 0.000216 0.002868 0.7600 42 | 36990. 404180. 14.4569 1.48140 4.0151 4.5457 3.7020 5.6008 281.139 18.6219 13.1064 -9.1772 0.000813 0.001940 0.7600 43 | -------------------------------------------------------------------------------- /data/swan/P1b.SP1: -------------------------------------------------------------------------------- 1 | SWAN 1 Swan standard spectral file, version 2 | $ Data produced by SWAN version 40.72ABCDE 3 | $ Project: SWAN ; run number: 4 | LOCATIONS locations in x-y-space 5 | 4 number of locations 6 | 30000.0000 404500.0000 7 | 35800.0000 404500.0000 8 | 38260.0000 403451.0000 9 | 35800.0000 404000.0000 10 | AFREQ absolute frequencies in Hz 11 | 47 number of frequencies 12 | 0.0150 13 | 0.0165 14 | 0.0181 15 | 0.0200 16 | 0.0220 17 | 0.0242 18 | 0.0266 19 | 0.0292 20 | 0.0321 21 | 0.0354 22 | 0.0389 23 | 0.0428 24 | 0.0470 25 | 0.0518 26 | 0.0569 27 | 0.0626 28 | 0.0689 29 | 0.0758 30 | 0.0833 31 | 0.0917 32 | 0.1008 33 | 0.1109 34 | 0.1220 35 | 0.1342 36 | 0.1476 37 | 0.1623 38 | 0.1785 39 | 0.1964 40 | 0.2160 41 | 0.2376 42 | 0.2614 43 | 0.2875 44 | 0.3162 45 | 0.3478 46 | 0.3826 47 | 0.4208 48 | 0.4629 49 | 0.5091 50 | 0.5600 51 | 0.6160 52 | 0.6776 53 | 0.7453 54 | 0.8198 55 | 0.9017 56 | 0.9918 57 | 1.0910 58 | 1.2000 59 | QUANT 60 | 3 number of quantities in table 61 | VaDens variance densities in m2/Hz 62 | m2/Hz unit 63 | -0.9900E+02 exception value 64 | NDIR average nautical direction in degr 65 | degr unit 66 | -0.9990E+03 exception value 67 | DSPRDEGR directional spreading 68 | degr unit 69 | -0.9000E+01 exception value 70 | LOCATION 1 71 | 1.0 300.0 20.0 72 | 1.0 300.0 20.0 73 | 1.0 300.0 20.0 74 | 1.0 300.0 20.0 75 | 1.0 300.0 20.0 76 | 1.0 300.0 20.0 77 | 1.0 300.0 20.0 78 | 1.0 300.0 20.0 79 | 1.0 300.0 20.0 80 | 1.0 300.0 20.0 81 | 1.0 300.0 20.0 82 | 1.0 300.0 20.0 83 | 1.0 300.0 20.0 84 | 1.0 300.0 20.0 85 | 1.0 300.0 20.0 86 | 1.0 300.0 20.0 87 | 1.0 300.0 20.0 88 | 1.0 300.0 20.0 89 | 1.0 300.0 20.0 90 | 1.0 300.0 20.0 91 | 1.0 300.0 20.0 92 | 1.0 300.0 20.0 93 | 1.0 300.0 20.0 94 | 1.0 300.0 20.0 95 | 1.0 300.0 20.0 96 | 1.0 300.0 20.0 97 | 1.0 300.0 20.0 98 | 1.0 300.0 20.0 99 | 1.0 300.0 20.0 100 | 1.0 300.0 20.0 101 | 1.0 300.0 20.0 102 | 1.0 300.0 20.0 103 | 1.0 300.0 20.0 104 | 1.0 300.0 20.0 105 | 1.0 300.0 20.0 106 | 1.0 300.0 20.0 107 | 1.0 300.0 20.0 108 | 1.0 300.0 20.0 109 | 1.0 300.0 20.0 110 | 1.0 300.0 20.0 111 | 1.0 300.0 20.0 112 | 1.0 300.0 20.0 113 | 1.0 300.0 20.0 114 | 1.0 300.0 20.0 115 | 1.0 300.0 20.0 116 | 1.0 300.0 20.0 117 | 1.0 300.0 20.0 118 | LOCATION 2 119 | 1.0 200.0 10.0 120 | 1.0 200.0 10.0 121 | 1.0 200.0 10.0 122 | 1.0 200.0 10.0 123 | 1.0 200.0 10.0 124 | 1.0 200.0 10.0 125 | 1.0 200.0 10.0 126 | 1.0 200.0 10.0 127 | 1.0 200.0 10.0 128 | 1.0 200.0 10.0 129 | 1.0 200.0 10.0 130 | 1.0 200.0 10.0 131 | 1.0 200.0 10.0 132 | 1.0 200.0 10.0 133 | 1.0 200.0 10.0 134 | 1.0 200.0 10.0 135 | 1.0 200.0 10.0 136 | 1.0 200.0 10.0 137 | 1.0 200.0 10.0 138 | 1.0 200.0 10.0 139 | 1.0 200.0 10.0 140 | 1.0 200.0 10.0 141 | 1.0 200.0 10.0 142 | 1.0 200.0 10.0 143 | 1.0 200.0 10.0 144 | 1.0 200.0 10.0 145 | 1.0 200.0 10.0 146 | 1.0 200.0 10.0 147 | 1.0 200.0 10.0 148 | 1.0 200.0 10.0 149 | 1.0 200.0 10.0 150 | 1.0 200.0 10.0 151 | 1.0 200.0 10.0 152 | 1.0 200.0 10.0 153 | 1.0 200.0 10.0 154 | 1.0 200.0 10.0 155 | 1.0 200.0 10.0 156 | 1.0 200.0 10.0 157 | 1.0 200.0 10.0 158 | 1.0 200.0 10.0 159 | 1.0 200.0 10.0 160 | 1.0 200.0 10.0 161 | 1.0 200.0 10.0 162 | 1.0 200.0 10.0 163 | 1.0 200.0 10.0 164 | 1.0 200.0 10.0 165 | 1.0 200.0 10.0 166 | LOCATION 3 167 | 1.0 100.0 5.0 168 | 1.0 100.0 5.0 169 | 1.0 100.0 5.0 170 | 1.0 100.0 5.0 171 | 1.0 100.0 5.0 172 | 1.0 100.0 5.0 173 | 1.0 100.0 5.0 174 | 1.0 100.0 5.0 175 | 1.0 100.0 5.0 176 | 1.0 100.0 5.0 177 | 1.0 100.0 5.0 178 | 1.0 100.0 5.0 179 | 1.0 100.0 5.0 180 | 1.0 100.0 5.0 181 | 1.0 100.0 5.0 182 | 1.0 100.0 5.0 183 | 1.0 100.0 5.0 184 | 1.0 100.0 5.0 185 | 1.0 100.0 5.0 186 | 1.0 100.0 5.0 187 | 1.0 100.0 5.0 188 | 1.0 100.0 5.0 189 | 1.0 100.0 5.0 190 | 1.0 100.0 5.0 191 | 1.0 100.0 5.0 192 | 1.0 100.0 5.0 193 | 1.0 100.0 5.0 194 | 1.0 100.0 5.0 195 | 1.0 100.0 5.0 196 | 1.0 100.0 5.0 197 | 1.0 100.0 5.0 198 | 1.0 100.0 5.0 199 | 1.0 100.0 5.0 200 | 1.0 100.0 5.0 201 | 1.0 100.0 5.0 202 | 1.0 100.0 5.0 203 | 1.0 100.0 5.0 204 | 1.0 100.0 5.0 205 | 1.0 100.0 5.0 206 | 1.0 100.0 5.0 207 | 1.0 100.0 5.0 208 | 1.0 100.0 5.0 209 | 1.0 100.0 5.0 210 | 1.0 100.0 5.0 211 | 1.0 100.0 5.0 212 | 1.0 100.0 5.0 213 | 1.0 100.0 5.0 214 | LOCATION 4 215 | 1.0 50.0 100.0 216 | 1.0 50.0 100.0 217 | 1.0 50.0 100.0 218 | 1.0 50.0 100.0 219 | 1.0 50.0 100.0 220 | 1.0 50.0 100.0 221 | 1.0 50.0 100.0 222 | 1.0 50.0 100.0 223 | 1.0 50.0 100.0 224 | 1.0 50.0 100.0 225 | 1.0 50.0 100.0 226 | 1.0 50.0 100.0 227 | 1.0 50.0 100.0 228 | 1.0 50.0 100.0 229 | 1.0 50.0 100.0 230 | 1.0 50.0 100.0 231 | 1.0 50.0 100.0 232 | 1.0 50.0 100.0 233 | 1.0 50.0 100.0 234 | 1.0 50.0 100.0 235 | 1.0 50.0 100.0 236 | 1.0 50.0 100.0 237 | 1.0 50.0 100.0 238 | 1.0 50.0 100.0 239 | 1.0 50.0 100.0 240 | 1.0 50.0 100.0 241 | 1.0 50.0 100.0 242 | 1.0 50.0 100.0 243 | 1.0 50.0 100.0 244 | 1.0 50.0 100.0 245 | 1.0 50.0 100.0 246 | 1.0 50.0 100.0 247 | 1.0 50.0 100.0 248 | 1.0 50.0 100.0 249 | 1.0 50.0 100.0 250 | 1.0 50.0 100.0 251 | 1.0 50.0 100.0 252 | 1.0 50.0 100.0 253 | 1.0 50.0 100.0 254 | 1.0 50.0 100.0 255 | 1.0 50.0 100.0 256 | 1.0 50.0 100.0 257 | 1.0 50.0 100.0 258 | 1.0 50.0 100.0 259 | 1.0 50.0 100.0 260 | 1.0 50.0 100.0 261 | 1.0 50.0 100.0 -------------------------------------------------------------------------------- /data/swan/TBL1.tbl: -------------------------------------------------------------------------------- 1 | 0.6960E+04 0.1169E+05 0.1508E+02 0.3188E+01 0.8140E+01 0.8147E+01 0.6688E+01 0.6338E+01 2 | 0.9920E+04 0.9965E+04 0.1164E+02 0.2951E+01 0.8140E+01 0.8150E+01 0.6598E+01 0.6219E+01 3 | 0.1139E+05 0.9219E+04 0.5720E+01 0.2474E+01 0.8140E+01 0.8288E+01 0.4956E+01 0.4501E+01 4 | 0.1166E+05 0.1072E+05 0.5649E+01 0.2433E+01 0.8140E+01 0.8298E+01 0.4873E+01 0.4407E+01 5 | 0.1462E+05 0.1022E+05 0.3812E+01 0.5739E+00 0.2853E+01 0.2797E+01 0.2121E+01 0.1814E+01 6 | 0.1410E+05 0.8476E+04 0.4579E+01 0.1236E+01 0.2853E+01 0.2928E+01 0.2784E+01 0.2462E+01 7 | 0.1364E+05 0.7246E+04 0.4144E+01 0.1391E+01 0.3797E+01 0.3843E+01 0.3145E+01 0.2742E+01 8 | 0.2039E+05 0.8179E+04 0.5502E+01 0.7454E+00 0.3797E+01 0.3618E+01 0.2690E+01 0.2351E+01 9 | -------------------------------------------------------------------------------- /data/swan/TBL2.tbl: -------------------------------------------------------------------------------- 1 | % 2 | % 3 | % Run:F31 Table:BUOYS SWAN version:41.10 4 | % 5 | % Xp Yp Botlev Hsig RTpeak TPsmoo Tm01 Tm02 6 | % [m] [m] [m] [m] [sec] [sec] [sec] [sec] 7 | % 8 | 6960. 11691. 15.0763 3.18798 8.1401 8.1469 6.6881 6.3380 9 | 9920. 9965. 11.6419 2.95120 8.1401 8.1505 6.5975 6.2193 10 | 11386. 9219. 5.7203 2.47357 8.1401 8.2885 4.9561 4.5005 11 | 11660. 10719. 5.6488 2.43264 8.1401 8.2981 4.8727 4.4073 12 | 14624. 10218. 3.8120 0.57394 2.8531 2.7975 2.1210 1.8140 13 | 14100. 8476. 4.5793 1.23602 2.8531 2.9280 2.7837 2.4616 14 | 13640. 7246. 4.1435 1.39149 3.7975 3.8434 3.1449 2.7417 15 | 20393. 8179. 5.5023 0.74541 3.7975 3.6178 2.6897 2.3509 16 | -------------------------------------------------------------------------------- /data/swan/TBL2NS.tbl: -------------------------------------------------------------------------------- 1 | % 2 | % 3 | % Run:F31 Table:BUOYS SWAN version:41.10 4 | % 5 | % Time Xp Yp Botlev Hsig RTpeak TPsmoo Tm01 Tm02 6 | % [ ] [m] [m] [m] [m] [sec] [sec] [sec] [sec] 7 | % 8 | 19821014.210000 6960. 11691. 15.0763 3.19125 8.1401 8.1469 6.6925 6.3431 9 | 19821014.210000 9920. 9965. 11.6419 0.14139 1.1000 1.1100 0.8818 0.7653 10 | 19821014.210000 11386. 9219. 5.7203 0.14139 1.1000 1.1100 0.8818 0.7653 11 | 19821014.210000 11660. 10719. 5.6488 0.14139 1.1000 1.1100 0.8818 0.7653 12 | 19821014.210000 14624. 10218. 3.8120 0.14139 1.1000 1.1100 0.8818 0.7653 13 | 19821014.210000 14100. 8476. 4.5793 0.14139 1.1000 1.1100 0.8818 0.7653 14 | 19821014.210000 13640. 7246. 4.1435 0.14139 1.1000 1.1100 0.8818 0.7653 15 | 19821014.210000 20393. 8179. 5.5023 0.14139 1.1000 1.1100 0.8818 0.7653 16 | 19821014.211000 6960. 11691. 15.0763 3.18830 8.1401 8.1468 6.6915 6.3419 17 | 19821014.211000 9920. 9965. 11.6419 1.14593 8.9541 8.8867 6.6028 4.4250 18 | 19821014.211000 11386. 9219. 5.7203 0.82328 8.9541 9.2694 6.0198 3.5168 19 | 19821014.211000 11660. 10719. 5.6488 0.78989 8.9541 9.1988 5.9197 3.4068 20 | 19821014.211000 14624. 10218. 3.8120 0.18907 9.8495 9.5417 1.1545 0.9134 21 | 19821014.211000 14100. 8476. 4.5793 0.36807 9.8495 9.4811 3.1011 1.7330 22 | 19821014.211000 13640. 7246. 4.1435 0.43399 9.8495 9.4779 3.7420 2.0209 23 | 19821014.211000 20393. 8179. 5.5023 0.16688 1.1000 1.1175 0.9488 0.8163 24 | 19821014.212000 6960. 11691. 15.0763 3.18839 8.1401 8.1468 6.6913 6.3417 25 | 19821014.212000 9920. 9965. 11.6419 1.93509 8.9541 8.7226 6.6911 5.3520 26 | 19821014.212000 11386. 9219. 5.7203 1.37649 8.9541 8.8946 6.2872 4.4813 27 | 19821014.212000 11660. 10719. 5.6488 1.33512 8.9541 8.9025 6.2658 4.4122 28 | 19821014.212000 14624. 10218. 3.8120 0.24490 9.8495 9.5854 1.4086 1.0585 29 | 19821014.212000 14100. 8476. 4.5793 0.56753 9.8495 9.5831 3.6036 2.2415 30 | 19821014.212000 13640. 7246. 4.1435 0.66515 9.8495 9.5893 4.1106 2.5680 31 | 19821014.212000 20393. 8179. 5.5023 0.20303 9.8495 9.5549 1.0844 0.8992 32 | 19821014.213000 6960. 11691. 15.0763 3.18851 8.1401 8.1468 6.6912 6.3415 33 | 19821014.213000 9920. 9965. 11.6419 2.45939 8.9541 8.6272 6.5578 5.6134 34 | 19821014.213000 11386. 9219. 5.7203 1.68786 8.9541 8.7859 5.9059 4.5908 35 | 19821014.213000 11660. 10719. 5.6488 1.63430 8.9541 8.7956 5.8601 4.5124 36 | 19821014.213000 14624. 10218. 3.8120 0.30120 9.8495 9.6187 1.5763 1.1713 37 | 19821014.213000 14100. 8476. 4.5793 0.75029 9.8495 9.6109 3.6857 2.5231 38 | 19821014.213000 13640. 7246. 4.1435 0.86540 9.8495 9.6042 4.0744 2.8198 39 | 19821014.213000 20393. 8179. 5.5023 0.25401 9.8495 9.5683 1.2998 1.0221 40 | 19821014.214000 6960. 11691. 15.0763 3.18864 8.1401 8.1468 6.6910 6.3413 41 | 19821014.214000 9920. 9965. 11.6419 2.73581 8.1401 8.3282 6.4898 5.7110 42 | 19821014.214000 11386. 9219. 5.7203 1.90061 8.9541 8.7626 5.6535 4.6335 43 | 19821014.214000 11660. 10719. 5.6488 1.84598 8.9541 8.7726 5.6033 4.5618 44 | 19821014.214000 14624. 10218. 3.8120 0.35414 9.8495 9.5991 1.6655 1.2550 45 | 19821014.214000 14100. 8476. 4.5793 0.88847 9.8495 9.5028 3.6968 2.6736 46 | 19821014.214000 13640. 7246. 4.1435 1.01876 9.8495 9.5108 4.0376 2.9583 47 | 19821014.214000 20393. 8179. 5.5023 0.31506 9.8495 9.6011 1.5318 1.1643 48 | 19821014.215000 6960. 11691. 15.0763 3.18875 8.1401 8.1468 6.6908 6.3411 49 | 19821014.215000 9920. 9965. 11.6419 2.85877 8.1401 8.1865 6.5042 5.8171 50 | 19821014.215000 11386. 9219. 5.7203 2.04417 8.9541 8.7314 5.4770 4.6406 51 | 19821014.215000 11660. 10719. 5.6488 1.99134 8.9541 8.7456 5.4269 4.5771 52 | 19821014.215000 14624. 10218. 3.8120 0.40163 9.8495 9.5019 1.7237 1.3257 53 | 19821014.215000 14100. 8476. 4.5793 0.96888 8.9541 9.1032 3.6658 2.7514 54 | 19821014.215000 13640. 7246. 4.1435 1.11026 8.9541 9.0965 3.9911 3.0422 55 | 19821014.215000 20393. 8179. 5.5023 0.38833 9.8495 9.6189 1.7637 1.3292 56 | 19821014.220000 6960. 11691. 15.0763 3.18884 8.1401 8.1468 6.6906 6.3409 57 | 19821014.220000 9920. 9965. 11.6419 2.91471 8.1401 8.1582 6.5329 5.9130 58 | 19821014.220000 11386. 9219. 5.7203 2.14471 8.9541 8.6789 5.3425 4.6205 59 | 19821014.220000 11660. 10719. 5.6488 2.09335 8.9541 8.7020 5.2936 4.5641 60 | 19821014.220000 14624. 10218. 3.8120 0.44126 8.9541 9.1947 1.7859 1.4048 61 | 19821014.220000 14100. 8476. 4.5793 1.01389 8.9541 8.9569 3.6293 2.7979 62 | 19821014.220000 13640. 7246. 4.1435 1.16094 8.9541 8.9489 3.9468 3.0900 63 | 19821014.220000 20393. 8179. 5.5023 0.46809 9.8495 9.5741 1.9679 1.5025 64 | -------------------------------------------------------------------------------- /data/swan/a11.sp2: -------------------------------------------------------------------------------- 1 | SWAN 1 Swan standard spectral file, version 2 | $ Data produced by SWAN version 40.72ABCDE 3 | $ Project: A11ref01 ; run number: A11 4 | LOCATIONS locations in x-y-space 5 | 1 number of locations 6 | 0.0000 1000.0000 7 | AFREQ absolute frequencies in Hz 8 | 3 number of frequencies 9 | 0.1200 10 | 0.1470 11 | 0.1800 12 | CDIR spectral Cartesian directions in degr 13 | 6 number of directions 14 | 95.0000 15 | 117.0000 16 | 139.0000 17 | 161.0000 18 | 183.0000 19 | 205.0000 20 | QUANT 21 | 1 number of quantities in table 22 | VaDens variance densities in m2/Hz/degr 23 | m2/Hz/degr unit 24 | -0.9900E+02 exception value 25 | FACTOR 26 | 0.83771518E-10 27 | 0 0 0 0 0 0 28 | -518239 -99009904 199975808 874384384 361910368 -6269530 29 | 0 0 0 0 0 0 30 | -------------------------------------------------------------------------------- /data/swan/a111uref01.sp2: -------------------------------------------------------------------------------- 1 | SWAN 1 Swan standard spectral file, version 2 | $ Data produced by SWAN version 40.72ABCDE 3 | $ Project: A11ref01 ; run number: A11 4 | LOCATIONS locations in x-y-space 5 | 13 number of locations 6 | 10000.0000 0.0000 7 | 10000.0000 1001.0000 8 | 10000.0000 2002.0000 9 | 10000.0000 2999.0000 10 | 10000.0000 3203.0000 11 | 10000.0000 3303.0000 12 | 10000.0000 3403.0000 13 | 10000.0000 3499.0000 14 | 10000.0000 3600.0000 15 | 10000.0000 3700.0000 16 | 10000.0000 3800.0000 17 | 10000.0000 3900.0000 18 | 10000.0000 3950.0000 19 | AFREQ absolute frequencies in Hz 20 | 3 number of frequencies 21 | 0.1200 22 | 0.1470 23 | 0.1800 24 | CDIR spectral Cartesian directions in degr 25 | 23 number of directions 26 | 95.0000 27 | 100.0000 28 | 105.0000 29 | 110.0000 30 | 115.0000 31 | 120.0000 32 | 125.0000 33 | 130.0000 34 | 135.0000 35 | 140.0000 36 | 145.0000 37 | 150.0000 38 | 155.0000 39 | 160.0000 40 | 165.0000 41 | 170.0000 42 | 175.0000 43 | 180.0001 44 | 185.0000 45 | 190.0000 46 | 195.0000 47 | 200.0000 48 | 205.0000 49 | QUANT 50 | 1 number of quantities in table 51 | VaDens variance densities in m2/Hz/degr 52 | m2/Hz/degr unit 53 | -0.9900E+02 exception value 54 | NODATA 55 | NODATA 56 | NODATA 57 | NODATA 58 | NODATA 59 | NODATA 60 | NODATA 61 | NODATA 62 | NODATA 63 | NODATA 64 | NODATA 65 | NODATA 66 | NODATA 67 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/oceanwaves.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/oceanwaves.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/oceanwaves" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/oceanwaves" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # oceanwaves documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Dec 7 12:28:55 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import shlex 18 | import sphinx_rtd_theme 19 | import oceanwaves 20 | 21 | # If extensions (or modules to document with autodoc) are in another directory, 22 | # add these directories to sys.path here. If the directory is relative to the 23 | # documentation root, use os.path.abspath to make it absolute, like shown here. 24 | #sys.path.insert(0, os.path.abspath('../oceanwaves')) 25 | 26 | # -- General configuration ------------------------------------------------ 27 | 28 | # If your documentation needs a minimal Sphinx version, state it here. 29 | #needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | 'sphinx.ext.mathjax', 37 | 'sphinx.ext.viewcode', 38 | 'sphinxcontrib.napoleon', 39 | ] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # The suffix(es) of source filenames. 45 | # You can specify multiple suffix as a list of string: 46 | # source_suffix = ['.rst', '.md'] 47 | source_suffix = '.rst' 48 | 49 | # The encoding of source files. 50 | #source_encoding = 'utf-8-sig' 51 | 52 | # The master toctree document. 53 | master_doc = 'index' 54 | 55 | # General information about the project. 56 | project = u'oceanwaves' 57 | copyright = u'2016, Bas Hoonhout' 58 | author = u'Bas Hoonhout' 59 | 60 | # The version info for the project you're documenting, acts as replacement for 61 | # |version| and |release|, also used in various other places throughout the 62 | # built documents. 63 | # 64 | # The short X.Y version. 65 | version = '0.1' 66 | # The full version, including alpha/beta/rc tags. 67 | release = '0.1' 68 | 69 | # The language for content autogenerated by Sphinx. Refer to documentation 70 | # for a list of supported languages. 71 | # 72 | # This is also used if you do content translation via gettext catalogs. 73 | # Usually you set "language" from the command line for these cases. 74 | language = None 75 | 76 | # There are two options for replacing |today|: either, you set today to some 77 | # non-false value, then it is used: 78 | #today = '' 79 | # Else, today_fmt is used as the format for a strftime call. 80 | #today_fmt = '%B %d, %Y' 81 | 82 | # List of patterns, relative to source directory, that match files and 83 | # directories to ignore when looking for source files. 84 | exclude_patterns = ['_build'] 85 | 86 | # The reST default role (used for this markup: `text`) to use for all 87 | # documents. 88 | #default_role = None 89 | 90 | # If true, '()' will be appended to :func: etc. cross-reference text. 91 | #add_function_parentheses = True 92 | 93 | # If true, the current module name will be prepended to all description 94 | # unit titles (such as .. function::). 95 | #add_module_names = True 96 | 97 | # If true, sectionauthor and moduleauthor directives will be shown in the 98 | # output. They are ignored by default. 99 | #show_authors = False 100 | 101 | # The name of the Pygments (syntax highlighting) style to use. 102 | pygments_style = 'sphinx' 103 | 104 | # A list of ignored prefixes for module index sorting. 105 | #modindex_common_prefix = [] 106 | 107 | # If true, keep warnings as "system message" paragraphs in the built documents. 108 | #keep_warnings = False 109 | 110 | # If true, `todo` and `todoList` produce output, else they produce nothing. 111 | todo_include_todos = False 112 | 113 | 114 | # -- Options for HTML output ---------------------------------------------- 115 | 116 | # The theme to use for HTML and HTML Help pages. See the documentation for 117 | # a list of builtin themes. 118 | html_theme = 'sphinx_rtd_theme' 119 | 120 | # Theme options are theme-specific and customize the look and feel of a theme 121 | # further. For a list of options available for each theme, see the 122 | # documentation. 123 | #html_theme_options = {} 124 | 125 | # Add any paths that contain custom themes here, relative to this directory. 126 | # Theme options are theme-specific and customize the look and feel of a theme 127 | # further. For a list of options available for each theme, see the 128 | # documentation. 129 | #html_theme_options = {} 130 | 131 | # Add any paths that contain custom themes here, relative to this directory. 132 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 133 | 134 | # The name for this set of Sphinx documents. If None, it defaults to 135 | # " v documentation". 136 | #html_title = None 137 | 138 | # A shorter title for the navigation bar. Default is the same as html_title. 139 | #html_short_title = None 140 | 141 | # The name of an image file (relative to this directory) to place at the top 142 | # of the sidebar. 143 | #html_logo = None 144 | 145 | # The name of an image file (within the static path) to use as favicon of the 146 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 147 | # pixels large. 148 | #html_favicon = None 149 | 150 | # Add any paths that contain custom static files (such as style sheets) here, 151 | # relative to this directory. They are copied after the builtin static files, 152 | # so a file named "default.css" will overwrite the builtin "default.css". 153 | html_static_path = ['_static'] 154 | 155 | # Add any extra paths that contain custom files (such as robots.txt or 156 | # .htaccess) here, relative to this directory. These files are copied 157 | # directly to the root of the documentation. 158 | #html_extra_path = [] 159 | 160 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 161 | # using the given strftime format. 162 | #html_last_updated_fmt = '%b %d, %Y' 163 | 164 | # If true, SmartyPants will be used to convert quotes and dashes to 165 | # typographically correct entities. 166 | #html_use_smartypants = True 167 | 168 | # Custom sidebar templates, maps document names to template names. 169 | #html_sidebars = {} 170 | 171 | # Additional templates that should be rendered to pages, maps page names to 172 | # template names. 173 | #html_additional_pages = {} 174 | 175 | # If false, no module index is generated. 176 | #html_domain_indices = True 177 | 178 | # If false, no index is generated. 179 | #html_use_index = True 180 | 181 | # If true, the index is split into individual pages for each letter. 182 | #html_split_index = False 183 | 184 | # If true, links to the reST sources are added to the pages. 185 | #html_show_sourcelink = True 186 | 187 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 188 | #html_show_sphinx = True 189 | 190 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 191 | #html_show_copyright = True 192 | 193 | # If true, an OpenSearch description file will be output, and all pages will 194 | # contain a tag referring to it. The value of this option must be the 195 | # base URL from which the finished HTML is served. 196 | #html_use_opensearch = '' 197 | 198 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 199 | #html_file_suffix = None 200 | 201 | # Language to be used for generating the HTML full-text search index. 202 | # Sphinx supports the following languages: 203 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 204 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 205 | #html_search_language = 'en' 206 | 207 | # A dictionary with options for the search language support, empty by default. 208 | # Now only 'ja' uses this config value 209 | #html_search_options = {'type': 'default'} 210 | 211 | # The name of a javascript file (relative to the configuration directory) that 212 | # implements a search results scorer. If empty, the default will be used. 213 | #html_search_scorer = 'scorer.js' 214 | 215 | # Output file base name for HTML help builder. 216 | htmlhelp_basename = 'oceanwavesdoc' 217 | 218 | # -- Options for LaTeX output --------------------------------------------- 219 | 220 | latex_elements = { 221 | # The paper size ('letterpaper' or 'a4paper'). 222 | #'papersize': 'letterpaper', 223 | 224 | # The font size ('10pt', '11pt' or '12pt'). 225 | #'pointsize': '10pt', 226 | 227 | # Additional stuff for the LaTeX preamble. 228 | #'preamble': '', 229 | 230 | # Latex figure (float) alignment 231 | #'figure_align': 'htbp', 232 | } 233 | 234 | # Grouping the document tree into LaTeX files. List of tuples 235 | # (source start file, target name, title, 236 | # author, documentclass [howto, manual, or own class]). 237 | latex_documents = [ 238 | (master_doc, 'oceanwaves.tex', u'oceanwaves Documentation', 239 | u'Bas Hoonhout', 'manual'), 240 | ] 241 | 242 | # The name of an image file (relative to this directory) to place at the top of 243 | # the title page. 244 | #latex_logo = None 245 | 246 | # For "manual" documents, if this is true, then toplevel headings are parts, 247 | # not chapters. 248 | #latex_use_parts = False 249 | 250 | # If true, show page references after internal links. 251 | #latex_show_pagerefs = False 252 | 253 | # If true, show URL addresses after external links. 254 | #latex_show_urls = False 255 | 256 | # Documents to append as an appendix to all manuals. 257 | #latex_appendices = [] 258 | 259 | # If false, no module index is generated. 260 | #latex_domain_indices = True 261 | 262 | 263 | # -- Options for manual page output --------------------------------------- 264 | 265 | # One entry per manual page. List of tuples 266 | # (source start file, name, description, authors, manual section). 267 | man_pages = [ 268 | (master_doc, 'oceanwaves', u'oceanwaves Documentation', 269 | [author], 1) 270 | ] 271 | 272 | # If true, show URL addresses after external links. 273 | #man_show_urls = False 274 | 275 | 276 | # -- Options for Texinfo output ------------------------------------------- 277 | 278 | # Grouping the document tree into Texinfo files. List of tuples 279 | # (source start file, target name, title, author, 280 | # dir menu entry, description, category) 281 | texinfo_documents = [ 282 | (master_doc, 'oceanwaves', u'oceanwaves Documentation', 283 | author, 'oceanwaves', 'One line description of project.', 284 | 'Miscellaneous'), 285 | ] 286 | 287 | # Documents to append as an appendix to all manuals. 288 | #texinfo_appendices = [] 289 | 290 | # If false, no module index is generated. 291 | #texinfo_domain_indices = True 292 | 293 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 294 | #texinfo_show_urls = 'footnote' 295 | 296 | # If true, do not generate a @detailmenu in the "Top" node's menu. 297 | #texinfo_no_detailmenu = False 298 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. oceanwaves documentation master file, created by 2 | sphinx-quickstart on Wed Dec 7 12:28:55 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to oceanwaves's documentation! 7 | ====================================== 8 | 9 | Oceanwaves is a Python package that provides a generic data storage 10 | object for ocean wave data (time series and/or spectral). The package 11 | provides a series of I/O functions to use with various file formats, 12 | like `SWAN `_, `Waverider buoys 13 | `_ and `WaveDroid `_. 14 | 15 | Oceanwaves is based on the `xarray DataSet object 16 | `_, but defines special variables 17 | for time, location, frequency and direction. Many of its 18 | functionalities are obtained from the `pyswan 19 | `_ toolbox, originally developed 20 | by Gerben de Boer, and the `swantools 21 | `_ toolbox, originally 22 | developed by Caio Eadi Stringari. 23 | 24 | The source code of the oceanwaves package can be found at 25 | ``_. 26 | 27 | Usage examples can be found in this notebook 28 | ``_. 29 | 30 | Features 31 | -------- 32 | 33 | The OceanWaves object supports various standard conversions, like: 34 | 35 | * From significant wave height to spectral 36 | * From omnidirectional to directional 37 | * From directional to omnidirectional 38 | * From spectral to significant wave height 39 | * From spectral to spectral wave period 40 | * From spectral to peak wave period 41 | * From directional to peak wave direction 42 | * From degrees to radians 43 | * Automated unit conversion 44 | 45 | The OceanWaves object supports various standard plotting methods, like: 46 | 47 | * Polar subplots for directional data on multiple locations/times 48 | * Polar subplots on a map 49 | * Plots supported by `xarray.Dataset 50 | `_ 51 | and `Seaborn `_ 52 | 53 | The OceanWaves object can be instantiated from: 54 | 55 | * Raw data 56 | * SWaN 1D/2D spectral or table files 57 | * An `xarray.Dataset 58 | `_ 59 | object 60 | * Another OceanWaves object 61 | 62 | The OceanWaves object can be written to: 63 | 64 | * SWaN 1D/2D spectral files 65 | * Output supported by `xarray.Dataset 66 | `_ 67 | (e.g. netcdf) 68 | 69 | Contents 70 | -------- 71 | 72 | .. toctree:: 73 | :maxdepth: 2 74 | 75 | sourcecode 76 | whatsnew 77 | 78 | 79 | Indices and tables 80 | ================== 81 | 82 | * :ref:`genindex` 83 | * :ref:`modindex` 84 | * :ref:`search` 85 | 86 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 2> nul 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\oceanwaves.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\oceanwaves.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /docs/sourcecode.rst: -------------------------------------------------------------------------------- 1 | Source code documentation 2 | ========================= 3 | 4 | OceanWaves 5 | ^^^^^^^^^^ 6 | 7 | .. autoclass:: oceanwaves.OceanWaves 8 | :special-members: 9 | :members: 10 | 11 | Spectral 12 | ^^^^^^^^ 13 | 14 | .. automodule:: oceanwaves.spectral 15 | :members: 16 | 17 | Plotting 18 | ^^^^^^^^ 19 | 20 | .. automodule:: oceanwaves.plot 21 | :members: 22 | 23 | Units 24 | ^^^^^ 25 | 26 | .. automodule:: oceanwaves.units 27 | :members: 28 | -------------------------------------------------------------------------------- /docs/whatsnew.rst: -------------------------------------------------------------------------------- 1 | What's New 2 | ========== 3 | 4 | v1.0.1 (unreleased) 5 | ------------------- 6 | 7 | Breaking changes 8 | ^^^^^^^^^^^^^^^^ 9 | 10 | None. 11 | 12 | Improvements 13 | ^^^^^^^^^^^^ 14 | 15 | * Also read units from quantities other than VarDens (e.g. EnDens and 16 | AcDens) 17 | 18 | * Package `pyproj` is not an optional dependency. Coordinate 19 | conversion is disabled if `pyproj` is not installed. Instead a 20 | warning is given in that situation. 21 | 22 | * Raise NotImplemented exception if SWAN test file with ITER keyword 23 | is read. 24 | 25 | New functions/methods 26 | ^^^^^^^^^^^^^^^^^^^^^ 27 | 28 | * Added `SwanBlockReader` for memory efficient reading of large 29 | files. The `SwanSpcReader` now uses this class to read spectrum 30 | files with a minimum required number of lines in memory at each 31 | point in the reading procedure. 32 | 33 | * Added support for the `ZERO` keyword in SWAN spectrum files. `ZERO` 34 | now results in zeros, while `NODATA` results in NaN values. 35 | 36 | Bug fixes 37 | ^^^^^^^^^ 38 | 39 | * Store comments from SWAN spectrum files as single string instead of a 40 | list of strings as the scipy netcdf I/O cannot cope with lists of 41 | strings. 42 | 43 | * Do not set units attribute on time coordinate if not given, as the 44 | time is likely to be given by a list of datetime objects that are 45 | automatically encoded by xarray. Setting the units attribute 46 | manually would raise an exception if the dataset is written to 47 | netCDF. 48 | 49 | * Do not squeeze energy matrix as that may cause conflicts with 50 | dimensions of length unity. Instead, reshape the energy matrix to 51 | the appropriate size. 52 | 53 | * Fix a datatype bug in spectral.jonswap() that raised `ValueError: 54 | Integers to negative integer powers are not allowed` in the case Hm0 or Tp 55 | were integers. Also modify spectral.jonswap() to accept arrays as inputs 56 | for Hm0 and Tp. Only one-dimensional arrays for now. - Caio Stringari 57 | 58 | Tests 59 | ^^^^^ 60 | 61 | * Added tests for converting SWAN files to netCDF. 62 | 63 | * Added to extra example input files: a *.hot hotstart file, a *.sp2 64 | file without data and a *.sp2 file with a single location. 65 | 66 | * Test SWAN I/O not only for reading the proper shapes, but also the 67 | proper values. 68 | 69 | v1.0.0 (15 November 2017) 70 | ------------------------- 71 | 72 | Initial release 73 | -------------------------------------------------------------------------------- /matplotlibrc: -------------------------------------------------------------------------------- 1 | ### MATPLOTLIBRC FORMAT 2 | 3 | # This is a sample matplotlib configuration file - you can find a copy 4 | # of it on your system in 5 | # site-packages/matplotlib/mpl-data/matplotlibrc. If you edit it 6 | # there, please note that it will be overwritten in your next install. 7 | # If you want to keep a permanent local copy that will not be 8 | # overwritten, place it in the following location: 9 | # unix/linux: 10 | # $HOME/.config/matplotlib/matplotlibrc or 11 | # $XDG_CONFIG_HOME/matplotlib/matplotlibrc (if $XDG_CONFIG_HOME is set) 12 | # other platforms: 13 | # $HOME/.matplotlib/matplotlibrc 14 | # 15 | # See http://matplotlib.org/users/customizing.html#the-matplotlibrc-file for 16 | # more details on the paths which are checked for the configuration file. 17 | # 18 | # This file is best viewed in a editor which supports python mode 19 | # syntax highlighting. Blank lines, or lines starting with a comment 20 | # symbol, are ignored, as are trailing comments. Other lines must 21 | # have the format 22 | # key : val # optional comment 23 | # 24 | # Colors: for the color values below, you can either use - a 25 | # matplotlib color string, such as r, k, or b - an rgb tuple, such as 26 | # (1.0, 0.5, 0.0) - a hex string, such as ff00ff - a scalar 27 | # grayscale intensity such as 0.75 - a legal html color name, e.g., red, 28 | # blue, darkslategray 29 | 30 | #### CONFIGURATION BEGINS HERE 31 | 32 | # The default backend; one of GTK GTKAgg GTKCairo GTK3Agg GTK3Cairo 33 | # MacOSX Qt4Agg Qt5Agg TkAgg WX WXAgg Agg Cairo GDK PS PDF SVG 34 | # Template. 35 | # You can also deploy your own backend outside of matplotlib by 36 | # referring to the module name (which must be in the PYTHONPATH) as 37 | # 'module://my_backend'. 38 | backend : Agg 39 | 40 | # If you are using the Qt4Agg backend, you can choose here 41 | # to use the PyQt4 bindings or the newer PySide bindings to 42 | # the underlying Qt4 toolkit. 43 | #backend.qt4 : PyQt4 # PyQt4 | PySide 44 | 45 | # Note that this can be overridden by the environment variable 46 | # QT_API used by Enthought Tool Suite (ETS); valid values are 47 | # "pyqt" and "pyside". The "pyqt" setting has the side effect of 48 | # forcing the use of Version 2 API for QString and QVariant. 49 | 50 | # The port to use for the web server in the WebAgg backend. 51 | # webagg.port : 8888 52 | 53 | # If webagg.port is unavailable, a number of other random ports will 54 | # be tried until one that is available is found. 55 | # webagg.port_retries : 50 56 | 57 | # When True, open the webbrowser to the plot that is shown 58 | # webagg.open_in_browser : True 59 | 60 | # When True, the figures rendered in the nbagg backend are created with 61 | # a transparent background. 62 | # nbagg.transparent : False 63 | 64 | # if you are running pyplot inside a GUI and your backend choice 65 | # conflicts, we will automatically try to find a compatible one for 66 | # you if backend_fallback is True 67 | #backend_fallback: True 68 | 69 | #interactive : False 70 | #toolbar : toolbar2 # None | toolbar2 ("classic" is deprecated) 71 | #timezone : UTC # a pytz timezone string, e.g., US/Central or Europe/Paris 72 | 73 | # Where your matplotlib data lives if you installed to a non-default 74 | # location. This is where the matplotlib fonts, bitmaps, etc reside 75 | #datapath : /home/jdhunter/mpldata 76 | 77 | 78 | ### LINES 79 | # See http://matplotlib.org/api/artist_api.html#module-matplotlib.lines for more 80 | # information on line properties. 81 | #lines.linewidth : 1.5 # line width in points 82 | #lines.linestyle : - # solid line 83 | #lines.color : C0 # has no affect on plot(); see axes.prop_cycle 84 | #lines.marker : None # the default marker 85 | #lines.markeredgewidth : 1.0 # the line width around the marker symbol 86 | #lines.markersize : 6 # markersize, in points 87 | #lines.dash_joinstyle : miter # miter|round|bevel 88 | #lines.dash_capstyle : butt # butt|round|projecting 89 | #lines.solid_joinstyle : miter # miter|round|bevel 90 | #lines.solid_capstyle : projecting # butt|round|projecting 91 | #lines.antialiased : True # render lines in antialiased (no jaggies) 92 | 93 | # The three standard dash patterns. These are scaled by the linewidth. 94 | #lines.dashed_pattern : 2.8, 1.2 95 | #lines.dashdot_pattern : 4.8, 1.2, 0.8, 1.2 96 | #lines.dotted_pattern : 1.1, 1.1 97 | #lines.scale_dashes : True 98 | 99 | #markers.fillstyle: full # full|left|right|bottom|top|none 100 | 101 | ### PATCHES 102 | # Patches are graphical objects that fill 2D space, like polygons or 103 | # circles. See 104 | # http://matplotlib.org/api/artist_api.html#module-matplotlib.patches 105 | # information on patch properties 106 | #patch.linewidth : 1 # edge width in points. 107 | #patch.facecolor : C0 108 | #patch.edgecolor : black # if forced, or patch is not filled 109 | #patch.force_edgecolor : False # True to always use edgecolor 110 | #patch.antialiased : True # render patches in antialiased (no jaggies) 111 | 112 | ### HATCHES 113 | #hatch.color : k 114 | #hatch.linewidth : 1.0 115 | 116 | ### Boxplot 117 | #boxplot.notch : False 118 | #boxplot.vertical : True 119 | #boxplot.whiskers : 1.5 120 | #boxplot.bootstrap : None 121 | #boxplot.patchartist : False 122 | #boxplot.showmeans : False 123 | #boxplot.showcaps : True 124 | #boxplot.showbox : True 125 | #boxplot.showfliers : True 126 | #boxplot.meanline : False 127 | 128 | #boxplot.flierprops.color : 'k' 129 | #boxplot.flierprops.marker : 'o' 130 | #boxplot.flierprops.markerfacecolor : 'none' 131 | #boxplot.flierprops.markeredgecolor : 'k' 132 | #boxplot.flierprops.markersize : 6 133 | #boxplot.flierprops.linestyle : 'none' 134 | #boxplot.flierprops.linewidth : 1.0 135 | 136 | #boxplot.boxprops.color : 'k' 137 | #boxplot.boxprops.linewidth : 1.0 138 | #boxplot.boxprops.linestyle : '-' 139 | 140 | #boxplot.whiskerprops.color : 'k' 141 | #boxplot.whiskerprops.linewidth : 1.0 142 | #boxplot.whiskerprops.linestyle : '-' 143 | 144 | #boxplot.capprops.color : 'k' 145 | #boxplot.capprops.linewidth : 1.0 146 | #boxplot.capprops.linestyle : '-' 147 | 148 | #boxplot.medianprops.color : 'C1' 149 | #boxplot.medianprops.linewidth : 1.0 150 | #boxplot.medianprops.linestyle : '-' 151 | 152 | #boxplot.meanprops.color : 'C2' 153 | #boxplot.meanprops.marker : '^' 154 | #boxplot.meanprops.markerfacecolor : 'C2' 155 | #boxplot.meanprops.markeredgecolor : 'C2' 156 | #boxplot.meanprops.markersize : 6 157 | #boxplot.meanprops.linestyle : 'none' 158 | #boxplot.meanprops.linewidth : 1.0 159 | 160 | ### FONT 161 | # 162 | # font properties used by text.Text. See 163 | # http://matplotlib.org/api/font_manager_api.html for more 164 | # information on font properties. The 6 font properties used for font 165 | # matching are given below with their default values. 166 | # 167 | # The font.family property has five values: 'serif' (e.g., Times), 168 | # 'sans-serif' (e.g., Helvetica), 'cursive' (e.g., Zapf-Chancery), 169 | # 'fantasy' (e.g., Western), and 'monospace' (e.g., Courier). Each of 170 | # these font families has a default list of font names in decreasing 171 | # order of priority associated with them. When text.usetex is False, 172 | # font.family may also be one or more concrete font names. 173 | # 174 | # The font.style property has three values: normal (or roman), italic 175 | # or oblique. The oblique style will be used for italic, if it is not 176 | # present. 177 | # 178 | # The font.variant property has two values: normal or small-caps. For 179 | # TrueType fonts, which are scalable fonts, small-caps is equivalent 180 | # to using a font size of 'smaller', or about 83%% of the current font 181 | # size. 182 | # 183 | # The font.weight property has effectively 13 values: normal, bold, 184 | # bolder, lighter, 100, 200, 300, ..., 900. Normal is the same as 185 | # 400, and bold is 700. bolder and lighter are relative values with 186 | # respect to the current weight. 187 | # 188 | # The font.stretch property has 11 values: ultra-condensed, 189 | # extra-condensed, condensed, semi-condensed, normal, semi-expanded, 190 | # expanded, extra-expanded, ultra-expanded, wider, and narrower. This 191 | # property is not currently implemented. 192 | # 193 | # The font.size property is the default font size for text, given in pts. 194 | # 10 pt is the standard value. 195 | # 196 | #font.family : sans-serif 197 | #font.style : normal 198 | #font.variant : normal 199 | #font.weight : medium 200 | #font.stretch : normal 201 | # note that font.size controls default text sizes. To configure 202 | # special text sizes tick labels, axes, labels, title, etc, see the rc 203 | # settings for axes and ticks. Special text sizes can be defined 204 | # relative to font.size, using the following values: xx-small, x-small, 205 | # small, medium, large, x-large, xx-large, larger, or smaller 206 | #font.size : 10.0 207 | #font.serif : DejaVu Serif, Bitstream Vera Serif, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif 208 | #font.sans-serif : DejaVu Sans, Bitstream Vera Sans, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif 209 | #font.cursive : Apple Chancery, Textile, Zapf Chancery, Sand, Script MT, Felipa, cursive 210 | #font.fantasy : Comic Sans MS, Chicago, Charcoal, Impact, Western, Humor Sans, xkcd, fantasy 211 | #font.monospace : DejaVu Sans Mono, Bitstream Vera Sans Mono, Andale Mono, Nimbus Mono L, Courier New, Courier, Fixed, Terminal, monospace 212 | 213 | ### TEXT 214 | # text properties used by text.Text. See 215 | # http://matplotlib.org/api/artist_api.html#module-matplotlib.text for more 216 | # information on text properties 217 | 218 | #text.color : black 219 | 220 | ### LaTeX customizations. See http://wiki.scipy.org/Cookbook/Matplotlib/UsingTex 221 | #text.usetex : False # use latex for all text handling. The following fonts 222 | # are supported through the usual rc parameter settings: 223 | # new century schoolbook, bookman, times, palatino, 224 | # zapf chancery, charter, serif, sans-serif, helvetica, 225 | # avant garde, courier, monospace, computer modern roman, 226 | # computer modern sans serif, computer modern typewriter 227 | # If another font is desired which can loaded using the 228 | # LaTeX \usepackage command, please inquire at the 229 | # matplotlib mailing list 230 | #text.latex.unicode : False # use "ucs" and "inputenc" LaTeX packages for handling 231 | # unicode strings. 232 | #text.latex.preamble : # IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURES 233 | # AND IS THEREFORE UNSUPPORTED. PLEASE DO NOT ASK FOR HELP 234 | # IF THIS FEATURE DOES NOT DO WHAT YOU EXPECT IT TO. 235 | # preamble is a comma separated list of LaTeX statements 236 | # that are included in the LaTeX document preamble. 237 | # An example: 238 | # text.latex.preamble : \usepackage{bm},\usepackage{euler} 239 | # The following packages are always loaded with usetex, so 240 | # beware of package collisions: color, geometry, graphicx, 241 | # type1cm, textcomp. Adobe Postscript (PSSNFS) font packages 242 | # may also be loaded, depending on your font settings 243 | 244 | #text.dvipnghack : None # some versions of dvipng don't handle alpha 245 | # channel properly. Use True to correct 246 | # and flush ~/.matplotlib/tex.cache 247 | # before testing and False to force 248 | # correction off. None will try and 249 | # guess based on your dvipng version 250 | 251 | #text.hinting : auto # May be one of the following: 252 | # 'none': Perform no hinting 253 | # 'auto': Use FreeType's autohinter 254 | # 'native': Use the hinting information in the 255 | # font file, if available, and if your 256 | # FreeType library supports it 257 | # 'either': Use the native hinting information, 258 | # or the autohinter if none is available. 259 | # For backward compatibility, this value may also be 260 | # True === 'auto' or False === 'none'. 261 | #text.hinting_factor : 8 # Specifies the amount of softness for hinting in the 262 | # horizontal direction. A value of 1 will hint to full 263 | # pixels. A value of 2 will hint to half pixels etc. 264 | 265 | #text.antialiased : True # If True (default), the text will be antialiased. 266 | # This only affects the Agg backend. 267 | 268 | # The following settings allow you to select the fonts in math mode. 269 | # They map from a TeX font name to a fontconfig font pattern. 270 | # These settings are only used if mathtext.fontset is 'custom'. 271 | # Note that this "custom" mode is unsupported and may go away in the 272 | # future. 273 | #mathtext.cal : cursive 274 | #mathtext.rm : serif 275 | #mathtext.tt : monospace 276 | #mathtext.it : serif:italic 277 | #mathtext.bf : serif:bold 278 | #mathtext.sf : sans 279 | #mathtext.fontset : dejavusans # Should be 'dejavusans' (default), 280 | # 'dejavuserif', 'cm' (Computer Modern), 'stix', 281 | # 'stixsans' or 'custom' 282 | #mathtext.fallback_to_cm : True # When True, use symbols from the Computer Modern 283 | # fonts when a symbol can not be found in one of 284 | # the custom math fonts. 285 | 286 | #mathtext.default : it # The default font to use for math. 287 | # Can be any of the LaTeX font names, including 288 | # the special name "regular" for the same font 289 | # used in regular text. 290 | 291 | ### AXES 292 | # default face and edge color, default tick sizes, 293 | # default fontsizes for ticklabels, and so on. See 294 | # http://matplotlib.org/api/axes_api.html#module-matplotlib.axes 295 | #axes.facecolor : white # axes background color 296 | #axes.edgecolor : black # axes edge color 297 | #axes.linewidth : 0.8 # edge linewidth 298 | #axes.grid : False # display grid or not 299 | #axes.titlesize : large # fontsize of the axes title 300 | #axes.titlepad : 6.0 # pad between axes and title in points 301 | #axes.labelsize : medium # fontsize of the x any y labels 302 | #axes.labelpad : 4.0 # space between label and axis 303 | #axes.labelweight : normal # weight of the x and y labels 304 | #axes.labelcolor : black 305 | #axes.axisbelow : 'line' # draw axis gridlines and ticks below 306 | # patches (True); above patches but below 307 | # lines ('line'); or above all (False) 308 | 309 | #axes.formatter.limits : -7, 7 # use scientific notation if log10 310 | # of the axis range is smaller than the 311 | # first or larger than the second 312 | #axes.formatter.use_locale : False # When True, format tick labels 313 | # according to the user's locale. 314 | # For example, use ',' as a decimal 315 | # separator in the fr_FR locale. 316 | #axes.formatter.use_mathtext : False # When True, use mathtext for scientific 317 | # notation. 318 | #axes.formatter.useoffset : True # If True, the tick label formatter 319 | # will default to labeling ticks relative 320 | # to an offset when the data range is 321 | # small compared to the minimum absolute 322 | # value of the data. 323 | #axes.formatter.offset_threshold : 4 # When useoffset is True, the offset 324 | # will be used when it can remove 325 | # at least this number of significant 326 | # digits from tick labels. 327 | 328 | # axes.spines.left : True # display axis spines 329 | # axes.spines.bottom : True 330 | # axes.spines.top : True 331 | # axes.spines.right : True 332 | 333 | 334 | #axes.unicode_minus : True # use unicode for the minus symbol 335 | # rather than hyphen. See 336 | # http://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes 337 | #axes.prop_cycle : cycler('color', 338 | # ['1f77b4', 'ff7f0e', '2ca02c', 'd62728', 339 | # '9467bd', '8c564b', 'e377c2', '7f7f7f', 340 | # 'bcbd22', '17becf']) 341 | # color cycle for plot lines 342 | # as list of string colorspecs: 343 | # single letter, long name, or 344 | # web-style hex 345 | #axes.autolimit_mode : data # How to scale axes limits to the data. 346 | # Use "data" to use data limits, plus some margin 347 | # Use "round_number" move to the nearest "round" number 348 | #axes.xmargin : .05 # x margin. See `axes.Axes.margins` 349 | #axes.ymargin : .05 # y margin See `axes.Axes.margins` 350 | 351 | #polaraxes.grid : True # display grid on polar axes 352 | #axes3d.grid : True # display grid on 3d axes 353 | 354 | ### DATES 355 | # These control the default format strings used in AutoDateFormatter. 356 | # Any valid format datetime format string can be used (see the python 357 | # `datetime` for details). For example using '%%x' will use the locale date representation 358 | # '%%X' will use the locale time representation and '%%c' will use the full locale datetime 359 | # representation. 360 | # These values map to the scales: 361 | # {'year': 365, 'month': 30, 'day': 1, 'hour': 1/24, 'minute': 1 / (24 * 60)} 362 | 363 | # date.autoformatter.year : %Y 364 | # date.autoformatter.month : %Y-%m 365 | # date.autoformatter.day : %Y-%m-%d 366 | # date.autoformatter.hour : %m-%d %H 367 | # date.autoformatter.minute : %d %H:%M 368 | # date.autoformatter.second : %H:%M:%S 369 | # date.autoformatter.microsecond : %M:%S.%f 370 | 371 | ### TICKS 372 | # see http://matplotlib.org/api/axis_api.html#matplotlib.axis.Tick 373 | #xtick.top : False # draw ticks on the top side 374 | #xtick.bottom : True # draw ticks on the bottom side 375 | #xtick.major.size : 3.5 # major tick size in points 376 | #xtick.minor.size : 2 # minor tick size in points 377 | #xtick.major.width : 0.8 # major tick width in points 378 | #xtick.minor.width : 0.6 # minor tick width in points 379 | #xtick.major.pad : 3.5 # distance to major tick label in points 380 | #xtick.minor.pad : 3.4 # distance to the minor tick label in points 381 | #xtick.color : k # color of the tick labels 382 | #xtick.labelsize : medium # fontsize of the tick labels 383 | #xtick.direction : out # direction: in, out, or inout 384 | #xtick.minor.visible : False # visibility of minor ticks on x-axis 385 | #xtick.major.top : True # draw x axis top major ticks 386 | #xtick.major.bottom : True # draw x axis bottom major ticks 387 | #xtick.minor.top : True # draw x axis top minor ticks 388 | #xtick.minor.bottom : True # draw x axis bottom minor ticks 389 | 390 | #ytick.left : True # draw ticks on the left side 391 | #ytick.right : False # draw ticks on the right side 392 | #ytick.major.size : 3.5 # major tick size in points 393 | #ytick.minor.size : 2 # minor tick size in points 394 | #ytick.major.width : 0.8 # major tick width in points 395 | #ytick.minor.width : 0.6 # minor tick width in points 396 | #ytick.major.pad : 3.5 # distance to major tick label in points 397 | #ytick.minor.pad : 3.4 # distance to the minor tick label in points 398 | #ytick.color : k # color of the tick labels 399 | #ytick.labelsize : medium # fontsize of the tick labels 400 | #ytick.direction : out # direction: in, out, or inout 401 | #ytick.minor.visible : False # visibility of minor ticks on y-axis 402 | #ytick.major.left : True # draw y axis left major ticks 403 | #ytick.major.right : True # draw y axis right major ticks 404 | #ytick.minor.left : True # draw y axis left minor ticks 405 | #ytick.minor.right : True # draw y axis right minor ticks 406 | 407 | 408 | ### GRIDS 409 | #grid.color : b0b0b0 # grid color 410 | #grid.linestyle : - # solid 411 | #grid.linewidth : 0.8 # in points 412 | #grid.alpha : 1.0 # transparency, between 0.0 and 1.0 413 | 414 | ### Legend 415 | #legend.loc : best 416 | #legend.frameon : True # if True, draw the legend on a background patch 417 | #legend.framealpha : 0.8 # legend patch transparency 418 | #legend.facecolor : inherit # inherit from axes.facecolor; or color spec 419 | #legend.edgecolor : 0.8 # background patch boundary color 420 | #legend.fancybox : True # if True, use a rounded box for the 421 | # legend background, else a rectangle 422 | #legend.shadow : False # if True, give background a shadow effect 423 | #legend.numpoints : 1 # the number of marker points in the legend line 424 | #legend.scatterpoints : 1 # number of scatter points 425 | #legend.markerscale : 1.0 # the relative size of legend markers vs. original 426 | #legend.fontsize : medium 427 | # Dimensions as fraction of fontsize: 428 | #legend.borderpad : 0.4 # border whitespace 429 | #legend.labelspacing : 0.5 # the vertical space between the legend entries 430 | #legend.handlelength : 2.0 # the length of the legend lines 431 | #legend.handleheight : 0.7 # the height of the legend handle 432 | #legend.handletextpad : 0.8 # the space between the legend line and legend text 433 | #legend.borderaxespad : 0.5 # the border between the axes and legend edge 434 | #legend.columnspacing : 2.0 # column separation 435 | 436 | ### FIGURE 437 | # See http://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure 438 | #figure.titlesize : large # size of the figure title (Figure.suptitle()) 439 | #figure.titleweight : normal # weight of the figure title 440 | #figure.figsize : 6.4, 4.8 # figure size in inches 441 | #figure.dpi : 100 # figure dots per inch 442 | #figure.facecolor : white # figure facecolor; 0.75 is scalar gray 443 | #figure.edgecolor : white # figure edgecolor 444 | #figure.autolayout : False # When True, automatically adjust subplot 445 | # parameters to make the plot fit the figure 446 | #figure.max_open_warning : 20 # The maximum number of figures to open through 447 | # the pyplot interface before emitting a warning. 448 | # If less than one this feature is disabled. 449 | 450 | # The figure subplot parameters. All dimensions are a fraction of the 451 | #figure.subplot.left : 0.125 # the left side of the subplots of the figure 452 | #figure.subplot.right : 0.9 # the right side of the subplots of the figure 453 | #figure.subplot.bottom : 0.11 # the bottom of the subplots of the figure 454 | #figure.subplot.top : 0.88 # the top of the subplots of the figure 455 | #figure.subplot.wspace : 0.2 # the amount of width reserved for blank space between subplots, 456 | # expressed as a fraction of the average axis width 457 | #figure.subplot.hspace : 0.2 # the amount of height reserved for white space between subplots, 458 | # expressed as a fraction of the average axis height 459 | 460 | 461 | ### IMAGES 462 | #image.aspect : equal # equal | auto | a number 463 | #image.interpolation : nearest # see help(imshow) for options 464 | #image.cmap : viridis # A colormap name, gray etc... 465 | #image.lut : 256 # the size of the colormap lookup table 466 | #image.origin : upper # lower | upper 467 | #image.resample : True 468 | #image.composite_image : True # When True, all the images on a set of axes are 469 | # combined into a single composite image before 470 | # saving a figure as a vector graphics file, 471 | # such as a PDF. 472 | 473 | ### CONTOUR PLOTS 474 | #contour.negative_linestyle : dashed # dashed | solid 475 | #contour.corner_mask : True # True | False | legacy 476 | 477 | ### ERRORBAR PLOTS 478 | #errorbar.capsize : 0 # length of end cap on error bars in pixels 479 | 480 | ### HISTOGRAM PLOTS 481 | #hist.bins : 10 # The default number of histogram bins. 482 | # If Numpy 1.11 or later is 483 | # installed, may also be `auto` 484 | 485 | ### SCATTER PLOTS 486 | #scatter.marker : o # The default marker type for scatter plots. 487 | 488 | ### Agg rendering 489 | ### Warning: experimental, 2008/10/10 490 | #agg.path.chunksize : 0 # 0 to disable; values in the range 491 | # 10000 to 100000 can improve speed slightly 492 | # and prevent an Agg rendering failure 493 | # when plotting very large data sets, 494 | # especially if they are very gappy. 495 | # It may cause minor artifacts, though. 496 | # A value of 20000 is probably a good 497 | # starting point. 498 | ### SAVING FIGURES 499 | #path.simplify : True # When True, simplify paths by removing "invisible" 500 | # points to reduce file size and increase rendering 501 | # speed 502 | #path.simplify_threshold : 0.1 # The threshold of similarity below which 503 | # vertices will be removed in the simplification 504 | # process 505 | #path.snap : True # When True, rectilinear axis-aligned paths will be snapped to 506 | # the nearest pixel when certain criteria are met. When False, 507 | # paths will never be snapped. 508 | #path.sketch : None # May be none, or a 3-tuple of the form (scale, length, 509 | # randomness). 510 | # *scale* is the amplitude of the wiggle 511 | # perpendicular to the line (in pixels). *length* 512 | # is the length of the wiggle along the line (in 513 | # pixels). *randomness* is the factor by which 514 | # the length is randomly scaled. 515 | 516 | # the default savefig params can be different from the display params 517 | # e.g., you may want a higher resolution, or to make the figure 518 | # background white 519 | #savefig.dpi : figure # figure dots per inch or 'figure' 520 | #savefig.facecolor : white # figure facecolor when saving 521 | #savefig.edgecolor : white # figure edgecolor when saving 522 | #savefig.format : png # png, ps, pdf, svg 523 | #savefig.bbox : standard # 'tight' or 'standard'. 524 | # 'tight' is incompatible with pipe-based animation 525 | # backends but will workd with temporary file based ones: 526 | # e.g. setting animation.writer to ffmpeg will not work, 527 | # use ffmpeg_file instead 528 | #savefig.pad_inches : 0.1 # Padding to be used when bbox is set to 'tight' 529 | #savefig.jpeg_quality: 95 # when a jpeg is saved, the default quality parameter. 530 | #savefig.directory : ~ # default directory in savefig dialog box, 531 | # leave empty to always use current working directory 532 | #savefig.transparent : False # setting that controls whether figures are saved with a 533 | # transparent background by default 534 | 535 | # tk backend params 536 | #tk.window_focus : False # Maintain shell focus for TkAgg 537 | 538 | # ps backend params 539 | #ps.papersize : letter # auto, letter, legal, ledger, A0-A10, B0-B10 540 | #ps.useafm : False # use of afm fonts, results in small files 541 | #ps.usedistiller : False # can be: None, ghostscript or xpdf 542 | # Experimental: may produce smaller files. 543 | # xpdf intended for production of publication quality files, 544 | # but requires ghostscript, xpdf and ps2eps 545 | #ps.distiller.res : 6000 # dpi 546 | #ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType) 547 | 548 | # pdf backend params 549 | #pdf.compression : 6 # integer from 0 to 9 550 | # 0 disables compression (good for debugging) 551 | #pdf.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType) 552 | 553 | # svg backend params 554 | #svg.image_inline : True # write raster image data directly into the svg file 555 | #svg.fonttype : 'path' # How to handle SVG fonts: 556 | # 'none': Assume fonts are installed on the machine where the SVG will be viewed. 557 | # 'path': Embed characters as paths -- supported by most SVG renderers 558 | # 'svgfont': Embed characters as SVG fonts -- supported only by Chrome, 559 | # Opera and Safari 560 | #svg.hashsalt : None # if not None, use this string as hash salt 561 | # instead of uuid4 562 | 563 | # docstring params 564 | #docstring.hardcopy = False # set this when you want to generate hardcopy docstring 565 | 566 | # Set the verbose flags. This controls how much information 567 | # matplotlib gives you at runtime and where it goes. The verbosity 568 | # levels are: silent, helpful, debug, debug-annoying. Any level is 569 | # inclusive of all the levels below it. If your setting is "debug", 570 | # you'll get all the debug and helpful messages. When submitting 571 | # problems to the mailing-list, please set verbose to "helpful" or "debug" 572 | # and paste the output into your report. 573 | # 574 | # The "fileo" gives the destination for any calls to verbose.report. 575 | # These objects can a filename, or a filehandle like sys.stdout. 576 | # 577 | # You can override the rc default verbosity from the command line by 578 | # giving the flags --verbose-LEVEL where LEVEL is one of the legal 579 | # levels, e.g., --verbose-helpful. 580 | # 581 | # You can access the verbose instance in your code 582 | # from matplotlib import verbose. 583 | #verbose.level : silent # one of silent, helpful, debug, debug-annoying 584 | #verbose.fileo : sys.stdout # a log filename, sys.stdout or sys.stderr 585 | 586 | # Event keys to interact with figures/plots via keyboard. 587 | # Customize these settings according to your needs. 588 | # Leave the field(s) empty if you don't need a key-map. (i.e., fullscreen : '') 589 | 590 | #keymap.fullscreen : f, ctrl+f # toggling 591 | #keymap.home : h, r, home # home or reset mnemonic 592 | #keymap.back : left, c, backspace # forward / backward keys to enable 593 | #keymap.forward : right, v # left handed quick navigation 594 | #keymap.pan : p # pan mnemonic 595 | #keymap.zoom : o # zoom mnemonic 596 | #keymap.save : s # saving current figure 597 | #keymap.quit : ctrl+w, cmd+w # close the current figure 598 | #keymap.grid : g # switching on/off a grid in current axes 599 | #keymap.yscale : l # toggle scaling of y-axes ('log'/'linear') 600 | #keymap.xscale : L, k # toggle scaling of x-axes ('log'/'linear') 601 | #keymap.all_axes : a # enable all axes 602 | 603 | # Control location of examples data files 604 | #examples.directory : '' # directory to look in for custom installation 605 | 606 | ###ANIMATION settings 607 | #animation.html : 'none' # How to display the animation as HTML in 608 | # the IPython notebook. 'html5' uses 609 | # HTML5 video tag. 610 | #animation.writer : ffmpeg # MovieWriter 'backend' to use 611 | #animation.codec : h264 # Codec to use for writing movie 612 | #animation.bitrate: -1 # Controls size/quality tradeoff for movie. 613 | # -1 implies let utility auto-determine 614 | #animation.frame_format: 'png' # Controls frame format used by temp files 615 | #animation.ffmpeg_path: 'ffmpeg' # Path to ffmpeg binary. Without full path 616 | # $PATH is searched 617 | #animation.ffmpeg_args: '' # Additional arguments to pass to ffmpeg 618 | #animation.avconv_path: 'avconv' # Path to avconv binary. Without full path 619 | # $PATH is searched 620 | #animation.avconv_args: '' # Additional arguments to pass to avconv 621 | #animation.mencoder_path: 'mencoder' 622 | # Path to mencoder binary. Without full path 623 | # $PATH is searched 624 | #animation.mencoder_args: '' # Additional arguments to pass to mencoder 625 | #animation.convert_path: 'convert' # Path to ImageMagick's convert binary. 626 | # On Windows use the full path since convert 627 | # is also the name of a system tool. 628 | -------------------------------------------------------------------------------- /oceanwaves/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from oceanwaves.oceanwaves import * 4 | from oceanwaves import oceanwaves 5 | from oceanwaves import spectral 6 | from oceanwaves import units 7 | from oceanwaves import plot 8 | -------------------------------------------------------------------------------- /oceanwaves/datawell.py: -------------------------------------------------------------------------------- 1 | class DatawellReader: 2 | 3 | 4 | def __init__(self): 5 | 6 | pass 7 | 8 | 9 | def __call__(self, fpath): 10 | 11 | raise NotImplementedError('Reading of Datawell files is not yet implemented') 12 | -------------------------------------------------------------------------------- /oceanwaves/plot.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import numpy as np 3 | from xarray.plot.plot import _PlotMethods 4 | 5 | 6 | try: 7 | import matplotlib.pyplot as plt 8 | from matplotlib.projections import PolarAxes 9 | HAS_MATPLOTLIB = True 10 | except ImportError: 11 | HAS_MATPLOTLIB = False 12 | 13 | 14 | def spatial_map(darray, x, y, ax=None, scale=.1, dim='location', 15 | add_colorbar=True, figure_kw={}, subplot_kw={}, 16 | **kwargs): 17 | '''Plot data on a spatial map 18 | 19 | Creates a subplot for each location in the DataArray and positions 20 | the subplot on a map. Connects events to both map axes and figure 21 | to keep the subplots positioned. 22 | 23 | Parameters 24 | ---------- 25 | darray : xarray.DataArray 26 | DataArray with spatial information 27 | x : list 28 | Array with x-coordinates of locations 29 | y : list 30 | Array with y-coordinates of locations 31 | scale : float 32 | Size of subplots in axes coordinates 33 | dim : str 34 | Name of spatial dimensions 35 | figure_kw : dict 36 | Options passed to :func:`matplotlib.pyplot.subplots` 37 | subplot_kw : dict 38 | Options passed to :func:`matplotlib.pyplot.add_axes` 39 | kwargs : dict 40 | Options passed to :meth:`xarray.DataArray.plot` 41 | 42 | Returns 43 | ------- 44 | ax_map : AxesSubplot 45 | Subplot containing the map 46 | axs : list of AxesSubplot 47 | Positionsed subplots visualizing data 48 | 49 | ''' 50 | 51 | if not HAS_MATPLOTLIB: 52 | raise ImportError('Matplotlib not available') 53 | 54 | # create map axis 55 | if ax is None: 56 | fig, ax_map = plt.subplots(**figure_kw) 57 | ax_map.set_aspect('equal') 58 | else: 59 | ax_map = ax 60 | fig = ax.get_figure() 61 | 62 | # plot locations to set axis limits 63 | ax_map.scatter(x, y, c='none', edgecolor='none') 64 | 65 | # set default plot options 66 | plot_kw = {} 67 | if darray.ndim == 2: 68 | pass 69 | elif darray.ndim == 3: 70 | plot_kw.update(dict(add_colorbar=False, 71 | vmin = np.min(darray.values), 72 | vmax = np.max(darray.values))) 73 | elif darray.ndim == 4: 74 | pass 75 | plot_kw.update(**kwargs) 76 | 77 | # create a subplot for each location 78 | axs = [] 79 | for i, (xi, yi) in enumerate(zip(x, y)): 80 | ax = fig.add_axes([0, 0, 1, 1], label=i, **subplot_kw) 81 | darray[{dim:i}].plot(ax=ax, **plot_kw) 82 | ax.coords = (xi, yi) 83 | ax.set_axis_off() 84 | 85 | axs.append(ax) 86 | 87 | # add colorbar 88 | if add_colorbar: 89 | meshes = [c 90 | for ax in axs 91 | for c in ax.get_children() 92 | if hasattr(c, 'autoscale')] 93 | if len(meshes) > 0: 94 | fig.colorbar(meshes[0], ax=ax_map) 95 | 96 | # set figure events to update subplot positions 97 | ax_map.callbacks.connect('xlim_changed', lambda x: position_subplots(ax_map, axs, scale=scale)) 98 | ax_map.callbacks.connect('ylim_changed', lambda x: position_subplots(ax_map, axs, scale=scale)) 99 | 100 | fig.canvas.mpl_connect('resize_event', lambda x: position_subplots(ax_map, axs, scale=scale)) 101 | fig.canvas.mpl_connect('draw_event', lambda x: position_subplots(ax_map, axs, scale=scale)) 102 | 103 | # set initial subplot positions 104 | position_subplots(ax_map, axs, scale=scale) 105 | 106 | return ax_map, axs 107 | 108 | 109 | def position_subplots(ax_map, axs, scale=.1): 110 | '''Updates subplot positions in map 111 | 112 | Parameters 113 | ---------- 114 | ax_map : AxesSubplot 115 | Map axes object 116 | axs : list of AxesSubplot 117 | List of subplots to be positioned. Each axes should have a 118 | property ``coords`` specifying the target position in data 119 | coordinates of the map axes. 120 | scale : float 121 | Size of subplots in axes coordinates 122 | 123 | ''' 124 | 125 | fig = ax_map.get_figure() 126 | 127 | for i, ax in enumerate(axs): 128 | if not hasattr(ax, 'coords'): 129 | continue 130 | 131 | # get current subplot position in axes coordinates 132 | pos = ax.get_position() 133 | pos_scn = ax_map.transData.transform(ax.coords) 134 | pos_axs = ax_map.transAxes.inverted().transform(pos_scn) 135 | 136 | # get position of subplot corners in figure coordinates 137 | pos_crn = [(pos_axs[0] - scale/2., 138 | pos_axs[1] - scale/2.), 139 | (pos_axs[0] + scale/2., 140 | pos_axs[1] + scale/2.)] 141 | pos_scn = ax_map.transAxes.transform(pos_crn) 142 | pos_fig = fig.transFigure.inverted().transform(pos_scn) 143 | 144 | # update subplot position 145 | pos.x0 = pos_fig[0,0] 146 | pos.x1 = pos_fig[1,0] 147 | pos.y0 = pos_fig[0,1] 148 | pos.y1 = pos_fig[1,1] 149 | 150 | ax.set_position(pos) 151 | 152 | 153 | class OceanWavesPlotMethods(_PlotMethods): 154 | 155 | '''Inheritence class to add map plotting functionality to xarray.DataArray objects''' 156 | 157 | def __init__(self, darray, x=None, y=None, **kwargs): 158 | '''Class initilisation 159 | 160 | Parameters 161 | ---------- 162 | x : list 163 | Array with x-coordinates 164 | y : list 165 | Array with y-coordinates 166 | args, kwargs 167 | Arguments passed to super class 168 | 169 | ''' 170 | 171 | if not HAS_MATPLOTLIB: 172 | raise ImportError('Matplotlib not available') 173 | 174 | self._x = x 175 | self._y = y 176 | super(OceanWavesPlotMethods, self).__init__(darray, **kwargs) 177 | 178 | 179 | def __call__(self, **kwargs): 180 | 181 | # if data is directional, faceted and not yet polar, make it polar 182 | if 'direction' in self._da.dims: 183 | if 'col' in kwargs.keys() or 'row' in kwargs.keys(): 184 | if not 'subplot_kws' in kwargs.keys(): 185 | kwargs.update(dict(subplot_kws = dict(projection = 'polar'), 186 | sharex = False, 187 | sharey = False)) 188 | 189 | r = super(OceanWavesPlotMethods, self).__call__(**kwargs) 190 | 191 | self.find_axes(r) 192 | self.rotate_axes() 193 | 194 | return r 195 | 196 | 197 | @functools.wraps(spatial_map) 198 | def spatial_map(self, ax=None, **kwargs): 199 | '''Plot wave data on map''' 200 | if self._x is None or self._y is None: 201 | raise ValueError('Cannot plot map if locations are not defined') 202 | self._axes = spatial_map(self._da, self._x, self._y, ax=ax, **kwargs)[1] 203 | self.rotate_axes() 204 | return self._axes 205 | 206 | 207 | def find_axes(self, r): 208 | 209 | # find axes 210 | try: 211 | self._axes = r.axes.flatten() 212 | except: 213 | try: 214 | self._axes = r.axes 215 | except: 216 | self._axes = r 217 | 218 | try: 219 | iter(self._axes) 220 | except: 221 | self._axes = [self._axes] 222 | 223 | 224 | def rotate_axes(self): 225 | 226 | # rotate polars 227 | try: 228 | for ax in self._axes: 229 | if isinstance(ax, PolarAxes): 230 | ax.set_theta_zero_location('N') 231 | ax.set_theta_direction(-1) 232 | except: 233 | pass 234 | 235 | 236 | -------------------------------------------------------------------------------- /oceanwaves/spectral.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import numpy as np 4 | 5 | from oceanwaves.utils import * 6 | 7 | import logging 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | def jonswap(f, Hm0, Tp, gamma=3.3, sigma_low=.07, sigma_high=.09, 12 | g=9.81, method='yamaguchi', normalize=True): 13 | '''Generate JONSWAP spectrum 14 | 15 | Parameters 16 | ---------- 17 | f : numpy.ndarray 18 | Array of frequencies 19 | Hm0 : float, numpy.ndarray 20 | Required zeroth order moment wave height 21 | Tp : float, numpy.ndarray 22 | Required peak wave period 23 | gamma : float 24 | JONSWAP peak-enhancement factor (default: 3.3) 25 | sigma_low : float 26 | Sigma value for frequencies <= ``1/Tp`` (default: 0.07) 27 | sigma_high : float 28 | Sigma value for frequencies > ``1/Tp`` (default: 0.09) 29 | g : float 30 | Gravitational constant (default: 9.81) 31 | method : str 32 | Method to compute alpha (default: yamaguchi) 33 | normalize : bool 34 | Normalize resulting spectrum to match ``Hm0`` 35 | 36 | Returns 37 | ------- 38 | E : numpy.ndarray 39 | Array of shape ``f, Hm0.shape`` with wave energy densities 40 | 41 | ''' 42 | 43 | # C Stringari - 04/06/2018 44 | # check input data types to avoid the following error: 45 | # ValueError: Integers to negative integer powers are not allowed. 46 | 47 | # raise an warning if the frequency array starts with zero. if the 48 | # user gives an array with zeros, the output will be inf at that 49 | # frequency 50 | if 0.0 in f: 51 | logger.warn('Frequency array contains zeros.') 52 | 53 | # get the input dtypes and promote to float, if needed 54 | f = ensure_float(f) 55 | Hm0 = ensure_float(Hm0) 56 | Tp = ensure_float(Tp) 57 | 58 | # check shapes of Hm0 and Tp, raise an error if the don't match 59 | if isinstance(Hm0, np.ndarray): 60 | if isinstance(Tp, np.ndarray): 61 | if Hm0.shape != Tp.shape: 62 | raise ValueError("Dimensions of Hm0 and Tp should match.") 63 | 64 | # This is a very naive implementation to deal with array inputs, 65 | # but will work if Hm0 and Tp are vectors. 66 | if isinstance(Hm0, np.ndarray): 67 | f = f[:, np.newaxis].repeat(len(Hm0), axis=1) 68 | Hm0 = Hm0[np.newaxis, :].repeat(len(f), axis=0) 69 | Tp = Tp[np.newaxis, :].repeat(len(f), axis=0) 70 | 71 | # Pierson-Moskowitz 72 | if method.lower() == 'yamaguchi': 73 | alpha = 1. / (.06533 * gamma ** .8015 + .13467) / 16. 74 | elif method.lower() == 'goda': 75 | alpha = 1. / (.23 + .03 * gamma - .185 / (1.9 + gamma)) / 16. 76 | else: 77 | raise ValueError('Unknown method: %s' % method) 78 | 79 | E_pm = alpha * Hm0**2 * Tp**-4 * f**-5 * np.exp(-1.25 * (Tp * f)**-4) 80 | 81 | # JONSWAP 82 | sigma = np.ones(f.shape) * sigma_low 83 | sigma[f > 1./Tp] = sigma_high 84 | 85 | E_js = E_pm * gamma**np.exp(-0.5 * (Tp * f - 1)**2. / sigma**2.) 86 | 87 | if normalize: 88 | # axis=0 seems to work fine with all kinds of inputs 89 | E_js *= Hm0**2. / (16. * trapz_and_repeat(E_js, f, axis=0)) 90 | 91 | return E_js 92 | 93 | 94 | def directional_spreading(theta, theta_peak=0., s=20., units='deg', 95 | normalize=True): 96 | '''Generate wave spreading 97 | 98 | Parameters 99 | ---------- 100 | theta : numpy.ndarray 101 | Array of mean bin directions 102 | theta_peak : float 103 | Peak direction (default: 0) 104 | s : float 105 | Exponent in cosine law (default: 20) 106 | units : str 107 | Directional units (deg or rad, default: deg) 108 | normalize : bool 109 | Normalize resulting spectrum to unity 110 | 111 | Returns 112 | ------- 113 | p_theta : numpy.ndarray 114 | Array of directional weights 115 | ''' 116 | 117 | from math import gamma 118 | 119 | theta = np.asarray(theta, dtype=np.float) 120 | 121 | # convert units to radians 122 | if units.lower().startswith('deg'): 123 | theta = np.radians(theta) 124 | theta_peak = np.radians(theta_peak) 125 | elif units.lower().startswith('rad'): 126 | pass 127 | else: 128 | raise ValueError('Unknown units: %s') 129 | 130 | # compute directional spreading 131 | # A1 = (2.**s) * (gamma(s / 2 + 1))**2. / (np.pi * gamma(s + 1)) 132 | # p_theta = A1 * np.maximum(0., np.cos(theta - theta_peak)) 133 | p_theta = np.maximum(0., np.cos(theta - theta_peak))**s 134 | 135 | # convert to original units 136 | if units.lower().startswith('deg'): 137 | theta = np.degrees(theta) 138 | theta_peak = np.degrees(theta_peak) 139 | p_theta = np.degrees(p_theta) 140 | 141 | # normalize directional spreading 142 | if normalize: 143 | p_theta /= trapz_and_repeat(p_theta, theta - theta_peak, axis=-1) 144 | 145 | return p_theta 146 | 147 | 148 | def ensure_float(var): 149 | ''' 150 | Auxiliary function to detect and fix dtypes, if needed 151 | 152 | Parameters 153 | ---------- 154 | var : anything 155 | Array to check 156 | 157 | Returns 158 | ------- 159 | var : numpy.ndarray 160 | The same as the input but either as a float or an array of floats 161 | ''' 162 | # fist, if it's a list, convert to a numpy.ndarray 163 | if isinstance(var, list): 164 | var = np.array(var) 165 | # if it's an np.ndarray(), make sure it's a array of floats 166 | elif isinstance(var, np.ndarray): 167 | if var.dtype != np.dtype('float'): 168 | var = var.astype(np.float) 169 | # if it's a float, well, do nothing 170 | elif isinstance(var, float): 171 | var = var 172 | # unknown data type 173 | else: 174 | raise ValueError("Data type could not be detected automatically.") 175 | # allways return a float or array of floats 176 | return var 177 | -------------------------------------------------------------------------------- /oceanwaves/swan.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | import re 5 | import glob 6 | import json 7 | import logging 8 | import xarray as xr 9 | import numpy as np 10 | import pandas as pd 11 | from datetime import datetime 12 | from collections import OrderedDict 13 | 14 | try: 15 | import pyproj 16 | HAS_PYPROJ = True 17 | except ImportError: 18 | HAS_PYPROJ = False 19 | 20 | import oceanwaves.oceanwaves 21 | 22 | 23 | TABLE_UNITS_FILE = 'table_units.json' 24 | SWAN_TIME_FORMAT = '%Y%m%d.%H%M%S' 25 | 26 | 27 | class SwanBlockReader: 28 | '''Class for memory efficient reading of large files. 29 | 30 | The class mimics the open().readlines() functionality as it 31 | implements a __getitem__ method. But lines are only read once they 32 | are requested. In addition, the class implements a line 33 | pointer. Lines prior to the current pointer position are 34 | immediately discarded. Therefore, only the currently required 35 | lines remain in memory. 36 | 37 | Note that indexing is always done with respect to the current 38 | pointer position. 39 | 40 | The class also implements convenience methods to read SWAN file 41 | blocks. 42 | 43 | Examples 44 | -------- 45 | >>> lines = SwanBlockReader.open('a11.sp2') 46 | >>> lines[0] # first line of file 47 | >>> lines[:10] # first ten lines from file 48 | 49 | >>> lines.advance() # advance file pointer one line (i.e. discard current line) 50 | >>> lines[0] # read second line of file (after pointer was advanced) 51 | 52 | >>> lines.advance(10) # advance file pointer ten lines 53 | >>> lines[0] # read tenth line of file (after pointer was advanced) 54 | 55 | >>> lines.readblock() # read data block starting at lines[1] (current position at key) 56 | 57 | ''' 58 | 59 | 60 | def __init__(self, filename): 61 | '''Initialize class 62 | 63 | Parameters 64 | ---------- 65 | filename : str 66 | Path to filename 67 | 68 | ''' 69 | 70 | self.filename = filename 71 | self.fp = open(filename, 'r') # file pointer 72 | self.n = 0 # line pointer 73 | self.eof = False # end of file 74 | self.lines = [] # line buffer 75 | 76 | 77 | def __enter__(self): 78 | '''Convenience function for usage with `with`''' 79 | pass 80 | 81 | 82 | def __exit__(self): 83 | '''Convenience function for usage with `with`''' 84 | self.close() 85 | 86 | 87 | def __getitem__(self, s): 88 | '''Implementation of line array mimicing 89 | 90 | Two types of indexing are supported: slice of integer. For 91 | each type the maximum extent of the reuqest (number of 92 | required lines from current pointer position) is 93 | determined. The line buffer is appended with the required 94 | amount of lines, if not yet sufficient. The requested part of 95 | the line buffer is returned. 96 | 97 | In case the end of file is reached, the remaining line buffer 98 | is returned. 99 | 100 | Parameters 101 | ---------- 102 | s : slice of int 103 | Line buffer index 104 | 105 | See Also 106 | -------- 107 | readline 108 | 109 | ''' 110 | 111 | if isinstance(s, slice): 112 | # append buffer to match max slice 113 | if s.stop is not None: 114 | while s.stop > len(self.lines) and not self.eof: 115 | self.readline() 116 | else: 117 | logger.warn('Unbound block read, might be missing data.') 118 | # append buffer to match min slice 119 | if s.start is not None: 120 | while s.start >= len(self.lines) and not self.eof: 121 | self.readline() 122 | elif isinstance(s, int): 123 | # append buffer to match index 124 | while s >= len(self.lines) and not self.eof: 125 | self.readline() 126 | else: 127 | raise KeyError('Block definition should be numeric') 128 | 129 | if self.eof: 130 | return self.lines 131 | else: 132 | return self.lines[s] 133 | 134 | 135 | def __add__(self, n): 136 | '''Alternative to `advance` method 137 | 138 | See Also 139 | -------- 140 | advance 141 | 142 | ''' 143 | 144 | self.advance(n=n) 145 | 146 | 147 | @classmethod 148 | def open(cls, filename): 149 | '''Class method to instantiate from file''' 150 | return cls(filename) 151 | 152 | 153 | def close(self): 154 | '''Close file pointer''' 155 | self.fp.close() 156 | 157 | 158 | def readline(self, n=1): 159 | '''Reads one or more lines from file into line buffer 160 | 161 | Sets end of file indicator, if file end is reached. 162 | 163 | Parameters 164 | ---------- 165 | n : int 166 | Number of lines to read 167 | 168 | Returns 169 | ------- 170 | self 171 | 172 | ''' 173 | 174 | for i in range(n): 175 | line = self.fp.readline() 176 | if line == '': 177 | self.eof = True 178 | break 179 | else: 180 | self.eof = False 181 | self.lines.append(line.rstrip('\n')) 182 | return self 183 | 184 | 185 | def advance(self, n=1): 186 | '''Advances line pointer 187 | 188 | Parameters 189 | ---------- 190 | n : int 191 | Number of lines to advance 192 | 193 | ''' 194 | 195 | self.n += n 196 | self.lines = self.lines[n:] 197 | 198 | 199 | def read_block(self): 200 | '''Reads data block 201 | 202 | Data blocks are expected to start at the second line in the 203 | current line buffer. This line contains an integer indicating 204 | the size of the data block. The line pointer is automatically 205 | advanced with the number of lines in the block, plus one for 206 | the size integer. 207 | 208 | See Also 209 | -------- 210 | read_blockbody 211 | 212 | ''' 213 | 214 | m = re.match('\s*(\d+)', self[1]) 215 | if m: 216 | n = int(m.groups()[0]) 217 | else: 218 | raise ValueError('Length of block not understood: %s' % self[1]) 219 | 220 | self.advance() 221 | 222 | return self.read_blockbody(n) 223 | 224 | 225 | def read_blockbody(self, n): 226 | '''Reads data in block given the block size 227 | 228 | Parameters 229 | ---------- 230 | n : int 231 | Number of lines in block 232 | 233 | Returns 234 | ------- 235 | list 236 | Data from block 237 | 238 | See Also 239 | -------- 240 | read_block 241 | 242 | ''' 243 | 244 | block = [] 245 | for i in range(n): 246 | arr = re.split('\s+', self[1+i].strip()) 247 | arr = tuple([float(x) for x in arr]) 248 | block.append(arr) 249 | 250 | self.advance(n) 251 | 252 | return block 253 | 254 | 255 | class SwanSpcReader: 256 | 257 | 258 | def __init__(self): 259 | 260 | self.stationairy = True 261 | self.directional = False 262 | 263 | self.crs = None 264 | self.frequency_convention = None 265 | self.direction_convention = None 266 | 267 | self.reset() 268 | 269 | 270 | def __call__(self, fpath): 271 | 272 | self.reset() 273 | return self.read(fpath) 274 | 275 | 276 | def reset(self): 277 | 278 | self.stationary = True 279 | self.directional = False 280 | 281 | self.version = None 282 | self.timecoding = None 283 | self.comments = [] 284 | 285 | self.time = [] 286 | self.locations = [] 287 | self.frequencies = [] 288 | self.directions = [] 289 | self.specs = OrderedDict() 290 | self.quantities = [] 291 | 292 | self.l = 0 # location counter 293 | 294 | 295 | def read(self, fpath): 296 | 297 | for fname in glob.glob(fpath): 298 | self.readfile(fname) 299 | 300 | return self.to_oceanwaves() 301 | 302 | 303 | def readfile(self, fpath): 304 | 305 | self.lines = SwanBlockReader.open(fpath) 306 | 307 | while True: 308 | 309 | line = self.lines[0] 310 | 311 | if self.lines.eof: 312 | break 313 | elif line.startswith('$'): 314 | self.parse_comments() 315 | elif line.startswith('SWAN'): 316 | self.parse_version() 317 | elif line.startswith('ITER'): 318 | self.parse_iter() 319 | elif line.startswith('TIME'): 320 | self.parse_time() 321 | elif line.startswith('LOCATIONS'): 322 | self.parse_locations() 323 | elif line.startswith('LONLAT'): 324 | self.crs = 'epsg:4326' 325 | self.parse_locations() 326 | elif line.startswith('AFREQ'): 327 | self.frequency_convention = 'absolute' 328 | self.parse_frequencies() 329 | elif line.startswith('RFREQ'): 330 | self.frequency_convention = 'relative' 331 | self.parse_frequencies() 332 | elif line.startswith('NDIR'): 333 | self.direction_convention = 'nautical' 334 | self.parse_directions() 335 | elif line.startswith('CDIR'): 336 | self.direction_convention = 'cartesian' 337 | self.parse_directions() 338 | elif line.startswith('QUANT'): 339 | self.parse_quantities() 340 | elif line.startswith('FACTOR'): 341 | self.parse_data() 342 | elif line.startswith('LOCATION'): 343 | self.parse_data() 344 | elif line.startswith('NODATA'): 345 | self.parse_nodata() 346 | elif line.startswith('ZERO'): 347 | self.parse_nodata(fill_value=0.) 348 | elif re.match('\s*[\d\.]+', line): 349 | self.parse_timestamp() 350 | else: 351 | logging.warn('Line not parsed: %s' % line) 352 | 353 | self.lines.advance() 354 | 355 | self.lines.close() 356 | 357 | 358 | def to_oceanwaves(self): 359 | 360 | energy_units = '1' 361 | for var, specs in self.specs.items(): 362 | if 'units' in specs.keys(): 363 | energy_units = specs['units'] 364 | break 365 | 366 | kwargs = dict( 367 | location=self.locations, 368 | location_units='m' if self.crs is None else 'deg', 369 | frequency=self.frequencies, 370 | frequency_units='Hz', 371 | frequency_convention=self.frequency_convention, 372 | energy_units=energy_units, 373 | attrs=dict(comments='\n'.join(self.comments)), 374 | crs=self.crs 375 | ) 376 | 377 | if self.directional: 378 | kwargs.update(dict( 379 | direction=self.directions, 380 | direction_units='deg', 381 | direction_convention=self.direction_convention, 382 | energy=self.quantities 383 | )) 384 | if not self.stationary: 385 | kwargs.update(dict( 386 | time=self.time, 387 | time_units='s' 388 | )) 389 | else: 390 | if not self.stationary: 391 | kwargs.update(dict( 392 | time=self.time, 393 | time_units='s', 394 | energy=[[q2[:,0] for q2 in q1] for q1 in self.quantities], 395 | direction=[[q2[:,1] for q2 in q1] for q1 in self.quantities], 396 | spreading=[[q2[:,2] for q2 in q1] for q1 in self.quantities] 397 | )) 398 | else: 399 | kwargs.update(dict( 400 | energy=[q[:,0] for q in self.quantities], 401 | direction=[q[:,1] for q in self.quantities], 402 | spreading=[q[:,2] for q in self.quantities] 403 | )) 404 | kwargs.update(dict(directional=self.directional)) 405 | 406 | return oceanwaves.oceanwaves.OceanWaves(**kwargs) 407 | 408 | 409 | def parse_comments(self): 410 | self.comments.append(self.lines[0][1:].strip()) 411 | 412 | 413 | def parse_version(self): 414 | m = re.match('SWAN\s+([^\s]+)', self.lines[0]) 415 | if m: 416 | version = m.groups()[0] 417 | self._check_if_matches(self.version, version, 418 | errormsg='Version mismatch') 419 | self.version = version 420 | 421 | 422 | def parse_iter(self): 423 | raise NotImplementedError( 424 | 'Reading of SWAN test files containing the ITER keyword are not yet ' 425 | 'supported. Please contribute to the oceanwaves toolbox by providing an ' 426 | 'exhaustive data format description or working implementation.' 427 | ) 428 | 429 | 430 | def parse_time(self): 431 | m = re.match('\s*([^\s]+)', self.lines[1]) 432 | if m: 433 | timecoding = m.groups()[0] 434 | self._check_if_matches(self.timecoding, timecoding, 435 | errormsg='Timecoding mismatch') 436 | self.timecoding = timecoding 437 | self.stationary = False 438 | self.lines.advance() 439 | 440 | 441 | def parse_locations(self): 442 | locations = self.lines.read_block() 443 | if not self.stationary: 444 | self._check_if_matches(self.locations, locations, 445 | errormsg='Location dimension mismatch') 446 | self.locations = locations 447 | else: 448 | self.locations.extend(locations) 449 | 450 | 451 | def parse_frequencies(self): 452 | frequencies = np.asarray(self.lines.read_block()).flatten() 453 | self._check_if_matches(self.frequencies, frequencies, 454 | errormsg='Frequency dimension mismatch') 455 | self.frequencies = frequencies 456 | 457 | 458 | def parse_directions(self): 459 | directions = np.asarray(self.lines.read_block()).flatten() 460 | self._check_if_matches(self.directions, directions, 461 | errormsg='Direction dimension mismatch') 462 | self.directions = directions 463 | self.directional = True 464 | 465 | 466 | def parse_quantities(self): 467 | m = re.match('\s*(\d+)', self.lines[1]) 468 | if m: 469 | n = int(m.groups()[0]) 470 | else: 471 | raise ValueError('Number of quantities not understood: %s' % self.lines[1]) 472 | 473 | self.lines.advance() 474 | 475 | self.specs = OrderedDict() 476 | for i in range(n): 477 | q = [] 478 | for j in range(3): 479 | m = re.match('\s*([^\s]+)', self.lines[1+j]) 480 | if m: 481 | q.append(m.groups()[0]) 482 | 483 | if len(q) == 3: 484 | self.specs[q[0]] = dict(zip(('units', 'fill_value'), q[1:])) 485 | else: 486 | logging.warn('Skipped invalid quantity definiton: %s' % ' '.join(q)) 487 | 488 | self.lines.advance(3) 489 | 490 | 491 | def parse_data(self): 492 | if self.lines[0].startswith('FACTOR'): 493 | factor = self.lines[1] 494 | m = re.match('\s*([\+\-\d\.Ee]+)\s*$', factor) 495 | if m: 496 | f = float(m.groups()[0]) 497 | else: 498 | raise ValueError('Factor not understood: %s' % factor) 499 | 500 | self.lines.advance() 501 | else: 502 | f = 1. 503 | 504 | n = len(self.frequencies) 505 | q = np.asarray(self.lines.read_blockbody(n)) * f 506 | if self.stationary: 507 | self.quantities.append(q) 508 | else: 509 | self.quantities[-1].append(q) 510 | 511 | 512 | def parse_nodata(self, fill_value=np.nan): 513 | if self.directional: 514 | q = np.zeros((len(self.frequencies), 515 | len(self.directions))) 516 | else: 517 | q = np.zeros((len(self.frequencies), 3)) 518 | 519 | q += fill_value 520 | 521 | if self.stationary: 522 | self.quantities.append(q) 523 | else: 524 | self.quantities[-1].append(q) 525 | 526 | 527 | def parse_timestamp(self): 528 | m = re.match('\s*([\d\.]+)', self.lines[0]) 529 | if m: 530 | self.time.append(datetime.strptime(m.groups()[0], SWAN_TIME_FORMAT)) 531 | self.quantities.append([]) 532 | else: 533 | raise ValueError('Time definition not understood: %s' % self.lines[0]) 534 | 535 | 536 | def _check_if_matches(self, current, new, errormsg='Dimension mismatch'): 537 | if current is None: 538 | return True 539 | elif type(current) is list: 540 | if len(current) == 0: 541 | return 542 | else: 543 | try: 544 | if all([a == b for a, b in zip(current, new)]): 545 | return 546 | except: 547 | pass 548 | else: 549 | if current == new: 550 | return 551 | 552 | raise ValueError(errormsg) 553 | 554 | 555 | class SwanSpcWriter: 556 | 557 | 558 | def __init__(self, obj): 559 | 560 | self.obj0 = obj.as_degrees() 561 | self.obj = self.obj0 562 | 563 | 564 | def __call__(self, fpath): 565 | 566 | self.write(fpath) 567 | 568 | 569 | def write(self, fpath): 570 | 571 | if self.obj.has_dimension('time'): 572 | fpath, fext = os.path.splitext(fpath) 573 | k = self._key_lookup('_time') 574 | for ix in range(len(self.obj.coords[k])): 575 | self.obj = self.obj0[dict(_time=ix)] 576 | self.writefile('%s_%03d%s' % (fpath, ix, fext)) 577 | else: 578 | self.obj = self.obj0 579 | self.writefile(fpath) 580 | 581 | 582 | def writefile(self, fpath): 583 | 584 | self.fp = open(fpath, 'w') 585 | self.fp.write('SWAN %4d\n' % 1) 586 | 587 | self.write_comments() 588 | self.write_time() 589 | self.write_locations() 590 | self.write_frequencies() 591 | self.write_directions() 592 | self.write_quantities() 593 | self.write_timestamp() 594 | self.write_data() 595 | 596 | self.fp.close() 597 | 598 | 599 | def write_comments(self): 600 | 601 | comments = self._get_attr('comments', default=[]) 602 | for c in comments: 603 | self.fp.write('$ %s\n' % c) 604 | 605 | 606 | def write_time(self): 607 | 608 | if self.obj.has_dimension('time'): 609 | self.fp.write('TIME\n') 610 | self.fp.write('%4d\n' % 1) 611 | 612 | 613 | def write_locations(self, latlon=False): 614 | 615 | if self.obj.has_dimension('location'): 616 | 617 | crs = self._get_attr('_crs') 618 | if crs is not None: 619 | if not HAS_PYPROJ: 620 | logger.warn('Package "pyproj" is not installed, cannot ' 621 | 'apply coordinate reference system.') 622 | else: 623 | latlon = pyproj.Proj(init=crs).is_latlong() 624 | 625 | if latlon: 626 | self.fp.write('LONLAT\n') 627 | else: 628 | self.fp.write('LOCATIONS\n') 629 | 630 | k = self.obj._key_lookup('_location') 631 | x = self.obj.variables['%s_x' % k].values 632 | y = self.obj.variables['%s_y' % k].values 633 | self.fp.write('%4d\n' % len(x)) 634 | for coords in zip(x, y): 635 | self.fp.write('%10.2f %10.2f\n' % coords) 636 | 637 | 638 | def write_frequencies(self, convention='absolute'): 639 | 640 | if self.obj.has_dimension('frequency'): 641 | 642 | convention = self._get_convention('frequency', convention) 643 | fmt = '%10.4f' 644 | 645 | if convention.lower() == 'relative': 646 | self._write_block('RFREQ', self.obj['_frequency'], fmt=fmt) 647 | else: 648 | self._write_block('AFREQ', self.obj['_frequency'], fmt=fmt) 649 | 650 | 651 | def write_directions(self, convention='nautical'): 652 | 653 | if self.obj.has_dimension('direction'): 654 | 655 | convention = self._get_convention('direction', convention) 656 | fmt = '%10.2f' 657 | 658 | if convention.lower() == 'cartesian': 659 | self._write_block('CDIR', self.obj['_direction'], fmt=fmt) 660 | else: 661 | self._write_block('NDIR', self.obj['_direction'], fmt=fmt) 662 | 663 | 664 | def write_quantities(self): 665 | 666 | self.fp.write('QUANT\n') 667 | 668 | if self.obj.has_dimension('direction'): 669 | 670 | self.fp.write('%4d\n' % 1) 671 | self.fp.write('VaDens\n') 672 | self.fp.write('%s\n' % self._get_units('_energy', 'm^2 s')) 673 | self.fp.write('-99.0\n') # TODO: replace NaN with fill value 674 | 675 | else: 676 | 677 | convention = self._get_convention('direction', 'nautical').lower() 678 | 679 | self.fp.write('%4d\n' % 3) 680 | self.fp.write('VaDens\n') 681 | self.fp.write('%s\n' % self._get_units('_energy', 'm^2 s')) 682 | self.fp.write('-99.0\n') 683 | self.fp.write('%s\n' % ('CDIR' if convention == 'cartesion' else 'NDIR')) 684 | self.fp.write('%s\n' % self._get_units('_direction', 'deg')) 685 | self.fp.write('-999\n') 686 | self.fp.write('DSPRDEGR\n') 687 | self.fp.write('%s\n' % self._get_units('_spreading', 'deg')) 688 | self.fp.write('-9\n') 689 | 690 | 691 | def write_timestamp(self): 692 | 693 | if self.obj.has_dimension('time'): 694 | time = self.obj['_time'].values[0].strftime(SWAN_TIME_FORMAT) 695 | self.fp.write('%s\n' % time) 696 | 697 | 698 | def write_data(self): 699 | 700 | E = self.obj['_energy'].values 701 | 702 | try: 703 | D = self.obj['_direction'].values 704 | except: 705 | D = np.zeros(E.shape) 706 | 707 | try: 708 | S = self.obj['_spreading'].values 709 | except: 710 | S = np.zeros(E.shape) 711 | 712 | if self.obj.has_dimension('direction'): 713 | 714 | if E.ndim == 2: 715 | E = E[np.newaxis,:,:] 716 | 717 | n = E.shape[2] 718 | for i in range(E.shape[0]): 719 | 720 | if E[i,:,:].max() == 0: 721 | f = 1. 722 | else: 723 | f = E[i,:,:].max() / 999999. 724 | 725 | self.fp.write('FACTOR\n') 726 | self.fp.write('%4e\n' % f) 727 | 728 | fmt = '%s\n' % ('%8d ' * n) 729 | for j in range(E.shape[1]): 730 | self.fp.write(fmt % tuple(E[i,j,:] / f)) 731 | 732 | else: 733 | 734 | if E.ndim == 1: 735 | E = E[np.newaxis,:] 736 | D = D[np.newaxis,:] 737 | S = S[np.newaxis,:] 738 | 739 | n = E.shape[1] 740 | for i in range(E.shape[0]): 741 | self.fp.write('LOCATION %4d\n' % i) 742 | fmt = '%8e %8e %8e\n' 743 | for j in range(E.shape[1]): 744 | self.fp.write(fmt % (E[i,j], D[i,j], S[i,j])) 745 | 746 | 747 | def _write_block(self, header, data, fmt='%10.4f'): 748 | 749 | self.fp.write('%s\n' % header.upper()) 750 | self.fp.write('%4d\n' % len(data)) 751 | 752 | for x in data.values: 753 | self.fp.write(('%s\n' % fmt) % x) 754 | 755 | 756 | def _get_convention(self, convention, default=None): 757 | 758 | conventions = self._get_attr('_conventions', default={}) 759 | if convention in conventions.keys(): 760 | return conventions[convention] 761 | else: 762 | return default 763 | 764 | 765 | def _get_attr(self, attr, default=None): 766 | 767 | if attr in self.obj.attrs.keys(): 768 | return self.obj.attrs[attr] 769 | else: 770 | return default 771 | 772 | 773 | def _get_units(self, variable, default=None): 774 | 775 | if variable in self.obj.variables.keys(): 776 | attrs = self.obj.variables[variable].attrs 777 | if 'units' in attrs.keys(): 778 | return attrs['units'] 779 | return default 780 | 781 | 782 | class SwanTableReader: 783 | 784 | 785 | def __init__(self): 786 | 787 | pass 788 | 789 | 790 | def __call__(self, fpath, columns=[], time_var='Time', 791 | location_vars=['Xp', 'Yp'], frequency_var='RTpeak', 792 | direction_var='Dir', energy_var='Hsig', **kwargs): 793 | 794 | # clear variables 795 | self.headers = [] 796 | self.columns = [] 797 | self.units = [] 798 | self.data = [] 799 | self.attrs = {} 800 | 801 | # assume columns names and units 802 | self.columns = columns 803 | self.get_units() 804 | 805 | # read data, column names and units from file 806 | self.read(fpath) 807 | self.parse_headers() 808 | self.check_integrity() 809 | 810 | return self.to_oceanwaves( 811 | energy_var=energy_var, 812 | time_var=time_var, 813 | **kwargs 814 | ) 815 | 816 | 817 | def to_oceanwaves(self, time_var='Time', 818 | location_vars=['Xp','Yp'], period_var='RTpeak', 819 | frequency_var=None, direction_var='Dir', 820 | energy_var='Hsig', **kwargs): 821 | 822 | '''Converts raw data in OceanWaves object 823 | 824 | Converts raw data and column names into Pandas 825 | DataFrame. Groups the DataFrame by time and location. Converts 826 | the MultiIndex DataFrame into an xarray Dataset. Adds unit 827 | information as attributes and uses the resulting Dataset to 828 | initialize a OceanWaves object. 829 | 830 | See for possible initialization arguments the 831 | :class:`OceanWaves` class. 832 | 833 | Returns 834 | ------- 835 | OceanWaves 836 | OceanWaves object. 837 | 838 | ''' 839 | 840 | df = pd.DataFrame(self.data, columns=self.columns) 841 | 842 | # group by time 843 | if time_var in df.columns: 844 | df[time_var] = ([datetime.strptime('%15.6f' % t, SWAN_TIME_FORMAT) 845 | for t in df[time_var].values]) 846 | dfs1 = [] 847 | grouped = df.groupby(time_var) 848 | for t, group in grouped: 849 | group.set_index(time_var, drop=True, append=True, inplace=True) 850 | dfs1.append(group) 851 | else: 852 | dfs1 = [df] 853 | 854 | # group by location 855 | if all([v in df.columns for v in location_vars]): 856 | dfs2 = [] 857 | for df in dfs1: 858 | grouped = df.groupby(location_vars) 859 | for (x, y), group in grouped: 860 | group = group.copy() 861 | group['Location'] = [(x,y)] * len(group) 862 | group.drop(location_vars, axis=1, inplace=True) 863 | group.set_index('Location', drop=True, append=True, inplace=True) 864 | dfs2.append(group) 865 | else: 866 | dfs2 = dfs1 867 | 868 | # concatenate per time/location dataframes and reset index 869 | df = pd.concat(dfs2, axis=0) 870 | df = df.reset_index(0, drop=True) 871 | 872 | # convert period to frequency 873 | if frequency_var is None: 874 | frequency_var = 'Freq' 875 | df[frequency_var] = 1./df[period_var] 876 | 877 | # convert dataframe to dataset and add units 878 | xa = df.to_xarray() 879 | for k in xa.variables.keys(): 880 | if k in self.columns: 881 | ix = self.columns.index(k) 882 | xa.variables[k].attrs['units'] = self.units[ix] 883 | elif k == 'Location': 884 | units = set() 885 | for v in location_vars: 886 | ix = self.columns.index(v) 887 | units.add(self.units[ix]) 888 | if len(units) == 1: 889 | xa.variables[k].attrs['units'] = units.pop() 890 | else: 891 | raise ValueError('Inconsistent units for location coordinates.') 892 | else: 893 | xa.variables[k].attrs['units'] = '1' 894 | 895 | # convert dataset to oceanwaves object 896 | return oceanwaves.OceanWaves.from_dataset( 897 | xa, 898 | time_var=time_var, 899 | location_var='Location', 900 | frequency_var=frequency_var, 901 | direction_var=direction_var, 902 | energy_var=energy_var, 903 | **kwargs 904 | ) 905 | 906 | 907 | def read(self, fpath): 908 | '''Read headers and data seperately''' 909 | 910 | with open(fpath, 'r') as fp: 911 | for line in fp: 912 | if line.startswith('%'): 913 | self.headers.append(line[1:].strip()) 914 | else: 915 | self.data.append([float(x) for x in line.split()]) 916 | 917 | self.data = np.asarray(self.data) 918 | 919 | 920 | def parse_headers(self): 921 | '''Parse headers into attributes, units and column names''' 922 | 923 | for line in self.headers: 924 | if len(line) == 0: 925 | continue 926 | elif ':' in line: 927 | self.attrs.update(dict([re.split('\s*:\s*', x) 928 | for x in re.split('\s{2,}', line)])) 929 | elif re.search('\[\S*\]', line): 930 | self.units = re.findall('\[\s*(\S*)\s*\]', line) 931 | else: 932 | self.columns = re.split('\s+', line) 933 | 934 | 935 | def check_integrity(self): 936 | '''Check integrity of parsed data 937 | 938 | Raises 939 | ------ 940 | ValueError 941 | If no columns are specified when using NOHEAD option or if 942 | the number of column names or units do not match the 943 | number of data columns. 944 | 945 | ''' 946 | 947 | if not self.columns: 948 | raise ValueError('Column names must be specified ' 949 | 'when using \'NOHEAD\' option.') 950 | if self.data.shape[1] != len(self.columns): 951 | raise ValueError('Number of column names (%d) does not match ' 952 | 'number of data columns (%d).' % (self.data.shape[1], 953 | len(self.columns))) 954 | if self.data.shape[1] != len(self.units): 955 | raise ValueError('Number of units (%d) does not match ' 956 | 'number of data columns (%d).' % (self.data.shape[1], 957 | len(self.units))) 958 | 959 | 960 | def get_units(self): 961 | '''Read relevant units from JSON file''' 962 | 963 | jsonpath = os.path.join(os.path.split(__file__)[0], TABLE_UNITS_FILE) 964 | if os.path.exists(jsonpath): 965 | with open(jsonpath, 'r') as fp: 966 | self.units = [u for c, u in json.load(fp).items() if c in self.columns] 967 | -------------------------------------------------------------------------------- /oceanwaves/table_units.json: -------------------------------------------------------------------------------- 1 | { 2 | "Stur": "m2/s", 3 | "FrCoef": null, 4 | "Leak": "m2/s", 5 | "Ubot": "m/s", 6 | "Propsi": "m2/s", 7 | "Steepn": null, 8 | "Watlev": "m", 9 | "FSpr": null, 10 | "Ssurf": "m2/s", 11 | "BFI": null, 12 | "Swcap": "m2/s", 13 | "dHs": "m", 14 | "Turb": "m2/s", 15 | "Dir": "degr", 16 | "Dist": "m", 17 | "X-Transp": "m3/s", 18 | "Wlen": "m", 19 | "Y-Vel": "m/s", 20 | "TDir": "degr", 21 | "Tsec": "s", 22 | "Hswell": "m", 23 | "Sfric": "m2/s", 24 | "TPsmoo": "sec", 25 | "RTpeak": "sec", 26 | "WfPT10": null, 27 | "Dissip": "m2/s", 28 | "Redist": "m2/s", 29 | "Genera": "m2/s", 30 | "Radstr": "m2/s", 31 | "HsPT10": "m", 32 | "Y-Windv": "m/s", 33 | "TpPT08": "sec", 34 | "TpPT09": "sec", 35 | "TpPT06": "sec", 36 | "TpPT07": "sec", 37 | "TpPT04": "sec", 38 | "TpPT05": "sec", 39 | "TpPT02": "sec", 40 | "TpPT03": "sec", 41 | "TpPT01": "sec", 42 | "WfPT01": null, 43 | "WfPT03": null, 44 | "WfPT02": null, 45 | "Swind": "m2/s", 46 | "WfPT04": null, 47 | "WfPT07": null, 48 | "RTm_10": "sec", 49 | "WfPT09": null, 50 | "WfPT08": null, 51 | "WlPT03": "m", 52 | "Propag": "m2/s", 53 | "Tm_10": "sec", 54 | "HsPT09": "m", 55 | "HsPT08": "m", 56 | "HsPT01": "m", 57 | "WfPT05": null, 58 | "HsPT03": "m", 59 | "HsPT02": "m", 60 | "HsPT05": "m", 61 | "HsPT04": "m", 62 | "HsPT07": "m", 63 | "HsPT06": "m", 64 | "X-Vel": "m/s", 65 | "Y-Transp": "m3/s", 66 | "dTm": "sec", 67 | "RTm01": "sec", 68 | "Snl4": "m2/s", 69 | "Snl3": "m2/s", 70 | "TpPT10": "sec", 71 | "DrPT08": "degr", 72 | "DrPT09": "degr", 73 | "DsPT": "degr", 74 | "DrPT04": "degr", 75 | "DrPT05": "degr", 76 | "DrPT06": "degr", 77 | "DrPT07": "degr", 78 | "WlPT10": "m", 79 | "DrPT01": "degr", 80 | "DrPT02": "degr", 81 | "DrPT03": "degr", 82 | "X-WForce": "N/m2", 83 | "Tm01": "sec", 84 | "Tm02": "sec", 85 | "Lwavp": "m", 86 | "StPT10": null, 87 | "WlPT09": "m", 88 | "WlPT08": "m", 89 | "Time": null, 90 | "X-Windv": "m/s", 91 | "WlPT02": "m", 92 | "WlPT01": "m", 93 | "DrPT10": "degr", 94 | "WlPT07": "m", 95 | "WlPT06": "m", 96 | "WlPT05": "m", 97 | "WlPT04": "m", 98 | "WfPT06": null, 99 | "Dspr": "degr", 100 | "DsPT10": "degr", 101 | "Propxy": "m2/s", 102 | "PkDir": "degr", 103 | "Xp": "m", 104 | "Propth": "m2/s", 105 | "StPT09": null, 106 | "StPT08": null, 107 | "StPT07": null, 108 | "StPT06": null, 109 | "StPT05": null, 110 | "Hsig": "m", 111 | "StPT03": null, 112 | "StPT02": null, 113 | "StPT01": null, 114 | "StPT04": null, 115 | "DsPT09": "degr", 116 | "DsPT08": "degr", 117 | "DsPT05": "degr", 118 | "DsPT04": "degr", 119 | "DsPT07": "degr", 120 | "DsPT01": "degr", 121 | "DsPT03": "degr", 122 | "DsPT02": "degr", 123 | "Yp": "m", 124 | "Smud": "m2/s", 125 | "Setup": "m", 126 | "Qp": null, 127 | "Botlev": "m", 128 | "TmBot": "sec", 129 | "Depth": "m", 130 | "Qb": null, 131 | "Y-WForce": "N/m2", 132 | "Urms": "m/s" 133 | } -------------------------------------------------------------------------------- /oceanwaves/units.py: -------------------------------------------------------------------------------- 1 | import re 2 | import six 3 | import logging 4 | import numpy as np 5 | 6 | 7 | # regular expressions 8 | RE_OPERATORS = r'\s*([ \*\/\+\-\^])\s*' 9 | RE_TERMS = r'([^\^])([\+\-])' 10 | RE_GROUPS = r'\/?\(([^\(\)]+?)\)((\^[\+\-\d\.]+)*)' 11 | RE_NUMBER = r'[\+\-\d\.]+' 12 | RE_VARIABLE = r'[a-zA-Z]+' 13 | RE_EXPONENTS = r'([^a-zA-Z])?(%s(\^[\+\-\d\.]+)*)' 14 | RE_EXPONENTS_MISSING = r'([a-zA-Z])([\-\+\d\.]+)' 15 | 16 | # initialize logger 17 | logger = logging.getLogger(__name__) 18 | 19 | 20 | def simplify(units): 21 | '''Simplify the notation of units 22 | 23 | Parameters 24 | ---------- 25 | units : str 26 | Unit specification 27 | 28 | Returns 29 | ------- 30 | units : str 31 | Simplified unit specification 32 | 33 | See Also 34 | -------- 35 | parse 36 | format 37 | 38 | ''' 39 | 40 | def simplify_group(m): 41 | group = m.groups()[0] 42 | parts = parse(group) 43 | if m.group().startswith('/'): 44 | parts = [(u, -e) for u, e in parts] 45 | if m.groups()[1]: 46 | exp = np.prod([float(e) for e in m.groups()[1].split('^') if e]) 47 | parts = [(u, e * exp) for u, e in parts] 48 | return ' %s' % format(parts) 49 | 50 | 51 | # only continue in case of string or unicode input 52 | if type(units) not in six.string_types + (six.text_type, str): 53 | return units 54 | 55 | # remove spaces around operators (space itself is also a multiplication operator) 56 | units = re.sub(RE_OPERATORS, r'\1', units) 57 | 58 | # encapsulate terms to be treated separately 59 | units = '(%s)' % re.sub(RE_TERMS, r'\1) \2(', units) 60 | 61 | # treat groups seprately 62 | while re.search(RE_GROUPS, units) is not None: 63 | units = re.sub(RE_GROUPS, simplify_group, units) 64 | parts = parse(units) 65 | 66 | # prevent odd units 67 | parts = prevent_odd_units(parts) 68 | 69 | return format(parts).strip() 70 | 71 | 72 | def format(parts, order=['kg','m','s','Hz']): 73 | '''Format unit parts into string 74 | 75 | Parameters 76 | ---------- 77 | parts : list of 2-tuples 78 | List of 2-tuples containing pairs of unit names and exponents 79 | order : list, optional 80 | Preferred order of units in formatted string (default: kg, m, s, Hz) 81 | 82 | Returns 83 | ------- 84 | units : str 85 | Formatted unit specification 86 | 87 | See Also 88 | -------- 89 | parse 90 | 91 | ''' 92 | 93 | # order units 94 | parts = sorted(parts, key=lambda x: order.index(x[0]) 95 | if x[0] in order else np.inf) 96 | 97 | # format individual units 98 | for i, (u, e) in enumerate(parts): 99 | if e == 0: 100 | parts[i] = '' 101 | elif e == 1: 102 | parts[i] = u 103 | elif np.mod(e, 1.) == 0.: 104 | parts[i] = '%s^%d' % (u, e) 105 | else: 106 | parts[i] = '%s^%0.1f' % (u, e) 107 | 108 | # join units 109 | return ' '.join(parts) 110 | 111 | 112 | def parse(units): 113 | '''Parse unit string into parts 114 | 115 | Parameters 116 | ---------- 117 | units : str 118 | Unit specification 119 | 120 | Returns 121 | ------- 122 | parts : list of 2-tuples 123 | List of 2-tuples containing pairs of unit names and exponents 124 | 125 | See Also 126 | -------- 127 | format 128 | 129 | ''' 130 | 131 | # multiple terms not supported, return as is 132 | if re.search(RE_TERMS, units): 133 | return [(units, 1.)] 134 | 135 | # add missing exponents 136 | units = re.sub(RE_EXPONENTS_MISSING, r'\1^\2', units) 137 | 138 | # loop over unique units 139 | parts = [] 140 | for u in set(re.findall(RE_VARIABLE, units)): 141 | 142 | # expand exponents 143 | m = re.findall(RE_EXPONENTS % u, units) 144 | 145 | n = 0. 146 | for prefix, string, exp in m: 147 | 148 | # group exponents 149 | if len(exp) > 0: 150 | exp = np.prod([float(x) for x in re.findall(RE_NUMBER, string)]) 151 | else: 152 | exp = 1. 153 | 154 | if prefix == '/': 155 | # negate exponents 156 | exp = -exp 157 | elif prefix == '^': 158 | # abort when exponent is a variable 159 | return [(units, 1.)] 160 | 161 | n += exp 162 | 163 | parts.append((u, n)) 164 | 165 | return parts 166 | 167 | 168 | def prevent_odd_units(parts): 169 | 170 | # replace per-hertz by seconds 171 | if len(parts) == 1: 172 | p = parts[0] 173 | if p[0].upper() == 'HZ' and p[1] == -1.: 174 | parts = [('s', 1.)] 175 | 176 | # replace degr with deg 177 | for i, p in enumerate(parts): 178 | if p[0].upper() == 'DEGR': 179 | parts[i] = ('deg', p[1]) 180 | 181 | return parts 182 | -------------------------------------------------------------------------------- /oceanwaves/utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def iterable(arr): 5 | '''Returns an iterable''' 6 | 7 | try: 8 | iter(arr) 9 | return arr 10 | except: 11 | return (arr,) 12 | 13 | 14 | def expand_and_repeat(mtx, shape=None, repeat=None, 15 | exist_dims=None, expand_dims=None): 16 | '''Expands matrix and repeats matrix contents along new dimensions 17 | 18 | Provide ``shape`` and ``exist_dims`` or ``expand_dims``, or 19 | ``repeat`` and ``expand_dims``. 20 | 21 | Parameters 22 | ---------- 23 | mtx : numpy.ndarray 24 | Input matrix 25 | shape : tuple, optional 26 | Target shape of output matrix 27 | repeat : tuple or int, optional 28 | Repititions along new dimensions 29 | exist_dims : tuple or int, optional 30 | Indices of dimensions in target shape that are present in input matrix 31 | expand_dims : tuple or int, optional 32 | Indices of dimensions in target shape that are not present in input matrix 33 | 34 | Returns 35 | ------- 36 | numpy.ndarray 37 | Matrix with target shape 38 | 39 | Examples 40 | -------- 41 | >>> expand_and_repeat([[1,2,3],[4,5,6]], shape=(2,3,4), exist_dims=(0,1)) 42 | >>> expand_and_repeat([[1,2,3],[4,5,6]], shape=(2,3,4), expand_dims=(2,)) 43 | >>> expand_and_repeat([[1,2,3],[4,5,6]], shape=(2,3,4), expand_dims=2) 44 | >>> expand_and_repeat([[1,2,3],[4,5,6]], repeat=(4,), expand_dims=(2,)) 45 | >>> expand_and_repeat([[1,2,3],[4,5,6]], repeat=4, expand_dims=2) 46 | 47 | ''' 48 | 49 | mtx = np.asarray(mtx) 50 | 51 | if shape is not None: 52 | shape = iterable(shape) 53 | 54 | if mtx.ndim > len(shape): 55 | raise ValueError('Nothing to expand. Number of matrix ' 56 | 'dimensions (%d) is larger than the ' 57 | 'dimensionality of the target shape ' 58 | '(%d).' % (mtx.ndim, len(shape))) 59 | 60 | if exist_dims is not None: 61 | exist_dims = iterable(exist_dims) 62 | 63 | if len(exist_dims) != len(set(exist_dims)): 64 | raise ValueError('Existing dimensions should be unique.') 65 | 66 | if mtx.ndim != len(exist_dims): 67 | raise ValueError('Number of matrix dimensions (%d) ' 68 | 'should match the number of existing ' 69 | 'dimensions (%d).' % (mtx.ndim, len(exist_dims))) 70 | 71 | expand_dims = [i 72 | for i in range(len(shape)) 73 | if i not in exist_dims] 74 | 75 | elif expand_dims is not None: 76 | expand_dims = iterable(expand_dims) 77 | 78 | if len(expand_dims) != len(set(expand_dims)): 79 | raise ValueError('Expanding dimensions should be unique.') 80 | 81 | if len(shape) - mtx.ndim != len(expand_dims): 82 | raise ValueError('Dimensionality of the target shape ' 83 | 'minus the number of matrix dimensions ' 84 | '(%d) should match the number of expanding ' 85 | 'dimensions (%d).' % (len(shape) - mtx.ndim, len(expand_dims))) 86 | 87 | exist_dims = [i 88 | for i in range(len(shape)) 89 | if i not in expand_dims] 90 | 91 | else: 92 | raise ValueError('Target shape undetermined. Provide ' 93 | '``exist_dims`` or ``expand_dims``.') 94 | 95 | repeat = [n 96 | for i, n in enumerate(shape) 97 | if i in expand_dims] 98 | 99 | for i1, i2 in enumerate(exist_dims): 100 | if shape[i2] != mtx.shape[i1]: 101 | raise ValueError('Current matrix dimension (%d = %d) ' 102 | 'should match target shape (%d = %d).' % (i1, mtx.shape[i1], i2, shape[i2])) 103 | 104 | elif repeat is not None and expand_dims is not None: 105 | repeat = iterable(repeat) 106 | expand_dims = iterable(expand_dims) 107 | 108 | if len(expand_dims) != len(set(expand_dims)): 109 | raise ValueError('Expanding dimensions should be unique.') 110 | 111 | if len(repeat) != len(expand_dims): 112 | raise ValueError('Number of repititions (%d) should ' 113 | 'match the number of expanding ' 114 | 'dimensions (%d).' % (len(repeat), len(expand_dims))) 115 | 116 | else: 117 | raise ValueError('Target shape undetermined. Provide ' 118 | '``shape`` and ``exist_dims`` or ' 119 | '``expand_dims``, or ``repeat`` and ``expand_dims``.') 120 | 121 | for i, n in zip(expand_dims, repeat): 122 | mtx = np.expand_dims(mtx, i).repeat(n, axis=i) 123 | 124 | return mtx 125 | 126 | 127 | def trapz_and_repeat(mtx, x, axis=-1): 128 | 129 | if axis < 0: 130 | axis += len(mtx.shape) 131 | 132 | return expand_and_repeat(np.trapz(mtx, x, axis=axis), 133 | shape=mtx.shape, expand_dims=axis) 134 | -------------------------------------------------------------------------------- /oceanwaves/wavedroid.py: -------------------------------------------------------------------------------- 1 | class WaveDroidReader: 2 | 3 | 4 | def __init__(self): 5 | 6 | pass 7 | 8 | 9 | def __call__(self, fpath): 10 | 11 | raise NotImplementedError('Reading of WaveDroid files is not yet implemented') 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.11.2 2 | scipy>=0.14.0 3 | -e git+https://github.com/pydata/xarray.git#egg=xarray 4 | pyproj>=1.9.3 5 | docopt>=0.6.2 6 | matplotlib>=1.4.2 7 | sphinxcontrib-napoleon>=0.2.8 8 | coverage>=4.3.1 9 | codecov 10 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal=1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='oceanwaves', 5 | version='1.0.0rc6', 6 | author='Bas Hoonhout', 7 | author_email='bas.hoonhout@deltares.nl', 8 | url='http://oceanwaves.readthedocs.io/', 9 | license='MIT', 10 | description='A toolbox for ocean wave datasets', 11 | long_description=open('README.rst').read(), 12 | classifiers=[ 13 | 'Development Status :: 4 - Beta', 14 | 'Environment :: Console', 15 | 'Intended Audience :: Developers', 16 | 'Intended Audience :: Science/Research', 17 | 'License :: OSI Approved :: MIT License', 18 | 'Natural Language :: English', 19 | 'Operating System :: MacOS :: MacOS X', 20 | 'Operating System :: Microsoft :: Windows', 21 | 'Operating System :: POSIX', 22 | 'Operating System :: Unix', 23 | 'Programming Language :: Python :: 2.7', 24 | 'Programming Language :: Python :: 3', 25 | 'Programming Language :: Python :: 3.2', 26 | 'Programming Language :: Python :: 3.3', 27 | 'Programming Language :: Python :: 3.4', 28 | 'Programming Language :: Python :: 3.5', 29 | 'Programming Language :: Python :: 3.6', 30 | 'Programming Language :: Python :: 3.7', 31 | 'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator', 32 | 'Topic :: Scientific/Engineering :: Physics', 33 | 'Topic :: Scientific/Engineering :: Visualization', 34 | ], 35 | keywords=['ocean waves oceanwaves swan waverider wavedroid xarray'], 36 | packages=find_packages(exclude=['docs', 'data', 'notebooks', 'tests', 'cover']), 37 | python_requires='>=2.7, <4', 38 | install_requires=[ 39 | 'docopt', 40 | 'xarray', 41 | 'scipy', 42 | 'numpy', 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /tests/test_datawell.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | from oceanwaves import * 3 | 4 | 5 | @raises(NotImplementedError) 6 | def test_datawell(): 7 | '''Test reading of datawell files''' 8 | ow = OceanWaves.from_datawell('../data/datawell') 9 | -------------------------------------------------------------------------------- /tests/test_netcdf.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | import oceanwaves as ow 3 | 4 | 5 | SPECTRA = [ 6 | 'data/swan/P1.SP1', 7 | 'data/swan/P1.SP2', 8 | 'data/swan/P1b.SP1', 9 | 'data/swan/a11.sp2', 10 | 'data/swan/a11.hot', 11 | 'data/swan/a111uref01.sp2' 12 | ] 13 | 14 | TABLES = [ 15 | 'data/swan/P1.TAB', 16 | 'data/swan/TBL1.tbl', 17 | 'data/swan/TBL2.tbl', 18 | 'data/swan/TBL2NS.tbl', 19 | ] 20 | 21 | 22 | def test_netcdf_spectra(): 23 | '''Test writing of swan spectra to netcdf''' 24 | for s in SPECTRA: 25 | yield readwrite_spectrum, s 26 | 27 | 28 | def test_netcdf_tables(): 29 | '''Test writing of swan tables to netcdf''' 30 | for t in TABLES: 31 | yield readwrite_table, t 32 | 33 | 34 | def readwrite_spectrum(spcfile): 35 | ow_spc = ow.OceanWaves.from_swan(spcfile) 36 | ow_spc.to_netcdf('%s.nc' % spcfile) 37 | 38 | 39 | def readwrite_table(tabfile): 40 | ow_tab = ow.OceanWaves.from_swantable( 41 | tabfile, 42 | columns=['Xp','Yp','Botlev','Hsig', 43 | 'RTpeak','TPsmoo','Tm01','Tm02']) 44 | ow_tab.to_netcdf('%s.nc' % tabfile) 45 | -------------------------------------------------------------------------------- /tests/test_oceanwaves.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | import numpy as np 3 | from datetime import datetime 4 | from oceanwaves import * 5 | 6 | 7 | DIMS = [('time', [datetime(1970,1,1,0), 8 | datetime(1970,1,1,1)]), 9 | ('location', [(0,0), 10 | (1,0), 11 | (0,1), 12 | (.5,.5)]), 13 | ('frequency', [.025, .05, .1, .15, .2, .25, .3, .35, .4, .45, .5]), 14 | ('direction', [-60, -50, -40, -30, -20, -10, 0, 10, 20, 30, 40, 50])] 15 | 16 | PARAMETERIZATIONS = [('Tm01', 3.61435704), 17 | ('Tm02', 3.53313111), 18 | ('Tp', 4.), 19 | ('peak_direction', 0.), 20 | ('peak_period', 4.)] 21 | 22 | ### CHECK SHAPE 23 | 24 | def test_shape_dim1(): 25 | '''Test one-dimensional object initialization''' 26 | for dim, arr in DIMS: 27 | yield check_shape, {dim:arr}, (len(arr),) 28 | 29 | 30 | def test_shape_dim2(): 31 | '''Test two-dimensional object initialization''' 32 | for i, (dim1, arr1) in enumerate(DIMS): 33 | for dim2, arr2 in DIMS[i+1:]: 34 | yield check_shape, {dim1:arr1, dim2:arr2}, (len(arr1), len(arr2)) 35 | 36 | 37 | def test_shape_dim3(): 38 | '''Test three-dimensional object initialization''' 39 | for i, (dim1, arr1) in enumerate(DIMS): 40 | for j, (dim2, arr2) in enumerate(DIMS[i+1:]): 41 | for dim3, arr3 in DIMS[i+j+2:]: 42 | yield check_shape, {dim1:arr1, dim2:arr2, dim3:arr3}, \ 43 | (len(arr1), len(arr2), len(arr3)) 44 | 45 | 46 | def test_shape_dim4(): 47 | '''Test four-dimensional object initialization''' 48 | yield check_shape, dict(DIMS), tuple([len(x[1]) for x in DIMS]) 49 | 50 | 51 | def check_shape(init, shape): 52 | ow = OceanWaves(**init) 53 | assert_equals(ow.shape, shape) 54 | 55 | 56 | ### CHECK ENERGY 57 | 58 | def test_energy_tospectral(): 59 | '''Test conversion from non-spectral to spectral''' 60 | ow = _generate_nonspectral() 61 | ow_spc = ow.as_spectral(frequency=dict(DIMS)['frequency']) 62 | assert_almost_equals(np.sum(np.abs(ow['_energy'].values - 63 | ow_spc.Hm0().values)), 0.) 64 | 65 | 66 | def test_energy_todirectional(): 67 | '''Test conversion from omnidirectional to directional''' 68 | ow = _generate_nonspectral() 69 | ow_dir = ow.as_directional(direction=dict(DIMS)['direction']) 70 | assert_almost_equals(np.sum(np.abs(ow['_energy'].values - 71 | ow_dir.as_omnidirectional()['_energy'].values)), 72 | 0.) 73 | 74 | 75 | def test_energy_todirectionalspectrum(): 76 | '''Test conversion from non-spectral and omnidirectional to directional spectrum''' 77 | ow = _generate_nonspectral() 78 | ow_spc = ow.as_spectral(frequency=dict(DIMS)['frequency']) 79 | ow_dir = ow_spc.as_directional(direction=dict(DIMS)['direction']) 80 | 81 | E1 = ow_dir['_energy'].values 82 | E2 = np.trapz(E1, ow_dir.coords['direction'].values, axis=-1) 83 | E3 = np.trapz(E2, ow_dir.coords['frequency'].values, axis=-1) 84 | 85 | assert_almost_equals(np.sum(np.abs(ow['_energy'].values**2./16. - E3)), 0.) 86 | 87 | 88 | ### CHECK PARAMETERIZATIONS 89 | 90 | def test_params(): 91 | '''Test computation of spectral parameters''' 92 | ow = _generate_nonspectral() 93 | ow_spc = ow.as_spectral(frequency=dict(DIMS)['frequency']) 94 | ow_dir = ow_spc.as_directional(direction=dict(DIMS)['direction']) 95 | for p, v in PARAMETERIZATIONS: 96 | yield check_param, ow_dir, p, v 97 | 98 | 99 | def check_param(ow, p, v): 100 | val = (getattr(ow, p)().values - v).max() 101 | assert_almost_equals(val, 0) 102 | 103 | 104 | ### STANDARD TEST OBJECTS 105 | 106 | def _generate_nonspectral(): 107 | dims = dict(DIMS) 108 | ow = OceanWaves(time=dims['time'], 109 | energy=[1.5] * len(dims['time']), 110 | energy_units='m') 111 | return ow 112 | 113 | 114 | -------------------------------------------------------------------------------- /tests/test_plot.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | import numpy as np 3 | from datetime import datetime 4 | from oceanwaves import * 5 | 6 | 7 | DIMS = [('time', [datetime(1970,1,1,0), 8 | datetime(1970,1,1,1)]), 9 | ('location', [(0,0), 10 | (1,0), 11 | (0,1), 12 | (.5,.5)]), 13 | ('frequency', [.025, .05, .1, .15, .2, .25, .3, .35, .4, .45, .5]), 14 | ('direction', [0, 10, 20, 30, 40, 50, 60, 70, 80, 90])] 15 | 16 | 17 | ### CHECK PLOTTING 18 | 19 | def test_plot_polar(): 20 | '''Test plotting of facetted polar plot''' 21 | ow = OceanWaves(**dict(DIMS)) 22 | ow['_energy'] = [d[0] for d in DIMS], np.random.rand(*ow.shape) 23 | ow.plot(col='time', row='location', 24 | subplot_kws=dict(projection='polar'), sharex=False, sharey=False) 25 | 26 | 27 | def test_plot_map(): 28 | '''Test plotting of mapped polar plot''' 29 | ow = OceanWaves(**dict(DIMS)) 30 | ow['_energy'] = [d[0] for d in DIMS], np.random.rand(*ow.shape) 31 | ow[dict(time=0)].plot.spatial_map(scale=.3, 32 | subplot_kw=dict(projection='polar')) 33 | -------------------------------------------------------------------------------- /tests/test_spectral.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | import numpy as np 3 | from oceanwaves.spectral import * 4 | 5 | FREQ = np.arange(0, 1, .05)[1:] 6 | THETA = np.arange(0, 180, 5) 7 | 8 | 9 | def test_jonswap_1(): 10 | '''Test computation of jonswap spectrum following Yamaguchi''' 11 | E = jonswap(FREQ, Hm0=1., Tp=4., method='yamaguchi', 12 | normalize=True) 13 | assert_almost_equals(np.trapz(E, FREQ), 1/16.) 14 | assert_almost_equals(FREQ[np.argmax(E)], 1/4.) 15 | 16 | 17 | def test_jonswap_2(): 18 | '''Test computation of jonswap spectrum following Goda''' 19 | E = jonswap(FREQ, Hm0=1., Tp=4., method='goda', normalize=True) 20 | assert_almost_equals(np.trapz(E, FREQ), 1/16.) 21 | assert_almost_equals(FREQ[np.argmax(E)], 1/4.) 22 | 23 | 24 | def test_jonswap_with_arrays(): 25 | '''Test computation of jonswap spectrum using arrays as input''' 26 | E = jonswap(FREQ, Hm0=np.array([1.0, 1.0]), Tp=np.array([4.0, 4.0]), 27 | method='goda', normalize=True) 28 | assert_equal(E.ndim, 2) 29 | assert_equal(E.shape[1], 2) 30 | 31 | 32 | @raises(ValueError) 33 | def test_jonswap_exception1(): 34 | '''Test exception if unsupported spectrum method is requested''' 35 | E = jonswap(FREQ, Hm0=1., Tp=4., method='pm', normalize=True) 36 | 37 | 38 | def test_spreading_1(): 39 | '''Test computation of directional spreading in degrees''' 40 | D = directional_spreading(THETA, theta_peak=90., s=20., 41 | units='deg', normalize=True) 42 | assert_almost_equals(np.trapz(D, THETA), 1.) 43 | assert_almost_equals(THETA[np.argmax(D)], 90.) 44 | 45 | 46 | def test_spreading_2(): 47 | '''Test computation of directional spreading in radians''' 48 | D = directional_spreading(np.radians(THETA), 49 | theta_peak=np.radians(90.), s=20., 50 | units='rad', normalize=True) 51 | assert_almost_equals(np.trapz(D, np.radians(THETA)), 1.) 52 | assert_almost_equals(THETA[np.argmax(D)], 90.) 53 | 54 | 55 | @raises(ValueError) 56 | def test_spreading_exception1(): 57 | '''Test exception if unsupported units are used''' 58 | D = directional_spreading(THETA, theta_peak=90., s=20., 59 | units='gon', normalize=True) 60 | -------------------------------------------------------------------------------- /tests/test_swan.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | import numpy as np 3 | import oceanwaves as ow 4 | 5 | 6 | def test_swan_1(): 7 | '''Test reading of one-dimensional swan spectrum''' 8 | ow_sp1 = ow.OceanWaves.from_swan('data/swan/P1.SP1') 9 | assert_equal(ow_sp1.shape, (4,47)) 10 | assert_almost_equal(np.nansum(ow_sp1.energy.values), 22.8, 1) 11 | 12 | 13 | def test_swan_2(): 14 | '''Test reading of two-dimensional swan spectrum''' 15 | ow_sp2 = ow.OceanWaves.from_swan('data/swan/P1.SP2') 16 | assert_equal(ow_sp2.shape, (4,47,42)) 17 | assert_almost_equal(np.nansum(ow_sp2.energy.values), 2.7, 1) 18 | 19 | 20 | def test_swan_3(): 21 | '''Test reading of swan table''' 22 | ow_tab = ow.OceanWaves.from_swantable('data/swan/P1.TAB') 23 | assert_equal(ow_tab.shape, (35,)) 24 | assert_almost_equal(np.nansum(ow_tab.Hsig.values), 53.9, 1) 25 | 26 | 27 | def test_swan_4(): 28 | '''Test reading of swan table without header''' 29 | ow_tab = ow.OceanWaves.from_swantable('data/swan/TBL1.tbl', 30 | columns=['Xp','Yp','Botlev','Hsig', 31 | 'RTpeak','TPsmoo','Tm01','Tm02']) 32 | assert_equal(ow_tab.shape, (8,)) 33 | assert_almost_equal(np.nansum(ow_tab.Hsig.values), 15.0, 1) 34 | 35 | 36 | def test_swan_5(): 37 | '''Test reading of swan table''' 38 | ow_tab = ow.OceanWaves.from_swantable('data/swan/TBL2.tbl') 39 | assert_equal(ow_tab.shape, (8,)) 40 | assert_almost_equal(np.nansum(ow_tab.Hsig.values), 15.0, 1) 41 | 42 | 43 | def test_swan_6(): 44 | '''Test reading of instationary swan table''' 45 | ow_tab = ow.OceanWaves.from_swantable('data/swan/TBL2NS.tbl') 46 | assert_equal(ow_tab.shape, (7,8)) 47 | assert_almost_equal(np.nansum(ow_tab.Hsig.values), 70.6, 1) 48 | 49 | 50 | def test_swan_7(): 51 | '''Test writing of one-dimensional swan spectrum''' 52 | fname1 = 'data/swan/P1.SP1' 53 | fname2 = 'data/swan/P1.SP1.copy' 54 | ow_sp1_1 = ow.OceanWaves.from_swan(fname1) 55 | ow_sp1_1.to_swan(fname2) 56 | ow_sp1_2 = ow.OceanWaves.from_swan(fname2) 57 | assert_almost_equals(np.sum(ow_sp1_1['_energy'].values - 58 | ow_sp1_2['_energy'].values), 0.) 59 | 60 | 61 | def test_swan_8(): 62 | '''Test writing of two-dimensional swan spectrum''' 63 | fname1 = 'data/swan/P1.SP2' 64 | fname2 = 'data/swan/P1.SP2.copy' 65 | ow_sp2_1 = ow.OceanWaves.from_swan(fname1) 66 | ow_sp2_1.to_swan(fname2) 67 | ow_sp2_2 = ow.OceanWaves.from_swan(fname2) 68 | assert_almost_equals(np.sum(ow_sp2_1['_energy'].values - 69 | ow_sp2_2['_energy'].values), 0., places=3) 70 | -------------------------------------------------------------------------------- /tests/test_units.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | import numpy as np 3 | from datetime import datetime 4 | from oceanwaves import * 5 | 6 | 7 | DIMS = [('time', [datetime(1970,1,1,0), 8 | datetime(1970,1,1,1)]), 9 | ('location', [(0,0), 10 | (1,0), 11 | (0,1), 12 | (.5,.5)]), 13 | ('frequency', [.025, .05, .1, .15, .2, .25, .3, .35, .4, .45, .5]), 14 | ('direction', [0, 10, 20, 30, 40, 50, 60, 70, 80, 90])] 15 | 16 | CONVERSIONS = [('m^2 / m', 'm'), 17 | ('m ^ 2 / m', 'm'), # test spaces 18 | ('m2 / m', 'm'), # test missing exponent 19 | ('m^3 / (m * m)', 'm'), # test groups 20 | ('m^4 / (m * (m / m^-1))', 'm'), # test nested groups 21 | ('m^5 / (m * m)^2', 'm'), # test group exponents 22 | ('m^5 / (m * m)^2 + NAP', 'm + NAP'), # test terms 23 | ('(kg * m^2 * s^2)^2 / (kg^2 * m^3 * s^4)', 'm'), # test multiple units 24 | ('(m^2 / Hz) * m^-2', 's'), # test herz to seconds conversion 25 | ('(m^2 * degr) * m^-2', 'deg'), # test degr to deg conversion 26 | ('m^2 * m^0.5', 'm^2.5'), # test partial exponents 27 | ('m^0', ''), # test zero exponents 28 | ('m^a * m^2', 'm^a*m^2'), # test variable exponents 29 | (1, 1)] # test non-string input 30 | 31 | 32 | ### CHECK UNITS 33 | 34 | def test_units_tospectral(): 35 | '''Test unit conversion when converting to spectrum''' 36 | ow = _generate_nonspectral() 37 | ow_spc = ow.as_spectral(frequency=dict(DIMS)['frequency']) 38 | assert_equals(ow_spc.energy.units, 'm^2 Hz^-1') 39 | 40 | 41 | def test_units_todirectional(): 42 | '''Test unit conversion when converting to directional spectrum''' 43 | ow = _generate_nonspectral() 44 | ow_spc = ow.as_spectral(frequency=dict(DIMS)['frequency']) 45 | ow_dir = ow_spc.as_directional(direction=dict(DIMS)['direction']) 46 | assert_equals(ow_dir.energy.units, 'm^2 Hz^-1 deg^-1') 47 | 48 | 49 | # CHECK RAW CONVERSIONS 50 | 51 | def test_units_conversions(): 52 | '''Test theoretical unit conversions''' 53 | for u1, u2 in CONVERSIONS: 54 | yield check_units, u1, u2 55 | 56 | 57 | def check_units(u1, u2): 58 | assert_equals(units.simplify(u1), u2) 59 | 60 | 61 | ### STANDARD TEST OBJECTS 62 | 63 | def _generate_nonspectral(): 64 | dims = dict(DIMS) 65 | ow = OceanWaves(time=dims['time'], 66 | energy=[1.] * len(dims['time']), 67 | energy_units='m') 68 | return ow 69 | 70 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | from oceanwaves.utils import * 3 | 4 | 5 | ITERABLES = [(1, 1), 6 | (1., 1), 7 | ('a', 1), 8 | ('abc', 3), 9 | ([1,2,3], 3), 10 | ((1,2,3), 3), 11 | ({1:1,2:2,3:3}, 3)] # iterable, length 12 | 13 | INTEGRATIONS = [(0, 9), 14 | (1, 18), 15 | (-1, 18), 16 | (-2, 9)] # axis, sum 17 | 18 | MTX = np.asarray([[1,1,1], 19 | [2,2,2]]) 20 | 21 | ### CHECK ITERABLE CONVERSION 22 | 23 | def test_iterables(): 24 | '''Test if any variable is properly transformed into an interable''' 25 | for a, l in ITERABLES: 26 | yield check_iterable, a, l 27 | 28 | 29 | def check_iterable(a, l): 30 | 31 | # check if iterable 32 | iter(iterable(a)) 33 | 34 | # check if length is correct 35 | assert_equals(len(iterable(a)), l) 36 | 37 | 38 | ### CHECK MATRIX EXPANSION 39 | 40 | def test_expansion_1(): 41 | '''Test matrix expansion when no expansion is needed''' 42 | mtx = expand_and_repeat(MTX, shape=(2,3), exist_dims=(0,1)) 43 | assert_equal(mtx.shape, MTX.shape) 44 | assert_equal(mtx.sum(), MTX.sum()) 45 | 46 | 47 | def test_expansion_2(): 48 | '''Test matrix expansion at end of matrix''' 49 | mtx = expand_and_repeat(MTX, shape=(2,3,4), exist_dims=(0,1)) 50 | assert_equal(mtx.shape, MTX.shape + (4,)) 51 | assert_equal(mtx.sum(), 4 * MTX.sum()) 52 | 53 | 54 | def test_expansion_3(): 55 | '''Test matrix expansion in the middle of matrix''' 56 | mtx = expand_and_repeat(MTX, shape=(2,4,3), exist_dims=(0,2)) 57 | assert_equal(mtx.shape, (2,4,3)) 58 | assert_equal(mtx.sum(), 4 * MTX.sum()) 59 | 60 | 61 | def test_expansion_4(): 62 | '''Test matrix expansion at the start of matrix''' 63 | mtx = expand_and_repeat(MTX, shape=(4,2,3), exist_dims=(1,2)) 64 | assert_equal(mtx.shape, (4,2,3)) 65 | assert_equal(mtx.sum(), 4 * MTX.sum()) 66 | 67 | 68 | def test_expansion_5(): 69 | '''Test matrix expansion of multiple dimensions at once''' 70 | mtx = expand_and_repeat(MTX, shape=(1,2,3,4), exist_dims=(1,2)) 71 | assert_equal(mtx.shape, (1,2,3,4)) 72 | assert_equal(mtx.sum(), 4 * MTX.sum()) 73 | 74 | 75 | def test_expansion_6(): 76 | '''Test matrix expansion of multiple dimensions using `expand_dims` construct''' 77 | mtx = expand_and_repeat(MTX, shape=(1,2,3,4), expand_dims=(0,3)) 78 | assert_equal(mtx.shape, (1,2,3,4)) 79 | assert_equal(mtx.sum(), 4 * MTX.sum()) 80 | 81 | 82 | def test_expansion_7(): 83 | '''Test matrix expansion of multiple dimensions using `repeat` construct''' 84 | mtx = expand_and_repeat(MTX, repeat=(1,4), expand_dims=(0,3)) 85 | assert_equal(mtx.shape, (1,2,3,4)) 86 | assert_equal(mtx.sum(), 4 * MTX.sum()) 87 | 88 | 89 | @raises(ValueError) 90 | def test_expansion_exception1(): 91 | '''Test exception if output shape is smaller than input shape''' 92 | mtx = expand_and_repeat(MTX, shape=(2,), expand_dims=()) 93 | 94 | 95 | @raises(ValueError) 96 | def test_expansion_exception2(): 97 | '''Test exception if existing dimensions are not unique''' 98 | mtx = expand_and_repeat(MTX, shape=(2,3,4), exist_dims=(0,0)) 99 | 100 | 101 | @raises(ValueError) 102 | def test_expansion_exception3(): 103 | '''Test exception if existing dimensions exceed current matrix dimensions''' 104 | mtx = expand_and_repeat(MTX, shape=(2,3,4), exist_dims=(0,1,2)) 105 | 106 | 107 | @raises(ValueError) 108 | def test_expansion_exception4(): 109 | '''Test exception if expanding dimensions are not unique''' 110 | mtx = expand_and_repeat(MTX, shape=(2,3,4), expand_dims=(2,2)) 111 | 112 | 113 | @raises(ValueError) 114 | def test_expansion_exception5(): 115 | '''Test exception if expanding dimensions exceed new dimensions in traget shape''' 116 | mtx = expand_and_repeat(MTX, shape=(2,3,4), expand_dims=(1,2)) 117 | 118 | 119 | @raises(ValueError) 120 | def test_expansion_exception6(): 121 | '''Test exception if `exist_dims` not `expand_dims` are given''' 122 | mtx = expand_and_repeat(MTX, shape=(2,3,4)) 123 | 124 | 125 | @raises(ValueError) 126 | def test_expansion_exception7(): 127 | '''Test exception if target shape dimensions do not match current matrix dimensions''' 128 | mtx = expand_and_repeat(MTX, shape=(2,3,4), exist_dims=(0,2)) 129 | 130 | 131 | @raises(ValueError) 132 | def test_expansion_exception8(): 133 | '''Test exception if expanding dimensions are not unique''' 134 | mtx = expand_and_repeat(MTX, repeat=4, expand_dims=(2,2)) 135 | 136 | 137 | @raises(ValueError) 138 | def test_expansion_exception9(): 139 | '''Test exception when number of expanding dimensions do not match number of repititions''' 140 | mtx = expand_and_repeat(MTX, repeat=4, expand_dims=(1,2)) 141 | 142 | 143 | @raises(ValueError) 144 | def test_expansion_exception10(): 145 | '''Test exception if `shape` not `repeat` is given''' 146 | mtx = expand_and_repeat(MTX, expand_dims=2) 147 | 148 | 149 | ### CHECK DIMENSION CONSERVING INTEGRATION 150 | 151 | def test_integrations(): 152 | '''Test integration with conservation of dimensions''' 153 | for i, s in INTEGRATIONS: 154 | yield check_integration, i, s 155 | 156 | 157 | def check_integration(i, s): 158 | x = range(MTX.shape[i]) 159 | mtx = trapz_and_repeat(MTX, x, axis=i) 160 | assert_equals(mtx.sum(), s) 161 | assert_equals(mtx.shape, MTX.shape) 162 | -------------------------------------------------------------------------------- /tests/test_wavedroid.py: -------------------------------------------------------------------------------- 1 | from nose.tools import * 2 | from oceanwaves import * 3 | 4 | 5 | @raises(NotImplementedError) 6 | def test_wavedroid(): 7 | ow = OceanWaves.from_wavedroid('../data/wavedroid') 8 | -------------------------------------------------------------------------------- /upload_to_pypi: -------------------------------------------------------------------------------- 1 | python setup.py bdist_wheel 2 | twine upload dist/* --------------------------------------------------------------------------------