├── .gitignore ├── LICENSE.md ├── README.md ├── data ├── arf.csv └── src.arf ├── draft ├── Playing with Frames.ipynb └── fits II.ipynb ├── finished ├── README.md ├── a FITSfull of ARF.ipynb ├── angular diameter distance.ipynb ├── k corrections with a dimensional twist.ipynb └── this time with units.ipynb ├── html ├── README.md ├── a FITSfull of ARF.html ├── angular diameter distance.html ├── k corrections with a dimensional twist.html └── this time with units.html └── notebooks ├── README.md ├── a FITSfull of ARF.ipynb ├── angular diameter distance.ipynb ├── k corrections with a dimensional twist.ipynb └── this time with units.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Astronomy and Haskell 3 | 4 | Author: 5 | 6 | - [Doug Burke](https://plus.google.com/+DougBurke) 7 | - [@doug_burke](https://twitter.com/doug_burke) 8 | 9 | A collection of 10 | [IHaskell notebooks](http://gibiansky.github.io/IHaskell/) 11 | showing examples of using 12 | [Haskell](https://www.haskell.org/) 13 | for Astronomy. 14 | 15 | The notebooks are placed in the public domain, and come with 16 | no warranty. 17 | 18 | ## Posts (reverse chronological order) 19 | 20 | 4. [a FITSfull of ARF](http://htmlpreview.github.io/?https://raw.githubusercontent.com/DougBurke/astro-haskell/master/html/a%20FITSfull%20of%20ARF.html) 21 | 22 | Notebooks: [read using nbviewer](http://nbviewer.ipython.org/github/DougBurke/astro-haskell/blob/master/finished/a%20FITSfull%20of%20ARF.ipynb); [on GitHub](https://github.com/DougBurke/astro-haskell/blob/master/notebooks/a%20FITSfull%20of%20ARF.ipynb) 23 | 24 | Published: 5 April 2015 25 | 26 | Synopsis: What does it take to parse data from a common Astronomy file format (`FITS`), and what 27 | does an ARF look like? There's no fun with dimensional quantities here (that's left for an 28 | unspecified notebook in the future), but there's plenty of mathematical abstraction, and 29 | even some envy at optical Astronomers and their large mirrors. 30 | 31 | 3. [K corrections with a dimensional twist](http://htmlpreview.github.io/?https://raw.githubusercontent.com/DougBurke/astro-haskell/master/html/k%20corrections%20with%20a%20dimensional%20twist.html) 32 | 33 | Notebooks: [read using nbviewer](http://nbviewer.ipython.org/github/DougBurke/astro-haskell/blob/master/finished/k%20corrections%20with%20a%20dimensional%20twist.ipynb); [on GitHub](https://github.com/DougBurke/astro-haskell/blob/master/notebooks/k%20corrections%20with%20a%20dimensional%20twist.ipynb) 34 | 35 | Published: 3 March 2015 36 | 37 | Synopsis: Have you ever wanted to calculate a [K 38 | correction](http://en.wikipedia.org/wiki/K_correction)? If so, 39 | then this one's for you. In the course of this notebook I also play 40 | around with representing spectral models and do a bit more work 41 | with the `dimensional-tf` package. 42 | 43 | 2. [This time with units](http://htmlpreview.github.io/?https://raw.githubusercontent.com/DougBurke/astro-haskell/master/html/this%20time%20with%20units.html) 44 | 45 | Notebooks: [read using nbviewer](http://nbviewer.ipython.org/github/DougBurke/astro-haskell/blob/master/finished/this%20time%20with%20units.ipynb); [on GitHub](https://github.com/DougBurke/astro-haskell/blob/master/notebooks/this%20time%20with%20units.ipynb) 46 | 47 | Published: 20 February 2015 48 | 49 | Synopsis: I've moved my IHaskell to ghc version 7.8.4 so can 50 | repeat the previous notebook using `units` instead of 51 | `dimensional-tf`. 52 | 53 | 1. [Angular Diameter Distance](http://htmlpreview.github.io/?https://raw.githubusercontent.com/DougBurke/astro-haskell/master/html/angular%20diameter%20distance.html) 54 | 55 | Notebooks: [read using nbviewer](http://nbviewer.ipython.org/github/DougBurke/astro-haskell/blob/master/finished/angular%20diameter%20distance.ipynb); [on GitHub](https://github.com/DougBurke/astro-haskell/blob/master/notebooks/angular%20diameter%20distance.ipynb) 56 | 57 | Published: 15 February 2015 58 | 59 | Synopsis: I wanted to try out the new 60 | [units](https://hackage.haskell.org/package/units) library, but 61 | it doesn't compile with ghc version 7.6.3, so I decided to try 62 | [dimensional-tf](https://hackage.haskell.org/package/dimensional-tf) 63 | instead. There's some simple Astronomical calculations, plots, 64 | and use of units. 65 | 66 | ## Comments 67 | 68 | Please use the 69 | [issues page](https://github.com/DougBurke/astro-haskell/issues) 70 | if you have questions or see a problem, and 71 | [pull requests](https://github.com/DougBurke/astro-haskell/pulls) 72 | are welcome. 73 | 74 | -------------------------------------------------------------------------------- /data/arf.csv: -------------------------------------------------------------------------------- 1 | ENERG_LO,ENERG_HI,SPECRESP 2 | 0.3000000, 0.3100000, 4.874343 3 | 0.3100000, 0.3200000, 14.82926 4 | 0.3200000, 0.3300000, 21.30229 5 | 0.3300000, 0.3400000, 28.51495 6 | 0.3400000, 0.3500000, 35.39883 7 | 0.3500000, 0.3600000, 41.54232 8 | 0.3600000, 0.3700000, 46.88145 9 | 0.3700000, 0.3800000, 53.06949 10 | 0.3800000, 0.3900000, 61.05444 11 | 0.3900000, 0.4000000, 69.30855 12 | 0.4000000, 0.4100000, 72.90137 13 | 0.4100000, 0.4200000, 79.87247 14 | 0.4200000, 0.4300000, 94.29903 15 | 0.4300000, 0.4400000, 106.0706 16 | 0.4400000, 0.4500000, 116.0293 17 | 0.4500000, 0.4600000, 127.8437 18 | 0.4600000, 0.4700000, 138.8801 19 | 0.4700000, 0.4800000, 150.0978 20 | 0.4800000, 0.4900000, 160.8329 21 | 0.4900000, 0.5000000, 171.5432 22 | 0.5000000, 0.5100000, 183.7123 23 | 0.5100000, 0.5200000, 196.1964 24 | 0.5200000, 0.5300000, 208.9698 25 | 0.5300000, 0.5400000, 171.4215 26 | 0.5400000, 0.5500000, 137.7340 27 | 0.5500000, 0.5600000, 161.4068 28 | 0.5600000, 0.5700000, 173.2176 29 | 0.5700000, 0.5800000, 192.7590 30 | 0.5800000, 0.5900000, 204.1549 31 | 0.5900000, 0.6000000, 214.9084 32 | 0.6000000, 0.6100000, 226.1857 33 | 0.6100000, 0.6200000, 236.2147 34 | 0.6200000, 0.6300000, 247.0838 35 | 0.6300000, 0.6400000, 258.5528 36 | 0.6400000, 0.6500000, 268.7657 37 | 0.6500000, 0.6600000, 279.4300 38 | 0.6600000, 0.6700000, 289.7876 39 | 0.6700000, 0.6800000, 300.6782 40 | 0.6800000, 0.6900000, 310.4438 41 | 0.6900000, 0.7000000, 253.8346 42 | 0.7000000, 0.7100000, 293.0952 43 | 0.7100000, 0.7200000, 306.0932 44 | 0.7200000, 0.7300000, 312.0426 45 | 0.7300000, 0.7400000, 322.7659 46 | 0.7400000, 0.7500000, 334.2620 47 | 0.7500000, 0.7600000, 345.7097 48 | 0.7600000, 0.7700000, 356.8057 49 | 0.7700000, 0.7800000, 367.5352 50 | 0.7800000, 0.7900000, 378.3373 51 | 0.7900000, 0.8000000, 388.6603 52 | 0.8000000, 0.8100000, 398.3632 53 | 0.8100000, 0.8200000, 407.5447 54 | 0.8200000, 0.8300000, 416.2292 55 | 0.8300000, 0.8400000, 424.5197 56 | 0.8400000, 0.8500000, 432.9290 57 | 0.8500000, 0.8600000, 440.3956 58 | 0.8600000, 0.8700000, 448.1352 59 | 0.8700000, 0.8800000, 455.3904 60 | 0.8800000, 0.8900000, 462.3498 61 | 0.8900000, 0.9000000, 468.9738 62 | 0.9000000, 0.9100000, 475.1421 63 | 0.9100000, 0.9200000, 480.6033 64 | 0.9200000, 0.9300000, 485.9647 65 | 0.9300000, 0.9400000, 491.1890 66 | 0.9400000, 0.9500000, 496.7201 67 | 0.9500000, 0.9600000, 502.1527 68 | 0.9600000, 0.9700000, 507.5832 69 | 0.9700000, 0.9800000, 513.0563 70 | 0.9800000, 0.9900000, 517.9399 71 | 0.9900000, 1.0, 522.6050 72 | 1.0, 1.010000, 527.2045 73 | 1.010000, 1.020000, 532.1409 74 | 1.020000, 1.030000, 537.1164 75 | 1.030000, 1.040000, 542.1820 76 | 1.040000, 1.050000, 547.0861 77 | 1.050000, 1.060000, 551.7854 78 | 1.060000, 1.070000, 556.1451 79 | 1.070000, 1.080000, 560.1052 80 | 1.080000, 1.090000, 563.9225 81 | 1.090000, 1.100000, 567.6266 82 | 1.100000, 1.110000, 571.6616 83 | 1.110000, 1.120000, 576.0004 84 | 1.120000, 1.130000, 580.1714 85 | 1.130000, 1.140000, 584.0022 86 | 1.140000, 1.150000, 587.8013 87 | 1.150000, 1.160000, 591.6940 88 | 1.160000, 1.170000, 595.1791 89 | 1.170000, 1.180000, 598.2278 90 | 1.180000, 1.190000, 601.2171 91 | 1.190000, 1.200000, 604.2618 92 | 1.200000, 1.210000, 607.2027 93 | 1.210000, 1.220000, 610.0424 94 | 1.220000, 1.230000, 612.9407 95 | 1.230000, 1.240000, 615.8650 96 | 1.240000, 1.250000, 618.7266 97 | 1.250000, 1.260000, 621.2061 98 | 1.260000, 1.270000, 623.6597 99 | 1.270000, 1.280000, 626.1587 100 | 1.280000, 1.290000, 628.5332 101 | 1.290000, 1.300000, 630.8076 102 | 1.300000, 1.310000, 632.5857 103 | 1.310000, 1.320000, 634.6429 104 | 1.320000, 1.330000, 636.8454 105 | 1.330000, 1.340000, 639.0624 106 | 1.340000, 1.350000, 641.2289 107 | 1.350000, 1.360000, 643.3621 108 | 1.360000, 1.370000, 645.5895 109 | 1.370000, 1.380000, 647.9391 110 | 1.380000, 1.390000, 650.0815 111 | 1.390000, 1.400000, 651.9780 112 | 1.400000, 1.410000, 653.6278 113 | 1.410000, 1.420000, 655.2343 114 | 1.420000, 1.430000, 656.8010 115 | 1.430000, 1.440000, 658.4128 116 | 1.440000, 1.450000, 659.9204 117 | 1.450000, 1.460000, 661.3051 118 | 1.460000, 1.470000, 662.6441 119 | 1.470000, 1.480000, 663.9455 120 | 1.480000, 1.490000, 665.3024 121 | 1.490000, 1.500000, 666.7132 122 | 1.500000, 1.510000, 667.8953 123 | 1.510000, 1.520000, 668.8191 124 | 1.520000, 1.530000, 670.0825 125 | 1.530000, 1.540000, 670.9475 126 | 1.540000, 1.550000, 672.1996 127 | 1.550000, 1.560000, 621.2475 128 | 1.560000, 1.570000, 562.6740 129 | 1.570000, 1.580000, 586.1133 130 | 1.580000, 1.590000, 599.2726 131 | 1.590000, 1.600000, 589.0936 132 | 1.600000, 1.610000, 593.7365 133 | 1.610000, 1.620000, 605.2255 134 | 1.620000, 1.630000, 601.9359 135 | 1.630000, 1.640000, 597.9885 136 | 1.640000, 1.650000, 600.7802 137 | 1.650000, 1.660000, 605.8731 138 | 1.660000, 1.670000, 608.3446 139 | 1.670000, 1.680000, 606.1815 140 | 1.680000, 1.690000, 605.2510 141 | 1.690000, 1.700000, 605.6105 142 | 1.700000, 1.710000, 607.2237 143 | 1.710000, 1.720000, 608.7419 144 | 1.720000, 1.730000, 609.2892 145 | 1.730000, 1.740000, 609.1394 146 | 1.740000, 1.750000, 608.6678 147 | 1.750000, 1.760000, 608.2353 148 | 1.760000, 1.770000, 609.4308 149 | 1.770000, 1.780000, 610.1130 150 | 1.780000, 1.790000, 610.9043 151 | 1.790000, 1.800000, 608.2599 152 | 1.800000, 1.810000, 606.1400 153 | 1.810000, 1.820000, 607.8388 154 | 1.820000, 1.830000, 603.8621 155 | 1.830000, 1.840000, 615.7025 156 | 1.840000, 1.850000, 601.3148 157 | 1.850000, 1.860000, 598.3405 158 | 1.860000, 1.870000, 600.0519 159 | 1.870000, 1.880000, 605.4395 160 | 1.880000, 1.890000, 608.0845 161 | 1.890000, 1.900000, 609.6236 162 | 1.900000, 1.910000, 610.7749 163 | 1.910000, 1.920000, 611.8636 164 | 1.920000, 1.930000, 612.9247 165 | 1.930000, 1.940000, 613.9611 166 | 1.940000, 1.950000, 614.5626 167 | 1.950000, 1.960000, 615.0671 168 | 1.960000, 1.970000, 614.2662 169 | 1.970000, 1.980000, 612.8874 170 | 1.980000, 1.990000, 610.3598 171 | 1.990000, 2.0, 606.4956 172 | 2.0, 2.010000, 600.9745 173 | 2.010000, 2.020000, 593.6163 174 | 2.020000, 2.030000, 584.2422 175 | 2.030000, 2.040000, 574.8760 176 | 2.040000, 2.050000, 556.0223 177 | 2.050000, 2.060000, 506.1811 178 | 2.060000, 2.070000, 448.9615 179 | 2.070000, 2.080000, 373.1582 180 | 2.080000, 2.090000, 343.6687 181 | 2.090000, 2.100000, 352.3013 182 | 2.100000, 2.110000, 356.2896 183 | 2.110000, 2.120000, 355.7609 184 | 2.120000, 2.130000, 340.3635 185 | 2.130000, 2.140000, 326.3962 186 | 2.140000, 2.150000, 315.1341 187 | 2.150000, 2.160000, 297.6422 188 | 2.160000, 2.170000, 304.0565 189 | 2.170000, 2.180000, 316.2159 190 | 2.180000, 2.190000, 317.4332 191 | 2.190000, 2.200000, 324.9800 192 | 2.200000, 2.210000, 330.6914 193 | 2.210000, 2.220000, 340.6989 194 | 2.220000, 2.230000, 346.0855 195 | 2.230000, 2.240000, 350.5702 196 | 2.240000, 2.250000, 352.4359 197 | 2.250000, 2.260000, 350.1648 198 | 2.260000, 2.270000, 353.3753 199 | 2.270000, 2.280000, 361.6607 200 | 2.280000, 2.290000, 367.6436 201 | 2.290000, 2.300000, 371.5120 202 | 2.300000, 2.310000, 371.9744 203 | 2.310000, 2.320000, 372.8828 204 | 2.320000, 2.330000, 371.1766 205 | 2.330000, 2.340000, 374.9475 206 | 2.340000, 2.350000, 379.9763 207 | 2.350000, 2.360000, 382.9757 208 | 2.360000, 2.370000, 387.3544 209 | 2.370000, 2.380000, 389.1618 210 | 2.380000, 2.390000, 388.6519 211 | 2.390000, 2.400000, 387.5881 212 | 2.400000, 2.410000, 388.5547 213 | 2.410000, 2.420000, 391.6821 214 | 2.420000, 2.430000, 394.0021 215 | 2.430000, 2.440000, 395.2317 216 | 2.440000, 2.450000, 396.2213 217 | 2.450000, 2.460000, 397.1639 218 | 2.460000, 2.470000, 396.6643 219 | 2.470000, 2.480000, 395.0365 220 | 2.480000, 2.490000, 393.6865 221 | 2.490000, 2.500000, 392.3551 222 | 2.500000, 2.510000, 391.5037 223 | 2.510000, 2.520000, 390.7799 224 | 2.520000, 2.530000, 388.1872 225 | 2.530000, 2.540000, 381.1514 226 | 2.540000, 2.550000, 357.2281 227 | 2.550000, 2.560000, 354.4886 228 | 2.560000, 2.570000, 369.0703 229 | 2.570000, 2.580000, 373.9236 230 | 2.580000, 2.590000, 376.4983 231 | 2.590000, 2.600000, 380.4721 232 | 2.600000, 2.610000, 381.6529 233 | 2.610000, 2.620000, 385.2249 234 | 2.620000, 2.630000, 387.6914 235 | 2.630000, 2.640000, 390.0440 236 | 2.640000, 2.650000, 390.8175 237 | 2.650000, 2.660000, 391.3342 238 | 2.660000, 2.670000, 392.2371 239 | 2.670000, 2.680000, 393.1525 240 | 2.680000, 2.690000, 394.5889 241 | 2.690000, 2.700000, 396.4089 242 | 2.700000, 2.710000, 396.9285 243 | 2.710000, 2.720000, 397.5380 244 | 2.720000, 2.730000, 398.6623 245 | 2.730000, 2.740000, 399.8392 246 | 2.740000, 2.750000, 400.7541 247 | 2.750000, 2.760000, 401.8992 248 | 2.760000, 2.770000, 403.1629 249 | 2.770000, 2.780000, 403.9364 250 | 2.780000, 2.790000, 404.2933 251 | 2.790000, 2.800000, 404.3622 252 | 2.800000, 2.810000, 404.3147 253 | 2.810000, 2.820000, 404.4481 254 | 2.820000, 2.830000, 404.9390 255 | 2.830000, 2.840000, 405.8460 256 | 2.840000, 2.850000, 406.7667 257 | 2.850000, 2.860000, 407.3813 258 | 2.860000, 2.870000, 407.4402 259 | 2.870000, 2.880000, 406.6824 260 | 2.880000, 2.890000, 405.0479 261 | 2.890000, 2.900000, 401.9485 262 | 2.900000, 2.910000, 394.7418 263 | 2.910000, 2.920000, 396.0839 264 | 2.920000, 2.930000, 399.7371 265 | 2.930000, 2.940000, 401.4314 266 | 2.940000, 2.950000, 402.5521 267 | 2.950000, 2.960000, 403.4329 268 | 2.960000, 2.970000, 403.8823 269 | 2.970000, 2.980000, 404.7239 270 | 2.980000, 2.990000, 405.9449 271 | 2.990000, 3.0, 407.1387 272 | 3.0, 3.010000, 407.8945 273 | 3.010000, 3.020000, 408.6049 274 | 3.020000, 3.030000, 409.3812 275 | 3.030000, 3.040000, 409.8532 276 | 3.040000, 3.050000, 410.2964 277 | 3.050000, 3.060000, 410.8039 278 | 3.060000, 3.070000, 411.1183 279 | 3.070000, 3.080000, 411.3220 280 | 3.080000, 3.090000, 411.5599 281 | 3.090000, 3.100000, 411.9138 282 | 3.100000, 3.110000, 412.3225 283 | 3.110000, 3.120000, 412.7808 284 | 3.120000, 3.130000, 413.0927 285 | 3.130000, 3.140000, 413.0010 286 | 3.140000, 3.150000, 412.5950 287 | 3.150000, 3.160000, 411.6373 288 | 3.160000, 3.170000, 409.7127 289 | 3.170000, 3.180000, 406.6531 290 | 3.180000, 3.190000, 404.2698 291 | 3.190000, 3.200000, 406.2932 292 | 3.200000, 3.210000, 407.7866 293 | 3.210000, 3.220000, 408.6494 294 | 3.220000, 3.230000, 410.5483 295 | 3.230000, 3.240000, 411.4446 296 | 3.240000, 3.250000, 411.8214 297 | 3.250000, 3.260000, 412.5706 298 | 3.260000, 3.270000, 413.3526 299 | 3.270000, 3.280000, 414.0936 300 | 3.280000, 3.290000, 414.6225 301 | 3.290000, 3.300000, 415.1515 302 | 3.300000, 3.310000, 415.4583 303 | 3.310000, 3.320000, 415.5415 304 | 3.320000, 3.330000, 415.6241 305 | 3.330000, 3.340000, 416.2448 306 | 3.340000, 3.350000, 416.8657 307 | 3.350000, 3.360000, 417.5220 308 | 3.360000, 3.370000, 418.2138 309 | 3.370000, 3.380000, 418.9060 310 | 3.380000, 3.390000, 419.0497 311 | 3.390000, 3.400000, 419.1936 312 | 3.400000, 3.410000, 419.5031 313 | 3.410000, 3.420000, 419.9786 314 | 3.420000, 3.430000, 420.4540 315 | 3.430000, 3.440000, 420.4157 316 | 3.440000, 3.450000, 420.3773 317 | 3.450000, 3.460000, 420.4822 318 | 3.460000, 3.470000, 420.7291 319 | 3.470000, 3.480000, 420.9757 320 | 3.480000, 3.490000, 421.2532 321 | 3.490000, 3.500000, 421.5309 322 | 3.500000, 3.510000, 421.7994 323 | 3.510000, 3.520000, 422.0587 324 | 3.520000, 3.530000, 422.3182 325 | 3.530000, 3.540000, 422.4283 326 | 3.540000, 3.550000, 422.5385 327 | 3.550000, 3.560000, 422.6730 328 | 3.560000, 3.570000, 422.8318 329 | 3.570000, 3.580000, 422.9909 330 | 3.580000, 3.590000, 423.0149 331 | 3.590000, 3.600000, 423.0392 332 | 3.600000, 3.610000, 423.1379 333 | 3.610000, 3.620000, 423.3112 334 | 3.620000, 3.630000, 423.4847 335 | 3.630000, 3.640000, 423.5077 336 | 3.640000, 3.650000, 423.5198 337 | 3.650000, 3.660000, 423.5132 338 | 3.660000, 3.670000, 423.4868 339 | 3.670000, 3.680000, 423.4605 340 | 3.680000, 3.690000, 423.5601 341 | 3.690000, 3.700000, 423.6442 342 | 3.700000, 3.710000, 423.6437 343 | 3.710000, 3.720000, 423.6278 344 | 3.720000, 3.730000, 423.6113 345 | 3.730000, 3.740000, 423.4310 346 | 3.740000, 3.750000, 423.2507 347 | 3.750000, 3.760000, 423.1031 348 | 3.760000, 3.770000, 422.9889 349 | 3.770000, 3.780000, 422.8745 350 | 3.780000, 3.790000, 422.7863 351 | 3.790000, 3.800000, 422.6982 352 | 3.800000, 3.810000, 422.5501 353 | 3.810000, 3.820000, 422.3422 354 | 3.820000, 3.830000, 422.1342 355 | 3.830000, 3.840000, 422.1346 356 | 3.840000, 3.850000, 422.1353 357 | 3.850000, 3.860000, 422.0187 358 | 3.860000, 3.870000, 421.7844 359 | 3.870000, 3.880000, 421.5504 360 | 3.880000, 3.890000, 421.3951 361 | 3.890000, 3.900000, 421.2396 362 | 3.900000, 3.910000, 421.0555 363 | 3.910000, 3.920000, 420.8427 364 | 3.920000, 3.930000, 420.6298 365 | 3.930000, 3.940000, 420.6717 366 | 3.940000, 3.950000, 420.7136 367 | 3.950000, 3.960000, 420.7579 368 | 3.960000, 3.970000, 420.8047 369 | 3.970000, 3.980000, 420.8516 370 | 3.980000, 3.990000, 420.8539 371 | 3.990000, 4.0, 420.8563 372 | 4.0, 4.010000, 420.9188 373 | 4.010000, 4.020000, 421.0415 374 | 4.020000, 4.030000, 421.1644 375 | 4.030000, 4.040000, 420.7272 376 | 4.040000, 4.050000, 420.2250 377 | 4.050000, 4.060000, 419.8042 378 | 4.060000, 4.070000, 419.4648 379 | 4.070000, 4.080000, 419.1256 380 | 4.080000, 4.090000, 418.7136 381 | 4.090000, 4.100000, 418.3015 382 | 4.100000, 4.110000, 417.9099 383 | 4.110000, 4.120000, 417.5399 384 | 4.120000, 4.130000, 417.1702 385 | 4.130000, 4.140000, 416.6909 386 | 4.140000, 4.150000, 416.2120 387 | 4.150000, 4.160000, 415.7574 388 | 4.160000, 4.170000, 415.3273 389 | 4.170000, 4.180000, 414.8973 390 | 4.180000, 4.190000, 414.4498 391 | 4.190000, 4.200000, 414.0027 392 | 4.200000, 4.210000, 413.5342 393 | 4.210000, 4.220000, 413.0443 394 | 4.220000, 4.230000, 412.5547 395 | 4.230000, 4.240000, 412.0550 396 | 4.240000, 4.250000, 411.5556 397 | 4.250000, 4.260000, 411.0597 398 | 4.260000, 4.270000, 410.5678 399 | 4.270000, 4.280000, 410.0762 400 | 4.280000, 4.290000, 409.4875 401 | 4.290000, 4.300000, 408.8992 402 | 4.300000, 4.310000, 408.3406 403 | 4.310000, 4.320000, 407.8118 404 | 4.320000, 4.330000, 407.2831 405 | 4.330000, 4.340000, 406.7527 406 | 4.340000, 4.350000, 406.2060 407 | 4.350000, 4.360000, 405.5374 408 | 4.360000, 4.370000, 404.7471 409 | 4.370000, 4.380000, 403.9572 410 | 4.380000, 4.390000, 403.3839 411 | 4.390000, 4.400000, 402.8110 412 | 4.400000, 4.410000, 402.1399 413 | 4.410000, 4.420000, 401.3708 414 | 4.420000, 4.430000, 400.6024 415 | 4.430000, 4.440000, 399.9634 416 | 4.440000, 4.450000, 399.3248 417 | 4.450000, 4.460000, 398.5962 418 | 4.460000, 4.470000, 397.7778 419 | 4.470000, 4.480000, 396.9601 420 | 4.480000, 4.490000, 396.2819 421 | 4.490000, 4.500000, 395.6043 422 | 4.500000, 4.510000, 394.8864 423 | 4.510000, 4.520000, 394.1279 424 | 4.520000, 4.530000, 393.3703 425 | 4.530000, 4.540000, 392.6284 426 | 4.540000, 4.550000, 391.8870 427 | 4.550000, 4.560000, 391.0412 428 | 4.560000, 4.570000, 390.0909 429 | 4.570000, 4.580000, 389.1414 430 | 4.580000, 4.590000, 388.2761 431 | 4.590000, 4.600000, 387.4120 432 | 4.600000, 4.610000, 386.4630 433 | 4.610000, 4.620000, 385.4381 434 | 4.620000, 4.630000, 384.4142 435 | 4.630000, 4.640000, 383.5469 436 | 4.640000, 4.650000, 382.6808 437 | 4.650000, 4.660000, 381.6971 438 | 4.660000, 4.670000, 380.5952 439 | 4.670000, 4.680000, 379.4948 440 | 4.680000, 4.690000, 378.6279 441 | 4.690000, 4.700000, 377.7614 442 | 4.700000, 4.710000, 376.8158 443 | 4.710000, 4.720000, 375.7915 444 | 4.720000, 4.730000, 374.7684 445 | 4.730000, 4.740000, 373.8029 446 | 4.740000, 4.750000, 372.8390 447 | 4.750000, 4.760000, 371.8353 448 | 4.760000, 4.770000, 370.7921 449 | 4.770000, 4.780000, 369.7503 450 | 4.780000, 4.790000, 368.7555 451 | 4.790000, 4.800000, 367.7618 452 | 4.800000, 4.810000, 366.6895 453 | 4.810000, 4.820000, 365.5382 454 | 4.820000, 4.830000, 364.3887 455 | 4.830000, 4.840000, 363.4570 456 | 4.840000, 4.850000, 362.5266 457 | 4.850000, 4.860000, 361.4785 458 | 4.860000, 4.870000, 360.3130 459 | 4.870000, 4.880000, 359.1494 460 | 4.880000, 4.890000, 358.0657 461 | 4.890000, 4.900000, 356.9546 462 | 4.900000, 4.910000, 355.7640 463 | 4.910000, 4.920000, 354.4946 464 | 4.920000, 4.930000, 353.2264 465 | 4.930000, 4.940000, 352.0195 466 | 4.940000, 4.950000, 350.8143 467 | 4.950000, 4.960000, 349.6080 468 | 4.960000, 4.970000, 348.4006 469 | 4.970000, 4.980000, 347.1954 470 | 4.980000, 4.990000, 345.9271 471 | 4.990000, 5.0, 344.6589 472 | 5.0, 5.010000, 343.3712 473 | 5.010000, 5.020000, 342.0637 474 | 5.020000, 5.030000, 340.7582 475 | 5.030000, 5.040000, 339.4781 476 | 5.040000, 5.050000, 338.1999 477 | 5.050000, 5.060000, 336.9052 478 | 5.060000, 5.070000, 335.5946 479 | 5.070000, 5.080000, 334.2860 480 | 5.080000, 5.090000, 332.9315 481 | 5.090000, 5.100000, 331.5799 482 | 5.100000, 5.110000, 330.2602 483 | 5.110000, 5.120000, 328.9727 484 | 5.120000, 5.130000, 327.6874 485 | 5.130000, 5.140000, 326.3781 486 | 5.140000, 5.150000, 325.0713 487 | 5.150000, 5.160000, 323.7137 488 | 5.160000, 5.170000, 322.3058 489 | 5.170000, 5.180000, 320.9005 490 | 5.180000, 5.190000, 319.5412 491 | 5.190000, 5.200000, 318.1319 492 | 5.200000, 5.210000, 316.7243 493 | 5.210000, 5.220000, 315.3184 494 | 5.220000, 5.230000, 313.9154 495 | 5.230000, 5.240000, 312.4223 496 | 5.240000, 5.250000, 310.9320 497 | 5.250000, 5.260000, 309.4671 498 | 5.260000, 5.270000, 308.0275 499 | 5.270000, 5.280000, 306.5909 500 | 5.280000, 5.290000, 305.1096 501 | 5.290000, 5.300000, 303.6316 502 | 5.300000, 5.310000, 302.1592 503 | 5.310000, 5.320000, 300.6925 504 | 5.320000, 5.330000, 299.2289 505 | 5.330000, 5.340000, 297.7368 506 | 5.340000, 5.350000, 296.2478 507 | 5.350000, 5.360000, 294.7606 508 | 5.360000, 5.370000, 293.2753 509 | 5.370000, 5.380000, 291.7929 510 | 5.380000, 5.390000, 290.2085 511 | 5.390000, 5.400000, 288.6277 512 | 5.400000, 5.410000, 287.1012 513 | 5.410000, 5.420000, 285.6292 514 | 5.420000, 5.430000, 284.1599 515 | 5.430000, 5.440000, 282.7286 516 | 5.440000, 5.450000, 281.3000 517 | 5.450000, 5.460000, 279.8371 518 | 5.460000, 5.470000, 278.3279 519 | 5.470000, 5.480000, 276.7905 520 | 5.480000, 5.490000, 275.3957 521 | 5.490000, 5.500000, 274.0042 522 | 5.500000, 5.510000, 272.5317 523 | 5.510000, 5.520000, 270.9786 524 | 5.520000, 5.530000, 269.4294 525 | 5.530000, 5.540000, 268.1018 526 | 5.540000, 5.550000, 266.7772 527 | 5.550000, 5.560000, 265.3829 528 | 5.560000, 5.570000, 263.9189 529 | 5.570000, 5.580000, 262.4585 530 | 5.580000, 5.590000, 261.0559 531 | 5.590000, 5.600000, 259.6569 532 | 5.600000, 5.610000, 258.2659 533 | 5.610000, 5.620000, 256.8833 534 | 5.620000, 5.630000, 255.5039 535 | 5.630000, 5.640000, 254.1515 536 | 5.640000, 5.650000, 252.8022 537 | 5.650000, 5.660000, 251.4846 538 | 5.660000, 5.670000, 250.1987 539 | 5.670000, 5.680000, 248.9159 540 | 5.680000, 5.690000, 247.6277 541 | 5.690000, 5.700000, 246.3426 542 | 5.700000, 5.710000, 245.1066 543 | 5.710000, 5.720000, 243.9192 544 | 5.720000, 5.730000, 242.7347 545 | 5.730000, 5.740000, 241.4967 546 | 5.740000, 5.750000, 240.2365 547 | 5.750000, 5.760000, 239.0125 548 | 5.760000, 5.770000, 237.8248 549 | 5.770000, 5.780000, 236.6399 550 | 5.780000, 5.790000, 235.3495 551 | 5.790000, 5.800000, 234.0623 552 | 5.800000, 5.810000, 232.8394 553 | 5.810000, 5.820000, 231.6804 554 | 5.820000, 5.830000, 230.5242 555 | 5.830000, 5.840000, 229.3219 556 | 5.840000, 5.850000, 228.1227 557 | 5.850000, 5.860000, 226.9472 558 | 5.860000, 5.870000, 225.7957 559 | 5.870000, 5.880000, 224.6470 560 | 5.880000, 5.890000, 223.4920 561 | 5.890000, 5.900000, 222.3400 562 | 5.900000, 5.910000, 221.3192 563 | 5.910000, 5.920000, 220.4288 564 | 5.920000, 5.930000, 219.5404 565 | 5.930000, 5.940000, 218.2817 566 | 5.940000, 5.950000, 217.0265 567 | 5.950000, 5.960000, 215.8338 568 | 5.960000, 5.970000, 214.7030 569 | 5.970000, 5.980000, 213.5752 570 | 5.980000, 5.990000, 212.4319 571 | 5.990000, 6.0, 211.2842 572 | 6.0, 6.010000, 210.1863 573 | 6.010000, 6.020000, 209.1378 574 | 6.020000, 6.030000, 208.0921 575 | 6.030000, 6.040000, 207.0466 576 | 6.040000, 6.050000, 206.0038 577 | 6.050000, 6.060000, 204.9430 578 | 6.060000, 6.070000, 203.8641 579 | 6.070000, 6.080000, 202.7881 580 | 6.080000, 6.090000, 201.7512 581 | 6.090000, 6.100000, 200.7169 582 | 6.100000, 6.110000, 199.6822 583 | 6.110000, 6.120000, 198.6473 584 | 6.120000, 6.130000, 197.6150 585 | 6.130000, 6.140000, 196.5046 586 | 6.140000, 6.150000, 195.3972 587 | 6.150000, 6.160000, 194.3558 588 | 6.160000, 6.170000, 193.3800 589 | 6.170000, 6.180000, 192.4067 590 | 6.180000, 6.190000, 191.2935 591 | 6.190000, 6.200000, 190.1835 592 | 6.200000, 6.210000, 189.1436 593 | 6.210000, 6.220000, 188.1732 594 | 6.220000, 6.230000, 187.2055 595 | 6.230000, 6.240000, 186.1049 596 | 6.240000, 6.250000, 185.0078 597 | 6.250000, 6.260000, 183.9271 598 | 6.260000, 6.270000, 182.8629 599 | 6.270000, 6.280000, 181.8015 600 | 6.280000, 6.290000, 180.7835 601 | 6.290000, 6.300000, 179.7682 602 | 6.300000, 6.310000, 178.7527 603 | 6.310000, 6.320000, 177.7371 604 | 6.320000, 6.330000, 176.7243 605 | 6.330000, 6.340000, 175.6834 606 | 6.340000, 6.350000, 174.6454 607 | 6.350000, 6.360000, 173.5642 608 | 6.360000, 6.370000, 172.4402 609 | 6.370000, 6.380000, 171.3196 610 | 6.380000, 6.390000, 170.2518 611 | 6.390000, 6.400000, 169.1872 612 | 6.400000, 6.410000, 168.1701 613 | 6.410000, 6.420000, 167.2002 614 | 6.420000, 6.430000, 166.2330 615 | 6.430000, 6.440000, 165.1210 616 | 6.440000, 6.450000, 164.0124 617 | 6.450000, 6.460000, 162.9131 618 | 6.460000, 6.470000, 161.8227 619 | 6.470000, 6.480000, 160.7359 620 | 6.480000, 6.490000, 159.6026 621 | 6.490000, 6.500000, 158.4731 622 | 6.500000, 6.510000, 157.3883 623 | 6.510000, 6.520000, 156.3490 624 | 6.520000, 6.530000, 155.3159 625 | 6.530000, 6.540000, 154.1591 626 | 6.540000, 6.550000, 153.0061 627 | 6.550000, 6.560000, 151.8990 628 | 6.560000, 6.570000, 150.8383 629 | 6.570000, 6.580000, 149.7845 630 | 6.580000, 6.590000, 148.6770 631 | 6.590000, 6.600000, 147.5732 632 | 6.600000, 6.610000, 146.4889 633 | 6.610000, 6.620000, 145.4241 634 | 6.620000, 6.630000, 144.3627 635 | 6.630000, 6.640000, 143.2640 636 | 6.640000, 6.650000, 142.1633 637 | 6.650000, 6.660000, 141.0939 638 | 6.660000, 6.670000, 140.0556 639 | 6.670000, 6.680000, 139.0207 640 | 6.680000, 6.690000, 137.8923 641 | 6.690000, 6.700000, 136.7693 642 | 6.700000, 6.710000, 135.6900 643 | 6.710000, 6.720000, 134.6473 644 | 6.720000, 6.730000, 133.6080 645 | 6.730000, 6.740000, 132.5267 646 | 6.740000, 6.750000, 131.4514 647 | 6.750000, 6.760000, 130.4137 648 | 6.760000, 6.770000, 129.4087 649 | 6.770000, 6.780000, 128.4069 650 | 6.780000, 6.790000, 127.3139 651 | 6.790000, 6.800000, 126.2248 652 | 6.800000, 6.810000, 125.1606 653 | 6.810000, 6.820000, 124.1302 654 | 6.820000, 6.830000, 123.1080 655 | 6.830000, 6.840000, 122.1268 656 | 6.840000, 6.850000, 121.1487 657 | 6.850000, 6.860000, 120.1600 658 | 6.860000, 6.870000, 119.1465 659 | 6.870000, 6.880000, 118.1141 660 | 6.880000, 6.890000, 117.0592 661 | 6.890000, 6.900000, 116.0220 662 | 6.900000, 6.910000, 115.0239 663 | 6.910000, 6.920000, 114.0575 664 | 6.920000, 6.930000, 113.0915 665 | 6.930000, 6.940000, 112.1174 666 | 6.940000, 6.950000, 111.1466 667 | 6.950000, 6.960000, 110.1900 668 | 6.960000, 6.970000, 109.2415 669 | 6.970000, 6.980000, 108.2965 670 | 6.980000, 6.990000, 107.3696 671 | 6.990000, 7.0, 106.4681 672 | 7.0, 7.010000, 105.5324 673 | 7.010000, 7.020000, 104.5593 674 | 7.020000, 7.030000, 103.5895 675 | 7.030000, 7.040000, 102.6831 676 | 7.040000, 7.050000, 101.7798 677 | 7.050000, 7.060000, 100.8904 678 | 7.060000, 7.070000, 100.0146 679 | 7.070000, 7.080000, 99.14191 680 | 7.080000, 7.090000, 98.25423 681 | 7.090000, 7.100000, 97.36972 682 | 7.100000, 7.110000, 96.49434 683 | 7.110000, 7.120000, 95.62825 684 | 7.120000, 7.130000, 94.76522 685 | 7.130000, 7.140000, 93.90019 686 | 7.140000, 7.150000, 93.03844 687 | 7.150000, 7.160000, 92.22681 688 | 7.160000, 7.170000, 91.46507 689 | 7.170000, 7.180000, 90.70601 690 | 7.180000, 7.190000, 89.83014 691 | 7.190000, 7.200000, 88.95749 692 | 7.200000, 7.210000, 88.17001 693 | 7.210000, 7.220000, 87.46698 694 | 7.220000, 7.230000, 86.76648 695 | 7.230000, 7.240000, 85.98085 696 | 7.240000, 7.250000, 85.19826 697 | 7.250000, 7.260000, 84.43953 698 | 7.260000, 7.270000, 83.70470 699 | 7.270000, 7.280000, 82.97251 700 | 7.280000, 7.290000, 82.18269 701 | 7.290000, 7.300000, 81.39593 702 | 7.300000, 7.310000, 80.63524 703 | 7.310000, 7.320000, 79.90035 704 | 7.320000, 7.330000, 79.16837 705 | 7.330000, 7.340000, 78.43034 706 | 7.340000, 7.350000, 77.69521 707 | 7.350000, 7.360000, 76.95379 708 | 7.360000, 7.370000, 76.20618 709 | 7.370000, 7.380000, 75.46152 710 | 7.380000, 7.390000, 74.77134 711 | 7.390000, 7.400000, 74.08385 712 | 7.400000, 7.410000, 73.36722 713 | 7.410000, 7.420000, 72.62167 714 | 7.420000, 7.430000, 71.87908 715 | 7.430000, 7.440000, 71.22904 716 | 7.440000, 7.450000, 70.58151 717 | 7.450000, 7.460000, 69.92595 718 | 7.460000, 7.470000, 69.25378 719 | 7.470000, 7.480000, 68.58413 720 | 7.480000, 7.490000, 67.90415 721 | 7.490000, 7.500000, 67.22684 722 | 7.500000, 7.510000, 66.57602 723 | 7.510000, 7.520000, 65.95153 724 | 7.520000, 7.530000, 65.32935 725 | 7.530000, 7.540000, 64.67407 726 | 7.540000, 7.550000, 64.02126 727 | 7.550000, 7.560000, 63.42982 728 | 7.560000, 7.570000, 62.89931 729 | 7.570000, 7.580000, 62.37066 730 | 7.580000, 7.590000, 61.63684 731 | 7.590000, 7.600000, 60.90595 732 | 7.600000, 7.610000, 60.24594 733 | 7.610000, 7.620000, 59.65628 734 | 7.620000, 7.630000, 59.06887 735 | 7.630000, 7.640000, 58.47047 736 | 7.640000, 7.650000, 57.87441 737 | 7.650000, 7.660000, 57.30429 738 | 7.660000, 7.670000, 56.75997 739 | 7.670000, 7.680000, 56.21770 740 | 7.680000, 7.690000, 55.64758 741 | 7.690000, 7.700000, 55.07961 742 | 7.700000, 7.710000, 54.51828 743 | 7.710000, 7.720000, 53.96339 744 | 7.720000, 7.730000, 53.41062 745 | 7.730000, 7.740000, 52.88986 746 | 7.740000, 7.750000, 52.37107 747 | 7.750000, 7.760000, 51.84652 748 | 7.760000, 7.770000, 51.31624 749 | 7.770000, 7.780000, 50.78799 750 | 7.780000, 7.790000, 50.24315 751 | 7.790000, 7.800000, 49.70042 752 | 7.800000, 7.810000, 49.19361 753 | 7.810000, 7.820000, 48.72236 754 | 7.820000, 7.830000, 48.25283 755 | 7.830000, 7.840000, 47.77689 756 | 7.840000, 7.850000, 47.30957 757 | 7.850000, 7.860000, 46.83662 758 | 7.860000, 7.870000, 46.35814 759 | 7.870000, 7.880000, 45.88128 760 | 7.880000, 7.890000, 45.46207 761 | 7.890000, 7.900000, 45.04432 762 | 7.900000, 7.910000, 44.62193 763 | 7.910000, 7.920000, 44.19492 764 | 7.920000, 7.930000, 43.76935 765 | 7.930000, 7.940000, 43.35687 766 | 7.940000, 7.950000, 42.94575 767 | 7.950000, 7.960000, 42.54553 768 | 7.960000, 7.970000, 42.15615 769 | 7.970000, 7.980000, 41.76803 770 | 7.980000, 7.990000, 41.35974 771 | 7.990000, 8.0, 40.95282 772 | 8.0, 8.010000, 40.57387 773 | 8.010000, 8.020000, 40.22274 774 | 8.020000, 8.030000, 39.87274 775 | 8.030000, 8.040000, 39.52312 776 | 8.040000, 8.050000, 39.17463 777 | 8.050000, 8.060000, 38.84677 778 | 8.060000, 8.070000, 38.53941 779 | 8.070000, 8.080000, 38.23299 780 | 8.080000, 8.090000, 37.90081 781 | 8.090000, 8.100000, 37.56972 782 | 8.100000, 8.110000, 37.25515 783 | 8.110000, 8.120000, 36.95695 784 | 8.120000, 8.130000, 36.65978 785 | 8.130000, 8.140000, 36.38116 786 | 8.140000, 8.150000, 36.10349 787 | 8.150000, 8.160000, 35.83578 788 | 8.160000, 8.170000, 35.57792 789 | 8.170000, 8.180000, 35.32088 790 | 8.180000, 8.190000, 35.06646 791 | 8.190000, 8.200000, 34.81927 792 | 8.200000, 8.210000, 34.57408 793 | 8.210000, 8.220000, 34.32956 794 | 8.220000, 8.230000, 34.08581 795 | 8.230000, 8.240000, 33.86412 796 | 8.240000, 8.250000, 33.64309 797 | 8.250000, 8.260000, 33.42145 798 | 8.260000, 8.270000, 33.19934 799 | 8.270000, 8.280000, 32.97795 800 | 8.280000, 8.290000, 32.77747 801 | 8.290000, 8.300000, 32.57759 802 | 8.300000, 8.310000, 32.38216 803 | 8.310000, 8.320000, 32.19120 804 | 8.320000, 8.330000, 32.00077 805 | 8.330000, 8.340000, 31.78924 806 | 8.340000, 8.350000, 31.57837 807 | 8.350000, 8.360000, 31.37864 808 | 8.360000, 8.370000, 31.18990 809 | 8.370000, 8.380000, 31.00177 810 | 8.380000, 8.390000, 30.80851 811 | 8.390000, 8.400000, 30.61587 812 | 8.400000, 8.410000, 30.42629 813 | 8.410000, 8.420000, 30.23979 814 | 8.420000, 8.430000, 30.05386 815 | 8.430000, 8.440000, 29.87946 816 | 8.440000, 8.450000, 29.70563 817 | 8.450000, 8.460000, 29.53038 818 | 8.460000, 8.470000, 29.35373 819 | 8.470000, 8.480000, 29.17763 820 | 8.480000, 8.490000, 29.01101 821 | 8.490000, 8.500000, 28.84489 822 | 8.500000, 8.510000, 28.67958 823 | 8.510000, 8.520000, 28.51511 824 | 8.520000, 8.530000, 28.35116 825 | 8.530000, 8.540000, 28.19708 826 | 8.540000, 8.550000, 28.04492 827 | 8.550000, 8.560000, 27.89309 828 | 8.560000, 8.570000, 27.74158 829 | 8.570000, 8.580000, 27.59047 830 | 8.580000, 8.590000, 27.44325 831 | 8.590000, 8.600000, 27.29645 832 | 8.600000, 8.610000, 27.14653 833 | 8.610000, 8.620000, 26.99351 834 | 8.620000, 8.630000, 26.84093 835 | 8.630000, 8.640000, 26.71295 836 | 8.640000, 8.650000, 26.58531 837 | 8.650000, 8.660000, 26.45345 838 | 8.660000, 8.670000, 26.31740 839 | 8.670000, 8.680000, 26.18173 840 | 8.680000, 8.690000, 26.04319 841 | 8.690000, 8.700000, 25.90505 842 | 8.700000, 8.710000, 25.76883 843 | 8.710000, 8.720000, 25.63451 844 | 8.720000, 8.730000, 25.50057 845 | 8.730000, 8.740000, 25.37455 846 | 8.740000, 8.750000, 25.24886 847 | 8.750000, 8.760000, 25.12057 848 | 8.760000, 8.770000, 24.98973 849 | 8.770000, 8.780000, 24.85928 850 | 8.780000, 8.790000, 24.72983 851 | 8.790000, 8.800000, 24.60075 852 | 8.800000, 8.810000, 24.47371 853 | 8.810000, 8.820000, 24.34870 854 | 8.820000, 8.830000, 24.22401 855 | 8.830000, 8.840000, 24.09416 856 | 8.840000, 8.850000, 23.96466 857 | 8.850000, 8.860000, 23.83811 858 | 8.860000, 8.870000, 23.71443 859 | 8.870000, 8.880000, 23.59640 860 | 8.880000, 8.890000, 23.48084 861 | 8.890000, 8.900000, 23.36560 862 | 8.900000, 8.910000, 23.23900 863 | 8.910000, 8.920000, 23.10114 864 | 8.920000, 8.930000, 22.96372 865 | 8.930000, 8.940000, 22.85305 866 | 8.940000, 8.950000, 22.74268 867 | 8.950000, 8.960000, 22.62527 868 | 8.960000, 8.970000, 22.50086 869 | 8.970000, 8.980000, 22.37683 870 | 8.980000, 8.990000, 22.23174 871 | 8.990000, 9.0, 22.08713 872 | 9.0, 9.010000, 21.94783 873 | 9.010000, 9.020000, 21.81380 874 | 9.020000, 9.030000, 21.68020 875 | 9.030000, 9.040000, 21.56842 876 | 9.040000, 9.050000, 21.45693 877 | 9.050000, 9.060000, 21.34906 878 | 9.060000, 9.070000, 21.24480 879 | 9.070000, 9.080000, 21.14079 880 | 9.080000, 9.090000, 21.00803 881 | 9.090000, 9.100000, 20.87571 882 | 9.100000, 9.110000, 20.74695 883 | 9.110000, 9.120000, 20.62174 884 | 9.120000, 9.130000, 20.49691 885 | 9.130000, 9.140000, 20.39250 886 | 9.140000, 9.150000, 20.28839 887 | 9.150000, 9.160000, 20.17527 888 | 9.160000, 9.170000, 20.05322 889 | 9.170000, 9.180000, 19.93153 890 | 9.180000, 9.190000, 19.81430 891 | 9.190000, 9.200000, 19.69941 892 | 9.200000, 9.210000, 19.59084 893 | 9.210000, 9.220000, 19.48589 894 | 9.220000, 9.230000, 19.38127 895 | 9.230000, 9.240000, 19.25171 896 | 9.240000, 9.250000, 19.12260 897 | 9.250000, 9.260000, 19.00969 898 | 9.260000, 9.270000, 18.91281 899 | 9.270000, 9.280000, 18.81623 900 | 9.280000, 9.290000, 18.69226 901 | 9.290000, 9.300000, 18.56872 902 | 9.300000, 9.310000, 18.44848 903 | 9.310000, 9.320000, 18.33157 904 | 9.320000, 9.330000, 18.21506 905 | 9.330000, 9.340000, 18.09879 906 | 9.340000, 9.350000, 17.98291 907 | 9.350000, 9.360000, 17.86544 908 | 9.360000, 9.370000, 17.74637 909 | 9.370000, 9.380000, 17.62774 910 | 9.380000, 9.390000, 17.51213 911 | 9.390000, 9.400000, 17.39697 912 | 9.400000, 9.410000, 17.27693 913 | 9.410000, 9.420000, 17.15209 914 | 9.420000, 9.430000, 17.02775 915 | 9.430000, 9.440000, 16.90534 916 | 9.440000, 9.450000, 16.78345 917 | 9.450000, 9.460000, 16.65935 918 | 9.460000, 9.470000, 16.53310 919 | 9.470000, 9.480000, 16.40737 920 | 9.480000, 9.490000, 16.28780 921 | 9.490000, 9.500000, 16.16871 922 | 9.500000, 9.510000, 16.04566 923 | 9.510000, 9.520000, 15.91869 924 | 9.520000, 9.530000, 15.79462 925 | 9.530000, 9.540000, 15.66459 926 | 9.540000, 9.550000, 15.53512 927 | 9.550000, 9.560000, 15.40902 928 | 9.560000, 9.570000, 15.28626 929 | 9.570000, 9.580000, 15.16403 930 | 9.580000, 9.590000, 15.03056 931 | 9.590000, 9.600000, 14.89770 932 | 9.600000, 9.610000, 14.76459 933 | 9.610000, 9.620000, 14.63124 934 | 9.620000, 9.630000, 14.49851 935 | 9.630000, 9.640000, 14.37304 936 | 9.640000, 9.650000, 14.24813 937 | 9.650000, 9.660000, 14.11506 938 | 9.660000, 9.670000, 13.97390 939 | 9.670000, 9.680000, 13.83342 940 | 9.680000, 9.690000, 13.69645 941 | 9.690000, 9.700000, 13.56017 942 | 9.700000, 9.710000, 13.41570 943 | 9.710000, 9.720000, 13.26315 944 | 9.720000, 9.730000, 13.11137 945 | 9.730000, 9.740000, 12.96682 946 | 9.740000, 9.750000, 12.82298 947 | 9.750000, 9.760000, 12.67288 948 | 9.760000, 9.770000, 12.51661 949 | 9.770000, 9.780000, 12.36115 950 | 9.780000, 9.790000, 12.20737 951 | 9.790000, 9.800000, 12.05437 952 | 9.800000, 9.810000, 11.89892 953 | 9.810000, 9.820000, 11.74101 954 | 9.820000, 9.830000, 11.58388 955 | 9.830000, 9.840000, 11.42616 956 | 9.840000, 9.850000, 11.26926 957 | 9.850000, 9.860000, 11.11206 958 | 9.860000, 9.870000, 10.95584 959 | 9.870000, 9.880000, 10.80039 960 | 9.880000, 9.890000, 10.64327 961 | 9.890000, 9.900000, 10.48694 962 | 9.900000, 9.910000, 10.33301 963 | 9.910000, 9.920000, 10.18141 964 | 9.920000, 9.930000, 10.03053 965 | 9.930000, 9.940000, 9.883158 966 | 9.940000, 9.950000, 9.736496 967 | 9.950000, 9.960000, 9.586643 968 | 9.960000, 9.970000, 9.433647 969 | 9.970000, 9.980000, 9.281395 970 | 9.980000, 9.990000, 9.135818 971 | 9.990000, 10.0, 8.990935 972 | 10.0, 10.01000, 8.846270 973 | 10.01000, 10.02000, 8.701756 974 | 10.02000, 10.03000, 8.557865 975 | 10.03000, 10.04000, 8.409375 976 | 10.04000, 10.05000, 8.261522 977 | 10.05000, 10.06000, 8.114630 978 | 10.06000, 10.07000, 7.969364 979 | 10.07000, 10.08000, 7.824683 980 | 10.08000, 10.09000, 7.673676 981 | 10.09000, 10.10000, 7.523295 982 | 10.10000, 10.11000, 7.376026 983 | 10.11000, 10.12000, 7.231812 984 | 10.12000, 10.13000, 7.088203 985 | 10.13000, 10.14000, 6.944844 986 | 10.14000, 10.15000, 6.802094 987 | 10.15000, 10.16000, 6.661960 988 | 10.16000, 10.17000, 6.524400 989 | 10.17000, 10.18000, 6.387429 990 | 10.18000, 10.19000, 6.250183 991 | 10.19000, 10.20000, 6.113548 992 | 10.20000, 10.21000, 5.976159 993 | 10.21000, 10.22000, 5.838039 994 | 10.22000, 10.23000, 5.700528 995 | 10.23000, 10.24000, 5.576426 996 | 10.24000, 10.25000, 5.452854 997 | 10.25000, 10.26000, 5.332209 998 | 10.26000, 10.27000, 5.214472 999 | 10.27000, 10.28000, 5.097268 1000 | 10.28000, 10.29000, 4.974730 1001 | 10.29000, 10.30000, 4.852746 1002 | 10.30000, 10.31000, 4.737000 1003 | 10.31000, 10.32000, 4.627458 1004 | 10.32000, 10.33000, 4.518402 1005 | 10.33000, 10.34000, 4.401076 1006 | 10.34000, 10.35000, 4.284282 1007 | 10.35000, 10.36000, 4.165083 1008 | 10.36000, 10.37000, 4.043474 1009 | 10.37000, 10.38000, 3.922431 1010 | 10.38000, 10.39000, 3.828380 1011 | 10.39000, 10.40000, 3.735570 1012 | 10.40000, 10.41000, 3.641296 1013 | 10.41000, 10.42000, 3.545546 1014 | 10.42000, 10.43000, 3.450203 1015 | 10.43000, 10.44000, 3.351016 1016 | 10.44000, 10.45000, 3.252268 1017 | 10.45000, 10.46000, 3.154171 1018 | 10.46000, 10.47000, 3.056674 1019 | 10.47000, 10.48000, 2.959550 1020 | 10.48000, 10.49000, 2.871018 1021 | 10.49000, 10.50000, 2.782872 1022 | 10.50000, 10.51000, 2.693994 1023 | 10.51000, 10.52000, 2.604404 1024 | 10.52000, 10.53000, 2.515236 1025 | 10.53000, 10.54000, 2.433507 1026 | 10.54000, 10.55000, 2.352146 1027 | 10.55000, 10.56000, 2.269678 1028 | 10.56000, 10.57000, 2.186206 1029 | 10.57000, 10.58000, 2.103165 1030 | 10.58000, 10.59000, 2.029825 1031 | 10.59000, 10.60000, 1.956811 1032 | 10.60000, 10.61000, 1.885408 1033 | 10.61000, 10.62000, 1.815598 1034 | 10.62000, 10.63000, 1.746106 1035 | 10.63000, 10.64000, 1.687208 1036 | 10.64000, 10.65000, 1.628675 1037 | 10.65000, 10.66000, 1.572957 1038 | 10.66000, 10.67000, 1.519946 1039 | 10.67000, 10.68000, 1.467168 1040 | 10.68000, 10.69000, 1.421691 1041 | 10.69000, 10.70000, 1.376418 1042 | 10.70000, 10.71000, 1.333534 1043 | 10.71000, 10.72000, 1.293028 1044 | 10.72000, 10.73000, 1.252700 1045 | 10.73000, 10.74000, 1.213833 1046 | 10.74000, 10.75000, 1.175135 1047 | 10.75000, 10.76000, 1.140952 1048 | 10.76000, 10.77000, 1.111253 1049 | 10.77000, 10.78000, 1.081686 1050 | 10.78000, 10.79000, 1.051528 1051 | 10.79000, 10.80000, 1.021502 1052 | 10.80000, 10.81000, 0.9925723 1053 | 10.81000, 10.82000, 0.9647321 1054 | 10.82000, 10.83000, 0.9370143 1055 | 10.83000, 10.84000, 0.9115387 1056 | 10.84000, 10.85000, 0.8863007 1057 | 10.85000, 10.86000, 0.8615682 1058 | 10.86000, 10.87000, 0.8373258 1059 | 10.87000, 10.88000, 0.8131868 1060 | 10.88000, 10.89000, 0.7908219 1061 | 10.89000, 10.90000, 0.7685533 1062 | 10.90000, 10.91000, 0.7468771 1063 | 10.91000, 10.92000, 0.7257646 1064 | 10.92000, 10.93000, 0.7047327 1065 | 10.93000, 10.94000, 0.6848949 1066 | 10.94000, 10.95000, 0.6651462 1067 | 10.95000, 10.96000, 0.6451889 1068 | 10.96000, 10.97000, 0.6250272 1069 | 10.97000, 10.98000, 0.6049552 1070 | 10.98000, 10.99000, 0.5852773 1071 | 10.99000, 11.0, 0.5657126 1072 | -------------------------------------------------------------------------------- /data/src.arf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DougBurke/astro-haskell/62aad9557ce49deecb0dae303f616fb182e10c0b/data/src.arf -------------------------------------------------------------------------------- /draft/fits II.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Draft" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Too many *things*:\n", 15 | "\n", 16 | " - read in a RMF (in the quest for understanding how to compare model to data)\n", 17 | " - play around with \"typing\" arrays based on their units\n", 18 | " - can we have a [`frames`](https://github.com/acowley/Frames) \n", 19 | " back end for FITS table files (perhaps ocnvert ARF to CSV to get some\n", 20 | " more experience with `frames`?)?\n", 21 | " \n", 22 | "Any of these require actual parsing of the header data...\n", 23 | "\n", 24 | "So, back to the ARF file from the previous notebook.\n", 25 | "\n", 26 | "I should note that up-to-date information on the \n", 27 | "[FITS standard](http://fits.gsfc.nasa.gov/fits_standard.html) is available!" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "Unrelated ideas (although they do involve parsing, mainly XML responses\n", 35 | "from a web server):\n", 36 | "\n", 37 | " - given a name, call a name resolver (e.g. CADC) to get a location;\n", 38 | " then Chandra footprint server (which has images). Now, name server\n", 39 | " isn't actually needed.\n", 40 | " \n", 41 | " - explain `@astroprop` code" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 1, 47 | "metadata": { 48 | "collapsed": false 49 | }, 50 | "outputs": [], 51 | "source": [ 52 | "import qualified Data.ByteString.Char8 as B8\n", 53 | "\n", 54 | "cts <- B8.readFile \"../data/src.arf\"\n", 55 | "\n", 56 | "chunk :: Int\n", 57 | "chunk = 2880\n", 58 | "\n", 59 | "splitBS :: B8.ByteString -> Int -> Int -> B8.ByteString\n", 60 | "splitBS inBS s n = B8.take n (B8.drop s inBS)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "Cheat since we know how long the header is..." 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 2, 73 | "metadata": { 74 | "collapsed": false 75 | }, 76 | "outputs": [], 77 | "source": [ 78 | "hdrBS = splitBS cts chunk (6*chunk)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 3, 84 | "metadata": { 85 | "collapsed": false 86 | }, 87 | "outputs": [ 88 | { 89 | "data": { 90 | "text/plain": [ 91 | "\"XTENSION= 'BINTABLE' / binary table extension \"" 92 | ] 93 | }, 94 | "metadata": {}, 95 | "output_type": "display_data" 96 | }, 97 | { 98 | "data": { 99 | "text/plain": [ 100 | "\" \"" 101 | ] 102 | }, 103 | "metadata": {}, 104 | "output_type": "display_data" 105 | } 106 | ], 107 | "source": [ 108 | "B8.take 80 hdrBS\n", 109 | "B8.drop (6*chunk-80) hdrBS" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "Re-use some of the code. However, let's tweak the types:\n", 117 | "\n", 118 | "XXX probably drop this, as don't really take it anywhere and have other types\n", 119 | "that can be used to represent \"type safety\" (e.g. Keyword)." 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": 4, 125 | "metadata": { 126 | "collapsed": false 127 | }, 128 | "outputs": [], 129 | "source": [ 130 | "newtype Card = Card B8.ByteString\n", 131 | " deriving (Eq, Show)\n", 132 | "\n", 133 | "-- | Given a string, split off the next 80 characters.\n", 134 | "getCard1 :: B8.ByteString -> (Card, B8.ByteString)\n", 135 | "getCard1 bs = let (ls, rs) = B8.splitAt 80 bs\n", 136 | " in (Card ls, rs)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "metadata": {}, 142 | "source": [ 143 | "With this, I can not mix up the unparsed data with a card, since they are a different\n", 144 | "type (previously I used `type` which is just a synonym)." 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 5, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [ 154 | { 155 | "data": { 156 | "text/plain": [ 157 | "Card \"XTENSION= 'BINTABLE' / binary table extension \"" 158 | ] 159 | }, 160 | "metadata": {}, 161 | "output_type": "display_data" 162 | } 163 | ], 164 | "source": [ 165 | "(a,_) = getCard1 hdrBS\n", 166 | "a" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 6, 172 | "metadata": { 173 | "collapsed": false 174 | }, 175 | "outputs": [ 176 | { 177 | "data": { 178 | "text/html": [ 179 | "Couldn't match expected type ‘B8.ByteString’ with actual type ‘Card’
In the first argument of ‘getCard1’, namely ‘a’
In the expression: getCard1 a
" 276 | ], 277 | "text/plain": [ 278 | "Couldn't match expected type ‘B8.ByteString’ with actual type ‘Card’\n", 279 | "In the first argument of ‘getCard1’, namely ‘a’\n", 280 | "In the expression: getCard1 a" 281 | ] 282 | }, 283 | "metadata": {}, 284 | "output_type": "display_data" 285 | } 286 | ], 287 | "source": [ 288 | "getCard1 a" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "metadata": {}, 294 | "source": [ 295 | "XXX hmmm; this doesn't really show off the type safety. may have to wait until have something more;\n", 296 | "perhaps the parse-a-header unit. Of course, the Card stuff is likely a digression if the aim is to use a parser combinator library\n", 297 | "a la `attoparsec`.\n", 298 | "\n", 299 | "I can continue my \"simplification mania\":" 300 | ] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": 7, 305 | "metadata": { 306 | "collapsed": true 307 | }, 308 | "outputs": [], 309 | "source": [ 310 | "import Control.Arrow (first)" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": 8, 316 | "metadata": { 317 | "collapsed": false 318 | }, 319 | "outputs": [ 320 | { 321 | "data": { 322 | "text/html": [ 323 | "first :: forall (a :: * -> * -> *) b c d. Arrow a => a b c -> a (b, d) (c, d)" 420 | ], 421 | "text/plain": [ 422 | "first :: forall (a :: * -> * -> *) b c d. Arrow a => a b c -> a (b, d) (c, d)" 423 | ] 424 | }, 425 | "metadata": {}, 426 | "output_type": "display_data" 427 | } 428 | ], 429 | "source": [ 430 | ":type first" 431 | ] 432 | }, 433 | { 434 | "cell_type": "code", 435 | "execution_count": 9, 436 | "metadata": { 437 | "collapsed": false 438 | }, 439 | "outputs": [ 440 | { 441 | "data": { 442 | "text/html": [ 443 | "first not (True,\"False\") :: (Bool, [Char])" 540 | ], 541 | "text/plain": [ 542 | "first not (True,\"False\") :: (Bool, [Char])" 543 | ] 544 | }, 545 | "metadata": {}, 546 | "output_type": "display_data" 547 | } 548 | ], 549 | "source": [ 550 | ":type first not (True,\"False\")" 551 | ] 552 | }, 553 | { 554 | "cell_type": "code", 555 | "execution_count": 10, 556 | "metadata": { 557 | "collapsed": false 558 | }, 559 | "outputs": [ 560 | { 561 | "data": { 562 | "text/plain": [ 563 | "(False,\"False\")" 564 | ] 565 | }, 566 | "metadata": {}, 567 | "output_type": "display_data" 568 | } 569 | ], 570 | "source": [ 571 | "first not (True,\"False\")" 572 | ] 573 | }, 574 | { 575 | "cell_type": "code", 576 | "execution_count": 11, 577 | "metadata": { 578 | "collapsed": false 579 | }, 580 | "outputs": [], 581 | "source": [ 582 | "-- | Given a string, split off the next 80 characters. The Arrow version.\n", 583 | "getCard :: B8.ByteString -> (Card, B8.ByteString)\n", 584 | "getCard = first Card . B8.splitAt 80" 585 | ] 586 | }, 587 | { 588 | "cell_type": "code", 589 | "execution_count": 12, 590 | "metadata": { 591 | "collapsed": false 592 | }, 593 | "outputs": [ 594 | { 595 | "data": { 596 | "text/plain": [ 597 | "Card \"XTENSION= 'BINTABLE' / binary table extension \"" 598 | ] 599 | }, 600 | "metadata": {}, 601 | "output_type": "display_data" 602 | } 603 | ], 604 | "source": [ 605 | "fst (getCard hdrBS)" 606 | ] 607 | }, 608 | { 609 | "cell_type": "markdown", 610 | "metadata": {}, 611 | "source": [ 612 | "XXX Insert a *quite hilarious* joke about `fst` and `first`." 613 | ] 614 | }, 615 | { 616 | "cell_type": "markdown", 617 | "metadata": {}, 618 | "source": [ 619 | "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n", 620 | "\n", 621 | "How about \n", 622 | "[attoparsec](https://hackage.haskell.org/package/attoparsec) => drop the card business?" 623 | ] 624 | }, 625 | { 626 | "cell_type": "code", 627 | "execution_count": 13, 628 | "metadata": { 629 | "collapsed": true 630 | }, 631 | "outputs": [], 632 | "source": [ 633 | "import Data.Attoparsec.ByteString.Char8 hiding (take)\n", 634 | "import Control.Applicative ((<*), (*>), (<|>), (<$>), optional)" 635 | ] 636 | }, 637 | { 638 | "cell_type": "markdown", 639 | "metadata": {}, 640 | "source": [ 641 | "Let's start with a keyword: 1 to 8 alphanumeric-ish characters:" 642 | ] 643 | }, 644 | { 645 | "cell_type": "code", 646 | "execution_count": 14, 647 | "metadata": { 648 | "collapsed": false 649 | }, 650 | "outputs": [], 651 | "source": [ 652 | "-- simplify rules\n", 653 | "keywordStart :: Parser Char\n", 654 | "keywordStart = satisfy (inClass ['A' .. 'Z'])" 655 | ] 656 | }, 657 | { 658 | "cell_type": "code", 659 | "execution_count": 15, 660 | "metadata": { 661 | "collapsed": false 662 | }, 663 | "outputs": [ 664 | { 665 | "data": { 666 | "text/plain": [ 667 | "Done \"\" 'X'" 668 | ] 669 | }, 670 | "metadata": {}, 671 | "output_type": "display_data" 672 | } 673 | ], 674 | "source": [ 675 | "parse keywordStart (B8.pack \"X\")" 676 | ] 677 | }, 678 | { 679 | "cell_type": "code", 680 | "execution_count": 16, 681 | "metadata": { 682 | "collapsed": true 683 | }, 684 | "outputs": [], 685 | "source": [ 686 | "Done leftover parseval = parse keywordStart (B8.pack \"XTENSION= 'blah blah'\")" 687 | ] 688 | }, 689 | { 690 | "cell_type": "code", 691 | "execution_count": 17, 692 | "metadata": { 693 | "collapsed": false 694 | }, 695 | "outputs": [ 696 | { 697 | "data": { 698 | "text/plain": [ 699 | "'X'" 700 | ] 701 | }, 702 | "metadata": {}, 703 | "output_type": "display_data" 704 | } 705 | ], 706 | "source": [ 707 | "parseval" 708 | ] 709 | }, 710 | { 711 | "cell_type": "code", 712 | "execution_count": 18, 713 | "metadata": { 714 | "collapsed": false 715 | }, 716 | "outputs": [ 717 | { 718 | "data": { 719 | "text/plain": [ 720 | "\"TENSION= 'blah blah'\"" 721 | ] 722 | }, 723 | "metadata": {}, 724 | "output_type": "display_data" 725 | } 726 | ], 727 | "source": [ 728 | "leftover" 729 | ] 730 | }, 731 | { 732 | "cell_type": "code", 733 | "execution_count": 19, 734 | "metadata": { 735 | "collapsed": false 736 | }, 737 | "outputs": [ 738 | { 739 | "data": { 740 | "text/plain": [ 741 | "Right 'Q'" 742 | ] 743 | }, 744 | "metadata": {}, 745 | "output_type": "display_data" 746 | } 747 | ], 748 | "source": [ 749 | "parseOnly keywordStart (B8.pack \"Q\")" 750 | ] 751 | }, 752 | { 753 | "cell_type": "code", 754 | "execution_count": 20, 755 | "metadata": { 756 | "collapsed": false 757 | }, 758 | "outputs": [ 759 | { 760 | "data": { 761 | "text/plain": [ 762 | "Left \"Failed reading: satisfyWith\"" 763 | ] 764 | }, 765 | "metadata": {}, 766 | "output_type": "display_data" 767 | } 768 | ], 769 | "source": [ 770 | "parseOnly keywordStart (B8.pack \"q\")" 771 | ] 772 | }, 773 | { 774 | "cell_type": "code", 775 | "execution_count": 21, 776 | "metadata": { 777 | "collapsed": false 778 | }, 779 | "outputs": [ 780 | { 781 | "data": { 782 | "text/plain": [ 783 | "Right 'Q'" 784 | ] 785 | }, 786 | "metadata": {}, 787 | "output_type": "display_data" 788 | } 789 | ], 790 | "source": [ 791 | "parseOnly keywordStart (B8.pack \"QQ\")" 792 | ] 793 | }, 794 | { 795 | "cell_type": "code", 796 | "execution_count": 22, 797 | "metadata": { 798 | "collapsed": false 799 | }, 800 | "outputs": [ 801 | { 802 | "data": { 803 | "text/plain": [ 804 | "Left \"endOfInput\"" 805 | ] 806 | }, 807 | "metadata": {}, 808 | "output_type": "display_data" 809 | } 810 | ], 811 | "source": [ 812 | "parseOnly (keywordStart <* endOfInput) (B8.pack \"QQ\")" 813 | ] 814 | }, 815 | { 816 | "cell_type": "code", 817 | "execution_count": 23, 818 | "metadata": { 819 | "collapsed": false 820 | }, 821 | "outputs": [], 822 | "source": [ 823 | "keywordBody1 :: Parser Char\n", 824 | "keywordBody1 = satisfy (inClass (['A' .. 'Z'] ++ ['0' .. '9'] ++ \"_-\"))" 825 | ] 826 | }, 827 | { 828 | "cell_type": "code", 829 | "execution_count": 24, 830 | "metadata": { 831 | "collapsed": false 832 | }, 833 | "outputs": [], 834 | "source": [ 835 | "keywordBody :: Parser Char\n", 836 | "keywordBody = keywordStart <|> satisfy (inClass ['0'..'9']) <|> satisfy (\\c -> c == '_' || c == '-')" 837 | ] 838 | }, 839 | { 840 | "cell_type": "code", 841 | "execution_count": 25, 842 | "metadata": { 843 | "collapsed": false 844 | }, 845 | "outputs": [ 846 | { 847 | "data": { 848 | "text/plain": [ 849 | "Right '6'" 850 | ] 851 | }, 852 | "metadata": {}, 853 | "output_type": "display_data" 854 | } 855 | ], 856 | "source": [ 857 | "parseOnly keywordBody (B8.pack \"6\")" 858 | ] 859 | }, 860 | { 861 | "cell_type": "code", 862 | "execution_count": 26, 863 | "metadata": { 864 | "collapsed": true 865 | }, 866 | "outputs": [], 867 | "source": [ 868 | "keywordUnbounded :: Parser String\n", 869 | "keywordUnbounded = do\n", 870 | " fchar <- keywordStart\n", 871 | " body <- many' keywordBody\n", 872 | " return (fchar : body)" 873 | ] 874 | }, 875 | { 876 | "cell_type": "code", 877 | "execution_count": 27, 878 | "metadata": { 879 | "collapsed": false 880 | }, 881 | "outputs": [ 882 | { 883 | "data": { 884 | "text/plain": [ 885 | "Right \"XTENSION\"" 886 | ] 887 | }, 888 | "metadata": {}, 889 | "output_type": "display_data" 890 | } 891 | ], 892 | "source": [ 893 | "parseOnly keywordUnbounded (B8.pack \"XTENSION= blah blah\")" 894 | ] 895 | }, 896 | { 897 | "cell_type": "code", 898 | "execution_count": 28, 899 | "metadata": { 900 | "collapsed": false 901 | }, 902 | "outputs": [ 903 | { 904 | "data": { 905 | "text/plain": [ 906 | "Right \"XTENSIONKEYWORD\"" 907 | ] 908 | }, 909 | "metadata": {}, 910 | "output_type": "display_data" 911 | } 912 | ], 913 | "source": [ 914 | "parseOnly keywordUnbounded (B8.pack \"XTENSIONKEYWORD = blah blah\")" 915 | ] 916 | }, 917 | { 918 | "cell_type": "code", 919 | "execution_count": 29, 920 | "metadata": { 921 | "collapsed": false 922 | }, 923 | "outputs": [], 924 | "source": [ 925 | "-- Parse 0 up to n copies of the parser, returning the results.\n", 926 | "--\n", 927 | "-- It is expected that the counter is 0 or more.\n", 928 | "upTo :: Int -> Parser a -> Parser [a]\n", 929 | "upTo n p | n < 1 = return []\n", 930 | " | otherwise = do\n", 931 | " mx <- optional p\n", 932 | " case mx of\n", 933 | " Just x -> do\n", 934 | " xs <- upTo (n-1) p\n", 935 | " return (x : xs)\n", 936 | " _ -> return []" 937 | ] 938 | }, 939 | { 940 | "cell_type": "code", 941 | "execution_count": 30, 942 | "metadata": { 943 | "collapsed": false 944 | }, 945 | "outputs": [ 946 | { 947 | "data": { 948 | "text/html": [ 949 | "optional :: forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)" 1046 | ], 1047 | "text/plain": [ 1048 | "optional :: forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)" 1049 | ] 1050 | }, 1051 | "metadata": {}, 1052 | "output_type": "display_data" 1053 | } 1054 | ], 1055 | "source": [ 1056 | ":type optional" 1057 | ] 1058 | }, 1059 | { 1060 | "cell_type": "code", 1061 | "execution_count": 31, 1062 | "metadata": { 1063 | "collapsed": false 1064 | }, 1065 | "outputs": [ 1066 | { 1067 | "data": { 1068 | "text/plain": [ 1069 | "Right \"XX\"" 1070 | ] 1071 | }, 1072 | "metadata": {}, 1073 | "output_type": "display_data" 1074 | } 1075 | ], 1076 | "source": [ 1077 | "parseOnly (upTo 3 (char 'X')) (B8.pack \"XX\")" 1078 | ] 1079 | }, 1080 | { 1081 | "cell_type": "code", 1082 | "execution_count": 32, 1083 | "metadata": { 1084 | "collapsed": false 1085 | }, 1086 | "outputs": [ 1087 | { 1088 | "data": { 1089 | "text/plain": [ 1090 | "Right \"XXX\"" 1091 | ] 1092 | }, 1093 | "metadata": {}, 1094 | "output_type": "display_data" 1095 | } 1096 | ], 1097 | "source": [ 1098 | "parseOnly (upTo 3 (char 'X')) (B8.pack \"XXXX X\")" 1099 | ] 1100 | }, 1101 | { 1102 | "cell_type": "code", 1103 | "execution_count": 33, 1104 | "metadata": { 1105 | "collapsed": false 1106 | }, 1107 | "outputs": [ 1108 | { 1109 | "data": { 1110 | "text/plain": [ 1111 | "Right \"\"" 1112 | ] 1113 | }, 1114 | "metadata": {}, 1115 | "output_type": "display_data" 1116 | } 1117 | ], 1118 | "source": [ 1119 | "parseOnly (upTo 3 (char 'X')) (B8.pack \"YXXX\")" 1120 | ] 1121 | }, 1122 | { 1123 | "cell_type": "code", 1124 | "execution_count": 34, 1125 | "metadata": { 1126 | "collapsed": false 1127 | }, 1128 | "outputs": [], 1129 | "source": [ 1130 | "-- Is keyword type useful here?\n", 1131 | "--\n", 1132 | "-- Ord is needed for the Map\n", 1133 | "newtype Keyword = Keyword String deriving (Eq, Ord, Show)\n", 1134 | "\n", 1135 | "-- Note: this parses the first 8 characters (i.e. requires\n", 1136 | "-- spaces after \"short\" keywords).\n", 1137 | "--\n", 1138 | "keyword :: Parser Keyword\n", 1139 | "keyword = do\n", 1140 | " fchar <- keywordStart\n", 1141 | " body <- upTo 7 keywordBody\n", 1142 | " let res = fchar : body\n", 1143 | " nres = length res\n", 1144 | " count (8-nres) space -- ignore the return value\n", 1145 | " return (Keyword (fchar : body))" 1146 | ] 1147 | }, 1148 | { 1149 | "cell_type": "code", 1150 | "execution_count": 35, 1151 | "metadata": { 1152 | "collapsed": false 1153 | }, 1154 | "outputs": [], 1155 | "source": [ 1156 | "testBS = (B8.take 40 (B8.drop 80 hdrBS)) -- want to show the remainder, and a < 8 char keyword\n", 1157 | "Done remainder kword = parse keyword testBS" 1158 | ] 1159 | }, 1160 | { 1161 | "cell_type": "code", 1162 | "execution_count": 36, 1163 | "metadata": { 1164 | "collapsed": false 1165 | }, 1166 | "outputs": [ 1167 | { 1168 | "data": { 1169 | "text/plain": [ 1170 | "\"BITPIX = 8 / 8-bit b\"" 1171 | ] 1172 | }, 1173 | "metadata": {}, 1174 | "output_type": "display_data" 1175 | } 1176 | ], 1177 | "source": [ 1178 | "testBS" 1179 | ] 1180 | }, 1181 | { 1182 | "cell_type": "code", 1183 | "execution_count": 37, 1184 | "metadata": { 1185 | "collapsed": false 1186 | }, 1187 | "outputs": [ 1188 | { 1189 | "data": { 1190 | "text/plain": [ 1191 | "Keyword \"BITPIX\"" 1192 | ] 1193 | }, 1194 | "metadata": {}, 1195 | "output_type": "display_data" 1196 | } 1197 | ], 1198 | "source": [ 1199 | "kword" 1200 | ] 1201 | }, 1202 | { 1203 | "cell_type": "code", 1204 | "execution_count": 38, 1205 | "metadata": { 1206 | "collapsed": false 1207 | }, 1208 | "outputs": [ 1209 | { 1210 | "data": { 1211 | "text/plain": [ 1212 | "\"= 8 / 8-bit b\"" 1213 | ] 1214 | }, 1215 | "metadata": {}, 1216 | "output_type": "display_data" 1217 | } 1218 | ], 1219 | "source": [ 1220 | "remainder" 1221 | ] 1222 | }, 1223 | { 1224 | "cell_type": "markdown", 1225 | "metadata": {}, 1226 | "source": [ 1227 | "What about values? Note: column lengths can be relaxed here (as there's a suggested fixed format but\n", 1228 | "can also be relaxed):\n", 1229 | "\n", 1230 | " - `'string' ...` where `''` is used to represent a\n", 1231 | " single quote character (there's also a minimum length of the string, since the trailing `'`\n", 1232 | " must be in column 20 or later, so trailing whitespace is ignored)\n", 1233 | " - ` T ...` (or `F`) - a boolean, which is in column 30\n", 1234 | " - ` n ...` - an integer\n", 1235 | " - ` f ...` - a floating-point value, which may or may not be in exponential form, but if so\n", 1236 | " uses `E`.\n", 1237 | "\n", 1238 | "where the numeric values end in column 30 (I am ignoring complex numbers, and the ... part should be `/...`)." 1239 | ] 1240 | }, 1241 | { 1242 | "cell_type": "code", 1243 | "execution_count": 39, 1244 | "metadata": { 1245 | "collapsed": true 1246 | }, 1247 | "outputs": [], 1248 | "source": [ 1249 | "-- Should this store an Integer rather than Int?\n", 1250 | "--\n", 1251 | "data KeyValue = KeyString String | KeyFloat Double | KeyInt Int | KeyBool Bool deriving (Eq, Show)" 1252 | ] 1253 | }, 1254 | { 1255 | "cell_type": "markdown", 1256 | "metadata": {}, 1257 | "source": [ 1258 | "In the following the `skipSpace` (before and handling of comments after) are left to the caller" 1259 | ] 1260 | }, 1261 | { 1262 | "cell_type": "code", 1263 | "execution_count": 40, 1264 | "metadata": { 1265 | "collapsed": false 1266 | }, 1267 | "outputs": [], 1268 | "source": [ 1269 | "-- | Remove trailing space characters\n", 1270 | "rstrip :: B8.ByteString -> B8.ByteString\n", 1271 | "rstrip = go\n", 1272 | " where\n", 1273 | " go bs = case B8.unsnoc bs of\n", 1274 | " Just (rbs,c) | c == ' ' -> go rbs\n", 1275 | " _ -> bs" 1276 | ] 1277 | }, 1278 | { 1279 | "cell_type": "code", 1280 | "execution_count": 41, 1281 | "metadata": { 1282 | "collapsed": false 1283 | }, 1284 | "outputs": [], 1285 | "source": [ 1286 | "stringVal :: Parser KeyValue\n", 1287 | "stringVal = do\n", 1288 | " let quote = '\\''\n", 1289 | " char quote\n", 1290 | " body <- takeTill (== quote)\n", 1291 | " return (KeyString (B8.unpack (rstrip body)))" 1292 | ] 1293 | }, 1294 | { 1295 | "cell_type": "code", 1296 | "execution_count": 42, 1297 | "metadata": { 1298 | "collapsed": false 1299 | }, 1300 | "outputs": [], 1301 | "source": [ 1302 | "boolVal :: Parser KeyValue\n", 1303 | "boolVal = \n", 1304 | " let true = char 'T' *> return (KeyBool True)\n", 1305 | " false = char 'F' *> return (KeyBool False)\n", 1306 | " in true <|> false" 1307 | ] 1308 | }, 1309 | { 1310 | "cell_type": "code", 1311 | "execution_count": 43, 1312 | "metadata": { 1313 | "collapsed": false 1314 | }, 1315 | "outputs": [ 1316 | { 1317 | "data": { 1318 | "text/plain": [ 1319 | "Right (KeyBool True)" 1320 | ] 1321 | }, 1322 | "metadata": {}, 1323 | "output_type": "display_data" 1324 | }, 1325 | { 1326 | "data": { 1327 | "text/plain": [ 1328 | "Right (KeyBool False)" 1329 | ] 1330 | }, 1331 | "metadata": {}, 1332 | "output_type": "display_data" 1333 | }, 1334 | { 1335 | "data": { 1336 | "text/plain": [ 1337 | "Left \"Failed reading: satisfyWith\"" 1338 | ] 1339 | }, 1340 | "metadata": {}, 1341 | "output_type": "display_data" 1342 | } 1343 | ], 1344 | "source": [ 1345 | "parseOnly boolVal (B8.pack \"T \")\n", 1346 | "parseOnly boolVal (B8.pack \"F\")\n", 1347 | "parseOnly boolVal (B8.pack \"'T'\")" 1348 | ] 1349 | }, 1350 | { 1351 | "cell_type": "markdown", 1352 | "metadata": {}, 1353 | "source": [ 1354 | "Use\n", 1355 | "[`floatingOrInteger`](http://hackage.haskell.org/package/scientific-0.3.3.8/docs/Data-Scientific.html#v:floatingOrInteger) \n", 1356 | "from the\n", 1357 | "[`scientific`](http://hackage.haskell.org/package/scientific)\n", 1358 | "package -- Nopity nope nope since it takes 1.23E23 as an integer." 1359 | ] 1360 | }, 1361 | { 1362 | "cell_type": "code", 1363 | "execution_count": 44, 1364 | "metadata": { 1365 | "collapsed": false 1366 | }, 1367 | "outputs": [], 1368 | "source": [ 1369 | "intVal1 :: Parser KeyValue\n", 1370 | "intVal1 = do\n", 1371 | " ans <- signed decimal\n", 1372 | " return (KeyInt ans)\n", 1373 | " \n", 1374 | "intVal :: Parser KeyValue\n", 1375 | "intVal = KeyInt <$> signed decimal" 1376 | ] 1377 | }, 1378 | { 1379 | "cell_type": "code", 1380 | "execution_count": 45, 1381 | "metadata": { 1382 | "collapsed": false 1383 | }, 1384 | "outputs": [ 1385 | { 1386 | "data": { 1387 | "text/plain": [ 1388 | "Right (KeyInt 123)" 1389 | ] 1390 | }, 1391 | "metadata": {}, 1392 | "output_type": "display_data" 1393 | }, 1394 | { 1395 | "data": { 1396 | "text/plain": [ 1397 | "Right (KeyInt 123)" 1398 | ] 1399 | }, 1400 | "metadata": {}, 1401 | "output_type": "display_data" 1402 | }, 1403 | { 1404 | "data": { 1405 | "text/plain": [ 1406 | "Right (KeyInt (-123))" 1407 | ] 1408 | }, 1409 | "metadata": {}, 1410 | "output_type": "display_data" 1411 | } 1412 | ], 1413 | "source": [ 1414 | "parseOnly intVal (B8.pack \"123\")\n", 1415 | "parseOnly intVal (B8.pack \"123.45\") \n", 1416 | "parseOnly intVal (B8.pack \"-123\")" 1417 | ] 1418 | }, 1419 | { 1420 | "cell_type": "code", 1421 | "execution_count": 46, 1422 | "metadata": { 1423 | "collapsed": false 1424 | }, 1425 | "outputs": [], 1426 | "source": [ 1427 | "-- Support E+23 or E-23\n", 1428 | "exponential :: Parser Int\n", 1429 | "exponential = char 'E' *> signed decimal\n", 1430 | "\n", 1431 | "-- this does not support 1E+23, it needs to be X.Y...\n", 1432 | "floatVal :: Parser KeyValue\n", 1433 | "floatVal = do\n", 1434 | " msign <- optional (char '-' <|> char '+')\n", 1435 | " ldigs <- many1' digit\n", 1436 | " char '.'\n", 1437 | " rdigs <- many1' digit\n", 1438 | " mexp <- optional exponential\n", 1439 | " let f = read (ldigs ++ '.' : rdigs)\n", 1440 | " g = case mexp of\n", 1441 | " Just eval | eval > 0 -> f * 10 ^ eval\n", 1442 | " | eval < 0 -> f / 10 ^ (abs eval)\n", 1443 | " | otherwise -> f\n", 1444 | " _ -> f\n", 1445 | " \n", 1446 | " s = case msign of\n", 1447 | " Just '-' -> -1\n", 1448 | " _ -> 1\n", 1449 | " \n", 1450 | " return (KeyFloat (s*g))" 1451 | ] 1452 | }, 1453 | { 1454 | "cell_type": "code", 1455 | "execution_count": 47, 1456 | "metadata": { 1457 | "collapsed": false 1458 | }, 1459 | "outputs": [ 1460 | { 1461 | "data": { 1462 | "text/plain": [ 1463 | "Right (KeyFloat 1.23e-43)" 1464 | ] 1465 | }, 1466 | "metadata": {}, 1467 | "output_type": "display_data" 1468 | }, 1469 | { 1470 | "data": { 1471 | "text/plain": [ 1472 | "Right (KeyFloat (-0.1))" 1473 | ] 1474 | }, 1475 | "metadata": {}, 1476 | "output_type": "display_data" 1477 | }, 1478 | { 1479 | "data": { 1480 | "text/plain": [ 1481 | "Left \"not enough input\"" 1482 | ] 1483 | }, 1484 | "metadata": {}, 1485 | "output_type": "display_data" 1486 | } 1487 | ], 1488 | "source": [ 1489 | "parseOnly floatVal (B8.pack \"1.23E-43\")\n", 1490 | "parseOnly floatVal (B8.pack \"-0.0001E3\")\n", 1491 | "parseOnly floatVal (B8.pack \"123\")" 1492 | ] 1493 | }, 1494 | { 1495 | "cell_type": "code", 1496 | "execution_count": 48, 1497 | "metadata": { 1498 | "collapsed": false 1499 | }, 1500 | "outputs": [], 1501 | "source": [ 1502 | "value :: Parser KeyValue\n", 1503 | "value = stringVal <|> boolVal <|> floatVal <|> intVal" 1504 | ] 1505 | }, 1506 | { 1507 | "cell_type": "code", 1508 | "execution_count": 49, 1509 | "metadata": { 1510 | "collapsed": false 1511 | }, 1512 | "outputs": [ 1513 | { 1514 | "data": { 1515 | "text/plain": [ 1516 | "[Right (KeyString \"A string\"),Right (KeyBool True),Right (KeyInt 123),Right (KeyFloat (-1.23e47))]" 1517 | ] 1518 | }, 1519 | "metadata": {}, 1520 | "output_type": "display_data" 1521 | } 1522 | ], 1523 | "source": [ 1524 | "map (parseOnly value . B8.pack) [\"'A string ' \", \"T\", \"123\", \"-1.23E+47\"]" 1525 | ] 1526 | }, 1527 | { 1528 | "cell_type": "code", 1529 | "execution_count": 50, 1530 | "metadata": { 1531 | "collapsed": false 1532 | }, 1533 | "outputs": [], 1534 | "source": [ 1535 | "import Data.Maybe (fromMaybe)\n", 1536 | "\n", 1537 | "-- Handle any description. I could also parse out any unit, but leave that for now.\n", 1538 | "comment :: Parser String\n", 1539 | "comment = do\n", 1540 | " skipSpace\n", 1541 | " mcbs <- optional (char '/' >> skipSpace >> many' anyChar)\n", 1542 | " -- TODO: strip trailing white space\n", 1543 | " return (fromMaybe \"\" mcbs)" 1544 | ] 1545 | }, 1546 | { 1547 | "cell_type": "code", 1548 | "execution_count": 51, 1549 | "metadata": { 1550 | "collapsed": false 1551 | }, 1552 | "outputs": [], 1553 | "source": [ 1554 | "-- Should the 80-character limitation be tracked by the parser, or can we split into chunks\n", 1555 | "-- beforehand?\n", 1556 | "card :: Parser (Keyword, KeyValue)\n", 1557 | "card = do\n", 1558 | " key <- keyword\n", 1559 | " -- have to have \"= \" but then need to also eat up any spaces\n", 1560 | " -- before the value.\n", 1561 | " char '=' >> many1' space\n", 1562 | " val <- value\n", 1563 | " _ <- comment\n", 1564 | " return (key, val)" 1565 | ] 1566 | }, 1567 | { 1568 | "cell_type": "code", 1569 | "execution_count": 52, 1570 | "metadata": { 1571 | "collapsed": false 1572 | }, 1573 | "outputs": [ 1574 | { 1575 | "data": { 1576 | "text/plain": [ 1577 | "\"XTENSION= 'BINTABLE' / binary table extension \"" 1578 | ] 1579 | }, 1580 | "metadata": {}, 1581 | "output_type": "display_data" 1582 | }, 1583 | { 1584 | "data": { 1585 | "text/plain": [ 1586 | "\"BITPIX = 8 / 8-bit bytes \"" 1587 | ] 1588 | }, 1589 | "metadata": {}, 1590 | "output_type": "display_data" 1591 | }, 1592 | { 1593 | "data": { 1594 | "text/plain": [ 1595 | "Right (Keyword \"XTENSION\",KeyString \"BINTABLE\")" 1596 | ] 1597 | }, 1598 | "metadata": {}, 1599 | "output_type": "display_data" 1600 | }, 1601 | { 1602 | "data": { 1603 | "text/plain": [ 1604 | "Right (Keyword \"BITPIX\",KeyInt 8)" 1605 | ] 1606 | }, 1607 | "metadata": {}, 1608 | "output_type": "display_data" 1609 | } 1610 | ], 1611 | "source": [ 1612 | "splitBS hdrBS 0 80\n", 1613 | "splitBS hdrBS 80 80\n", 1614 | "parseOnly card (splitBS hdrBS 0 80)\n", 1615 | "parseOnly card (splitBS hdrBS 80 80)" 1616 | ] 1617 | }, 1618 | { 1619 | "cell_type": "code", 1620 | "execution_count": 53, 1621 | "metadata": { 1622 | "collapsed": true 1623 | }, 1624 | "outputs": [], 1625 | "source": [ 1626 | "import Data.List (unfoldr)" 1627 | ] 1628 | }, 1629 | { 1630 | "cell_type": "code", 1631 | "execution_count": 54, 1632 | "metadata": { 1633 | "collapsed": false 1634 | }, 1635 | "outputs": [], 1636 | "source": [ 1637 | "getCards :: B8.ByteString -> [B8.ByteString]\n", 1638 | "getCards = unfoldr go\n", 1639 | " where\n", 1640 | " go bs | B8.null bs = Nothing\n", 1641 | " | otherwise = Just (B8.splitAt 80 bs)\n", 1642 | " \n", 1643 | "hdrCards = getCards hdrBS" 1644 | ] 1645 | }, 1646 | { 1647 | "cell_type": "code", 1648 | "execution_count": 55, 1649 | "metadata": { 1650 | "collapsed": false 1651 | }, 1652 | "outputs": [ 1653 | { 1654 | "data": { 1655 | "text/plain": [ 1656 | "Right (Keyword \"XTENSION\",KeyString \"BINTABLE\")" 1657 | ] 1658 | }, 1659 | "metadata": {}, 1660 | "output_type": "display_data" 1661 | } 1662 | ], 1663 | "source": [ 1664 | "parseOnly card (head hdrCards)" 1665 | ] 1666 | }, 1667 | { 1668 | "cell_type": "code", 1669 | "execution_count": 56, 1670 | "metadata": { 1671 | "collapsed": false 1672 | }, 1673 | "outputs": [ 1674 | { 1675 | "data": { 1676 | "text/plain": [ 1677 | "[Right (Keyword \"XTENSION\",KeyString \"BINTABLE\"),Right (Keyword \"BITPIX\",KeyInt 8),Right (Keyword \"NAXIS\",KeyInt 2),Right (Keyword \"NAXIS1\",KeyInt 12),Right (Keyword \"NAXIS2\",KeyInt 1070)]" 1678 | ] 1679 | }, 1680 | "metadata": {}, 1681 | "output_type": "display_data" 1682 | } 1683 | ], 1684 | "source": [ 1685 | "map (parseOnly card) (take 5 hdrCards)" 1686 | ] 1687 | }, 1688 | { 1689 | "cell_type": "code", 1690 | "execution_count": 57, 1691 | "metadata": { 1692 | "collapsed": false 1693 | }, 1694 | "outputs": [ 1695 | { 1696 | "data": { 1697 | "text/plain": [ 1698 | "Right [(Keyword \"XTENSION\",KeyString \"BINTABLE\"),(Keyword \"BITPIX\",KeyInt 8),(Keyword \"NAXIS\",KeyInt 2),(Keyword \"NAXIS1\",KeyInt 12),(Keyword \"NAXIS2\",KeyInt 1070)]" 1699 | ] 1700 | }, 1701 | "metadata": {}, 1702 | "output_type": "display_data" 1703 | } 1704 | ], 1705 | "source": [ 1706 | "mapM (parseOnly card) (take 5 hdrCards)" 1707 | ] 1708 | }, 1709 | { 1710 | "cell_type": "code", 1711 | "execution_count": 58, 1712 | "metadata": { 1713 | "collapsed": false 1714 | }, 1715 | "outputs": [ 1716 | { 1717 | "data": { 1718 | "text/plain": [ 1719 | "Left \"Failed reading: satisfyWith\"" 1720 | ] 1721 | }, 1722 | "metadata": {}, 1723 | "output_type": "display_data" 1724 | } 1725 | ], 1726 | "source": [ 1727 | "mapM (parseOnly card) hdrCards" 1728 | ] 1729 | }, 1730 | { 1731 | "cell_type": "code", 1732 | "execution_count": 59, 1733 | "metadata": { 1734 | "collapsed": false 1735 | }, 1736 | "outputs": [ 1737 | { 1738 | "data": { 1739 | "text/plain": [ 1740 | "(\"HISTORY TOOL :mkwarf 2014-11-26T05:38:12 ASC00001\",Left \"Failed reading: satisfyWith\")" 1741 | ] 1742 | }, 1743 | "metadata": {}, 1744 | "output_type": "display_data" 1745 | } 1746 | ], 1747 | "source": [ 1748 | "import Data.Either (isLeft)\n", 1749 | "\n", 1750 | "-- find the first card that fails (returns a Left value)\n", 1751 | "head (filter (isLeft . snd) (zip hdrCards (map (parseOnly card) hdrCards)))" 1752 | ] 1753 | }, 1754 | { 1755 | "cell_type": "markdown", 1756 | "metadata": {}, 1757 | "source": [ 1758 | "So, good types can only take you so far in protecting yourself against errors in your data model." 1759 | ] 1760 | }, 1761 | { 1762 | "cell_type": "code", 1763 | "execution_count": 60, 1764 | "metadata": { 1765 | "collapsed": false 1766 | }, 1767 | "outputs": [], 1768 | "source": [ 1769 | "import Control.Monad (void)\n", 1770 | "\n", 1771 | "-- Skip blank lines, END lines, and those that are \"metadata\" lines (e.g. HISTORY/COMMENT)\n", 1772 | "fixedCard :: Parser (Maybe (Keyword, KeyValue))\n", 1773 | "fixedCard = \n", 1774 | " (do\n", 1775 | " key <- keyword\n", 1776 | " mchar <- optional (char '=')\n", 1777 | " case mchar of\n", 1778 | " Just _ -> do\n", 1779 | " many1' space\n", 1780 | " val <- value\n", 1781 | " void comment\n", 1782 | " return (Just (key, val))\n", 1783 | " \n", 1784 | " _ -> many' anyChar >> return Nothing\n", 1785 | " )\n", 1786 | " <|>\n", 1787 | " (string (B8.pack \"END\") >> skipSpace >> return Nothing)\n", 1788 | " <|>\n", 1789 | " (skipSpace >> return Nothing)" 1790 | ] 1791 | }, 1792 | { 1793 | "cell_type": "code", 1794 | "execution_count": 61, 1795 | "metadata": { 1796 | "collapsed": false 1797 | }, 1798 | "outputs": [ 1799 | { 1800 | "data": { 1801 | "text/html": [ 1802 | "Prelude.head: empty list" 1899 | ], 1900 | "text/plain": [ 1901 | "Prelude.head: empty list" 1902 | ] 1903 | }, 1904 | "metadata": {}, 1905 | "output_type": "display_data" 1906 | } 1907 | ], 1908 | "source": [ 1909 | "head (filter (isLeft . snd) (zip hdrCards (map (parseOnly fixedCard) hdrCards)))" 1910 | ] 1911 | }, 1912 | { 1913 | "cell_type": "code", 1914 | "execution_count": 62, 1915 | "metadata": { 1916 | "collapsed": false 1917 | }, 1918 | "outputs": [], 1919 | "source": [ 1920 | "import qualified Data.Map as Map\n", 1921 | "import Data.Maybe (catMaybes)\n", 1922 | "\n", 1923 | "Right mcs = mapM (parseOnly fixedCard) hdrCards\n", 1924 | "hdrMap = Map.fromList (catMaybes mcs)" 1925 | ] 1926 | }, 1927 | { 1928 | "cell_type": "code", 1929 | "execution_count": 63, 1930 | "metadata": { 1931 | "collapsed": false 1932 | }, 1933 | "outputs": [ 1934 | { 1935 | "data": { 1936 | "text/plain": [ 1937 | "Just (KeyString \"3C273-JET\")" 1938 | ] 1939 | }, 1940 | "metadata": {}, 1941 | "output_type": "display_data" 1942 | } 1943 | ], 1944 | "source": [ 1945 | "Map.lookup (Keyword \"OBJECT\") hdrMap" 1946 | ] 1947 | }, 1948 | { 1949 | "cell_type": "markdown", 1950 | "metadata": {}, 1951 | "source": [ 1952 | "****************************************************************" 1953 | ] 1954 | }, 1955 | { 1956 | "cell_type": "code", 1957 | "execution_count": 64, 1958 | "metadata": { 1959 | "collapsed": true 1960 | }, 1961 | "outputs": [], 1962 | "source": [ 1963 | "-- import qualified Data.Sequence as Seq -- no longer using" 1964 | ] 1965 | }, 1966 | { 1967 | "cell_type": "code", 1968 | "execution_count": 65, 1969 | "metadata": { 1970 | "collapsed": false 1971 | }, 1972 | "outputs": [], 1973 | "source": [ 1974 | "-- Represent a binary table\n", 1975 | "--\n", 1976 | "-- Could the bitpix be a phantom argument ? Does this even make sense?\n", 1977 | "--\n", 1978 | "-- Assumes:\n", 1979 | "-- XTENSION=BINTABLE, NAXIS=2, PCOUNT=0, GCOUNT=1\n", 1980 | "--\n", 1981 | "-- Stores (explicitly or implicitly):\n", 1982 | "-- BITPIX, NAXIS1, NAXIS2, TFIELDS\n", 1983 | "-- TTYPE/TFORM/TUNIT values for columns\n", 1984 | "--\n", 1985 | "data TableExt = TableExt {\n", 1986 | " _teBitPix :: Int\n", 1987 | " , _teNRows :: Int\n", 1988 | " , _teNCols :: Int\n", 1989 | " , _teWidth :: Int -- width, in bytes, of each row\n", 1990 | " , _teCols :: [ColData] -- would be nice to statically enforce size == _teNCols\n", 1991 | " } deriving Show\n", 1992 | "\n", 1993 | "-- This stores the TTYPEn, TFORNm, and TUNITn keywords. Would it be\n", 1994 | "-- useful to encode the Haskell type for the data in the type?\n", 1995 | "--\n", 1996 | "-- At present the description/comment for the field is not included\n", 1997 | "data ColData = ColData {\n", 1998 | " _cdName :: String\n", 1999 | " , _cdFormat :: String -- the FITS encoding of the data type\n", 2000 | " , _cdUnit :: Maybe String\n", 2001 | " } deriving (Eq, Show)" 2002 | ] 2003 | }, 2004 | { 2005 | "cell_type": "code", 2006 | "execution_count": 66, 2007 | "metadata": { 2008 | "collapsed": true 2009 | }, 2010 | "outputs": [], 2011 | "source": [ 2012 | "import Control.Monad (forM, unless)\n", 2013 | "import Data.Either (either)" 2014 | ] 2015 | }, 2016 | { 2017 | "cell_type": "code", 2018 | "execution_count": 67, 2019 | "metadata": { 2020 | "collapsed": false 2021 | }, 2022 | "outputs": [], 2023 | "source": [ 2024 | "-- Note that a function is sent in, rather than the map. \n", 2025 | "createColumn :: (String -> Either String String) -> Int -> Either String ColData \n", 2026 | "createColumn getS c = do\n", 2027 | " let cs = show c\n", 2028 | " cName <- getS (\"TTYPE\" ++ cs)\n", 2029 | " cForm <- getS (\"TFORM\" ++ cs)\n", 2030 | " \n", 2031 | " -- Need to swap from Either to Maybe; note that I do not want to\n", 2032 | " -- use cUnit <- getS ... otherwise the whole computation would\n", 2033 | " -- fail if no unit was given.\n", 2034 | " let cUnit = either (const Nothing) Just (getS (\"TUNIT\" ++ cs))\n", 2035 | " \n", 2036 | " Right ColData { _cdName = cName, _cdFormat = cForm, _cdUnit = cUnit }\n", 2037 | "\n", 2038 | "getKeyValue :: Map.Map Keyword KeyValue -> String -> Either String KeyValue\n", 2039 | "getKeyValue xs kn = \n", 2040 | " let err = \"Key not found: \" ++ kn\n", 2041 | " in maybe (Left err) Right (Map.lookup (Keyword kn) xs) \n", 2042 | "\n", 2043 | "createTable :: Map.Map Keyword KeyValue -> Either String TableExt\n", 2044 | "createTable m = do\n", 2045 | " let get = getKeyValue m\n", 2046 | " \n", 2047 | " asString _ (KeyString s) = Right s\n", 2048 | " asString k v = Left (k ++ \" not a string: \" ++ show v)\n", 2049 | " \n", 2050 | " asInt _ (KeyInt i) = Right i\n", 2051 | " asInt k v = Left (k ++ \" not an int: \" ++ show v)\n", 2052 | " \n", 2053 | " -- extract typed info \n", 2054 | " getS k = get k >>= asString k\n", 2055 | " getI k = get k >>= asInt k\n", 2056 | " \n", 2057 | " ext <- getS \"XTENSION\"\n", 2058 | " naxis <- getI \"NAXIS\"\n", 2059 | " pcount <- getI \"PCOUNT\"\n", 2060 | " gcount <- getI \"GCOUNT\"\n", 2061 | " let conds = [ ext == \"BINTABLE\"\n", 2062 | " , naxis == 2\n", 2063 | " , pcount == 0\n", 2064 | " , gcount == 1 ]\n", 2065 | " unless (and conds) (Left (\"invalid conditions: \" ++ show conds))\n", 2066 | " \n", 2067 | " bpix <- getI \"BITPIX\"\n", 2068 | " nbytes <- getI \"NAXIS1\"\n", 2069 | " nrows <- getI \"NAXIS2\"\n", 2070 | " ncols <- getI \"TFIELDS\"\n", 2071 | "\n", 2072 | " -- Here cols is Either String [ColData], where the\n", 2073 | " -- string will represent the error from the fist\n", 2074 | " -- column which failed.\n", 2075 | " --\n", 2076 | " cols <- forM [1..ncols] $ createColumn getS \n", 2077 | " let lc = length cols\n", 2078 | " unless (lc == ncols) (Left (\"Expected \" ++ show ncols ++ \" columns, found \" ++ show lc))\n", 2079 | " \n", 2080 | " Right TableExt {\n", 2081 | " _teBitPix = bpix\n", 2082 | " , _teNCols = ncols\n", 2083 | " , _teNRows = nrows\n", 2084 | " , _teWidth = nbytes\n", 2085 | " , _teCols = cols\n", 2086 | " } " 2087 | ] 2088 | }, 2089 | { 2090 | "cell_type": "code", 2091 | "execution_count": 68, 2092 | "metadata": { 2093 | "collapsed": false 2094 | }, 2095 | "outputs": [ 2096 | { 2097 | "data": { 2098 | "text/plain": [ 2099 | "Right (TableExt {_teBitPix = 8, _teNRows = 1070, _teNCols = 3, _teWidth = 12, _teCols = [ColData {_cdName = \"ENERG_LO\", _cdFormat = \"1E\", _cdUnit = Just \"keV\"},ColData {_cdName = \"ENERG_HI\", _cdFormat = \"1E\", _cdUnit = Just \"keV\"},ColData {_cdName = \"SPECRESP\", _cdFormat = \"1E\", _cdUnit = Just \"cm**2\"}]})" 2100 | ] 2101 | }, 2102 | "metadata": {}, 2103 | "output_type": "display_data" 2104 | } 2105 | ], 2106 | "source": [ 2107 | "createTable hdrMap" 2108 | ] 2109 | }, 2110 | { 2111 | "cell_type": "markdown", 2112 | "metadata": { 2113 | "collapsed": true 2114 | }, 2115 | "source": [ 2116 | "Now, the question is how to use this to parse the data: i.e. create a parser." 2117 | ] 2118 | }, 2119 | { 2120 | "cell_type": "code", 2121 | "execution_count": 85, 2122 | "metadata": { 2123 | "collapsed": false 2124 | }, 2125 | "outputs": [], 2126 | "source": [ 2127 | "wrongMap = Map.insert (Keyword \"TFIELDS\") (KeyInt 4) hdrMap" 2128 | ] 2129 | }, 2130 | { 2131 | "cell_type": "code", 2132 | "execution_count": 86, 2133 | "metadata": { 2134 | "collapsed": false 2135 | }, 2136 | "outputs": [ 2137 | { 2138 | "data": { 2139 | "text/plain": [ 2140 | "Just (KeyInt 4)" 2141 | ] 2142 | }, 2143 | "metadata": {}, 2144 | "output_type": "display_data" 2145 | } 2146 | ], 2147 | "source": [ 2148 | "Map.lookup (Keyword \"TFIELDS\") wrongMap" 2149 | ] 2150 | }, 2151 | { 2152 | "cell_type": "code", 2153 | "execution_count": 87, 2154 | "metadata": { 2155 | "collapsed": false 2156 | }, 2157 | "outputs": [ 2158 | { 2159 | "data": { 2160 | "text/plain": [ 2161 | "Left \"Key not found: TTYPE4\"" 2162 | ] 2163 | }, 2164 | "metadata": {}, 2165 | "output_type": "display_data" 2166 | } 2167 | ], 2168 | "source": [ 2169 | "createTable wrongMap" 2170 | ] 2171 | }, 2172 | { 2173 | "cell_type": "markdown", 2174 | "metadata": {}, 2175 | "source": [ 2176 | "The original map is unchanged:" 2177 | ] 2178 | }, 2179 | { 2180 | "cell_type": "code", 2181 | "execution_count": 88, 2182 | "metadata": { 2183 | "collapsed": false 2184 | }, 2185 | "outputs": [ 2186 | { 2187 | "data": { 2188 | "text/plain": [ 2189 | "Just (KeyInt 3)" 2190 | ] 2191 | }, 2192 | "metadata": {}, 2193 | "output_type": "display_data" 2194 | } 2195 | ], 2196 | "source": [ 2197 | "Map.lookup (Keyword \"TFIELDS\") hdrMap" 2198 | ] 2199 | }, 2200 | { 2201 | "cell_type": "code", 2202 | "execution_count": null, 2203 | "metadata": { 2204 | "collapsed": true 2205 | }, 2206 | "outputs": [], 2207 | "source": [] 2208 | } 2209 | ], 2210 | "metadata": { 2211 | "kernelspec": { 2212 | "display_name": "Haskell", 2213 | "language": "haskell", 2214 | "name": "haskell" 2215 | }, 2216 | "language_info": { 2217 | "name": "haskell", 2218 | "version": "7.8.4" 2219 | } 2220 | }, 2221 | "nbformat": 4, 2222 | "nbformat_minor": 0 2223 | } 2224 | -------------------------------------------------------------------------------- /finished/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Astronomy and Haskell 3 | 4 | These are finished [IHaskell 5 | notebooks](http://gibiansky.github.io/IHaskell/) showing examples of 6 | using [Haskell](https://www.haskell.org/) for Astronomy. The notebooks 7 | in this directory contain both input and output cells, so you should 8 | be able to use a service like [nbviewer](http://nbviewer.ipython.org/) 9 | to view them. 10 | 11 | Please see the [top-level 12 | README](https://github.com/DougBurke/astro-haskell/blob/master/README.md) 13 | for more information, including links to the HTML and "raw/clean" 14 | versions. 15 | 16 | The first two notebooks - "angular diameter distance" and "this time 17 | with units" - were stored in this directory after the HTML version was 18 | created, which means that the dates in the "Last time the notebook was 19 | run" section do not match. 20 | -------------------------------------------------------------------------------- /html/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Astronomy and Haskell 3 | 4 | These are the HTML version of my [IHaskell 5 | notebooks](http://gibiansky.github.io/IHaskell/) showing examples of 6 | using [Haskell](https://www.haskell.org/) for Astronomy. 7 | 8 | Please see the [top-level 9 | README](https://github.com/DougBurke/astro-haskell/blob/master/README.md) 10 | for more information, including links to viewers for these pages. 11 | 12 | -------------------------------------------------------------------------------- /notebooks/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Astronomy and Haskell 3 | 4 | These are finished and in-development [IHaskell 5 | notebooks](http://gibiansky.github.io/IHaskell/) showing examples of 6 | using [Haskell](https://www.haskell.org/) for Astronomy. I am 7 | currently only storing the input values - i.e. I have cleared the 8 | notebook cells of their output - to make it easier for me to see how 9 | things have changed. 10 | 11 | Please see the [top-level 12 | README](https://github.com/DougBurke/astro-haskell/blob/master/README.md) 13 | for more information, including links to the HTML and "finished" 14 | (i.e. the output cells are included) versions. 15 | 16 | -------------------------------------------------------------------------------- /notebooks/this time with units.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "language": "haskell", 4 | "name": "", 5 | "signature": "sha256:c11259ee370904bddf149fb5f2cab13a9e0af71006716d98efad834b109677aa" 6 | }, 7 | "nbformat": 3, 8 | "nbformat_minor": 0, 9 | "worksheets": [ 10 | { 11 | "cells": [ 12 | { 13 | "cell_type": "markdown", 14 | "metadata": { 15 | "hidden": false 16 | }, 17 | "source": [ 18 | "The information presented here is placed in the public domain, and was written by\n", 19 | "[Doug Burke](https://plus.google.com/+DougBurke). The \n", 20 | "[notebook](https://github.com/DougBurke/astro-haskell/blob/master/notebooks/this%20time%20with%20units.ipynb)\n", 21 | "used to create this page is available, and questions can be\n", 22 | "asked using the\n", 23 | "[GitHub issues page](https://github.com/DougBurke/astro-haskell/issues)\n", 24 | "or via Twitter: .\n", 25 | "\n", 26 | "It will probably make a lot-more sense if you have already read the\n", 27 | "[previous entry](http://htmlpreview.github.io/?https://raw.githubusercontent.com/DougBurke/astro-haskell/master/html/angular%20diameter%20distance.html)\n", 28 | "in this poorly-defined series (if you can call two notebooks a series)." 29 | ] 30 | }, 31 | { 32 | "cell_type": "heading", 33 | "level": 1, 34 | "metadata": { 35 | "hidden": false 36 | }, 37 | "source": [ 38 | "What am I up to now?" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": { 44 | "hidden": false 45 | }, 46 | "source": [ 47 | "The whole point of the first notebook was to try out the cool-looking\n", 48 | "[units package](https://hackage.haskell.org/package/units), motivated by\n", 49 | "the paper\n", 50 | "[\"Experience Report: Type-checking Polymorphic Units for Astrophysics Research in Haskell\"](http://www.cis.upenn.edu/~eir/papers/2014/units/units.pdf)\n", 51 | "by Takayuki Muranushi and Richard A. Eisenberg (Haskell Symposium 2014, Gothenburg, Sweden).\n", 52 | "However, I couldn't install the package on the system-installed version\n", 53 | "of [`ghc`](https://www.haskell.org/ghc/) on my laptop, so in the interest of people who may be trying to follow\n", 54 | "along at home, I stuck with version 7.6.3 of `ghc` and used the\n", 55 | "[dimensional-tf package](https://hackahe.haskell.org/package/dimensional-tf)\n", 56 | "instead. As I do have newer versions of `ghc` on this laptop, I can also try out\n", 57 | "`units` at the possible expense of making it harder for people to replicate the notebook:\n", 58 | "\n", 59 | " - switch to version 7.8.4 of `ghc`\n", 60 | " \n", 61 | " - in a new directory, create a new cabal sandbox; e.g.\n", 62 | " \n", 63 | " % cabal sandbox init\n", 64 | " % cabal install ihaskell units-defs integration --dry-run\n", 65 | " % cabal install ihaskell units-defs integration\n", 66 | " ...\n", 67 | " \n", 68 | " - remove the existing ihaskell configuration directories\n", 69 | " \n", 70 | " % rm -rf ~/.ihaskell\n", 71 | " % rm -rf ~/.ipython/profile_haskell\n", 72 | " \n", 73 | " I did this because without it the IHaskell kernel would fail with\n", 74 | " the message\n", 75 | " \n", 76 | " IHaskell: Bad interface file: /home/dburke/local/ghc-7.8.4/lib/ghc-7.8.4/base-4.7.0.2/Prelude.hi\n", 77 | " mismatched interface file versions (wanted \"7063\", got \"7084\")\n", 78 | "\n", 79 | " - start the IHaskell notebook with\n", 80 | " \n", 81 | " % mkdir notebooks\n", 82 | " % cabal exec IHaskell -- notebook --serve=notebooks/" 83 | ] 84 | }, 85 | { 86 | "cell_type": "heading", 87 | "level": 2, 88 | "metadata": { 89 | "hidden": false 90 | }, 91 | "source": [ 92 | "Last time the notebook was run" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "collapsed": false, 98 | "input": [ 99 | "import Data.Time\n", 100 | "getCurrentTime" 101 | ], 102 | "language": "python", 103 | "metadata": {}, 104 | "outputs": [] 105 | }, 106 | { 107 | "cell_type": "heading", 108 | "level": 2, 109 | "metadata": { 110 | "hidden": false 111 | }, 112 | "source": [ 113 | "Load the modules" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": { 119 | "hidden": false 120 | }, 121 | "source": [ 122 | "I need to turn on a few \n", 123 | "[GHC extensions](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghc-language-features.html)\n", 124 | "to use the `units` code:" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "collapsed": false, 130 | "input": [ 131 | "-- TypeFamilies is needed when defining Unit instances\n", 132 | ":set -XTypeFamilies\n", 133 | "\n", 134 | "-- TypeOperators is needed for defining the EnergyFlux type\n", 135 | ":set -XTypeOperators\n", 136 | "\n", 137 | "-- DataKinds is needed for defining AngleDim and Radians\n", 138 | ":set -XDataKinds" 139 | ], 140 | "language": "python", 141 | "metadata": {}, 142 | "outputs": [] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": { 147 | "hidden": false 148 | }, 149 | "source": [ 150 | "Unlike \n", 151 | "[last time](http://htmlpreview.github.io/?https://raw.githubusercontent.com/DougBurke/astro-haskell/master/html/angular%20diameter%20distance.html),\n", 152 | "I'm not going to create any plots, so I don't need the Chart modules.\n", 153 | "\n", 154 | "I do want the integration routine, and I also have to load in the relevant\n", 155 | "modules from \n", 156 | "[units](https://hackage.haskell.org/package/units)\n", 157 | "and\n", 158 | "[units-defs](https://hackage.haskell.org/package/units-defs),\n", 159 | "which provides the definitions of the SI units I need." 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "collapsed": false, 165 | "input": [ 166 | "-- Numerics\n", 167 | "import Numeric.Integration.TanhSinh" 168 | ], 169 | "language": "python", 170 | "metadata": {}, 171 | "outputs": [] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "collapsed": false, 176 | "input": [ 177 | "-- Units\n", 178 | "import Data.Metrology\n", 179 | "import Data.Metrology.SI\n", 180 | "import Data.Metrology.Show" 181 | ], 182 | "language": "python", 183 | "metadata": {}, 184 | "outputs": [] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": { 189 | "hidden": false 190 | }, 191 | "source": [ 192 | "The `units` package provides two \"forms\" of the API: a general version (the\n", 193 | "`Data.Metrology.Poly`/`Data.Metrology.SI.Poly` modules), which provides more\n", 194 | "control, and a \"simpler\" version, which I use here. The difference is not\n", 195 | "in the function names, but in their types, with the polymorphic version being\n", 196 | "more general than the simpler, monomorphic, version. The code below would be\n", 197 | "unchanged if I used the `.Poly` versions of the modules, I just would have to\n", 198 | "supply more complex types (or at least, that's what I believe; I haven't\n", 199 | "actually tried it ;-)." 200 | ] 201 | }, 202 | { 203 | "cell_type": "heading", 204 | "level": 2, 205 | "metadata": { 206 | "hidden": false 207 | }, 208 | "source": [ 209 | "Using the units module" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": { 215 | "hidden": false 216 | }, 217 | "source": [ 218 | "As with `dimensional-tf`, there's a way to convert a number into a type with units. In this case\n", 219 | "it is `%`, but the values to the right are now Haskell types, rather than values as they are\n", 220 | "in `dimensional-tf`, which means that\n", 221 | "they are handled in a different manner. The `units` package provides syntax for \"combining\" units;\n", 222 | "here `:/` is used to indicate division, but there's also `:*` for multiplication, `:^` for raising\n", 223 | "to a power, and `:@` for combining terms (there's examples of most, if not all, of these later\n", 224 | "on in the notebook).\n", 225 | "\n", 226 | "I start by defining the speed of light, which can be done by sepcifying the numeric value\n", 227 | "and then the units, separated by the `%` operator. This is similar to the `*~` operator\n", 228 | "of `dimensional-tf`, except that the units are defined by types rather than values (Haskell\n", 229 | "starts type names with a capital letter). As we shall see, there's not much difference \n", 230 | "between these two approaches *for the code I write here*, but that's not really a fair comparison\n", 231 | "as you don't get to see all the mis-steps I made along the way, and this is not a complex\n", 232 | "piece of code that needs much maintenance or extension." 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "collapsed": false, 238 | "input": [ 239 | "c = 299792458.0 % (Meter :/ Second)\n", 240 | ":type c" 241 | ], 242 | "language": "python", 243 | "metadata": {}, 244 | "outputs": [] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "metadata": { 249 | "hidden": false 250 | }, 251 | "source": [ 252 | "The inferred type for this value is as equally baroque (for the non-Haskeller) as the `dimensional-tf` version, which for reference\n", 253 | "was\n", 254 | "\n", 255 | " c = 299792458.0 P.*~ (P.meter P./ P.second)\n", 256 | " :type c\n", 257 | " c :: forall a. Fractional a => Quantity (Dim (S Z) Z (N (S Z)) Z Z Z Z) a\n", 258 | " \n", 259 | "In this case the first argument to `Qu`, which is the term within ``[ ... ]`, indicates the dimensions - so here length of 1 and time of -1 ([Peano numbers](https://wiki.haskell.org/Peano_numbers) again being used to indicate values in the type, with aliases for common numbers, such as `One`), with the second term being a mysterious `DefaultLCSU` value (which I am going to ignore, since I can get away with writing this notebook\n", 260 | "without having to use it, as it only really comes into play when using the polymorphic version), and the third is the type of the numeric value.\n", 261 | "\n", 262 | "As with `dimensional-tf`, there are more-readable types which can be specified. In this case `Velocity`, which is defined\n", 263 | "as `Length :/ Time`:" 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "collapsed": false, 269 | "input": [ 270 | "c :: Velocity\n", 271 | "c = 299792458.0 % (Meter :/ Second)\n", 272 | "\n", 273 | ":type c" 274 | ], 275 | "language": "python", 276 | "metadata": {}, 277 | "outputs": [] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": { 282 | "hidden": false 283 | }, 284 | "source": [ 285 | "While `%` promotes a number into a unit, `#` extracts a value from a value in the specified units. These are similar in spirit to the `*~` and `/~` operators of `dimensional-tf`, but they have rather eye-glazing types:" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "collapsed": false, 291 | "input": [ 292 | ":type (%)\n", 293 | ":type (#)" 294 | ], 295 | "language": "python", 296 | "metadata": {}, 297 | "outputs": [] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": { 302 | "hidden": false 303 | }, 304 | "source": [ 305 | "To combine values, you have to use the operators provided by the module; these use a naming scheme where\n", 306 | "'|' appears to the side that a quantity is. This means that to multiply `c` by `0.99` you could say either of" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "collapsed": false, 312 | "input": [ 313 | "0.99 *| c\n", 314 | "c |* 0.99" 315 | ], 316 | "language": "python", 317 | "metadata": {}, 318 | "outputs": [] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "metadata": { 323 | "hidden": false 324 | }, 325 | "source": [ 326 | "To multiply two values together you use `|*|`, which also keeps track of the combined units:" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "collapsed": false, 332 | "input": [ 333 | "c |*| c" 334 | ], 335 | "language": "python", 336 | "metadata": {}, 337 | "outputs": [] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": { 342 | "hidden": false 343 | }, 344 | "source": [ 345 | "or add together values with consistent units, which also shows how compound units such as `cm` or `kg` are formed\n", 346 | "using the `:@` operator to combine the types:" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "collapsed": false, 352 | "input": [ 353 | "(4 % Meter) |+| (3 % (Centi :@ Meter))" 354 | ], 355 | "language": "python", 356 | "metadata": {}, 357 | "outputs": [] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "metadata": { 362 | "hidden": false 363 | }, 364 | "source": [ 365 | "I used brackets to make it clearer what terms were related, but fortunately the precedence of the various operators means\n", 366 | "that you can often get away without using them. In this particular case no brackets are needed since:" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "collapsed": false, 372 | "input": [ 373 | "4 % Meter |+| 3 % Centi :@ Meter" 374 | ], 375 | "language": "python", 376 | "metadata": {}, 377 | "outputs": [] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": { 382 | "hidden": false 383 | }, 384 | "source": [ 385 | "As you'd hope, you get errors when trying to combine values with incompatible units, as shown in the next set of examples:" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "collapsed": false, 391 | "input": [ 392 | "4 % Meter |+| 0.03" 393 | ], 394 | "language": "python", 395 | "metadata": {}, 396 | "outputs": [] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "collapsed": false, 401 | "input": [ 402 | "0.99 * c" 403 | ], 404 | "language": "python", 405 | "metadata": {}, 406 | "outputs": [] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "collapsed": false, 411 | "input": [ 412 | "1 % Meter |+| 1 % Second" 413 | ], 414 | "language": "python", 415 | "metadata": {}, 416 | "outputs": [] 417 | }, 418 | { 419 | "cell_type": "markdown", 420 | "metadata": { 421 | "hidden": false 422 | }, 423 | "source": [ 424 | "If you use the normal mathematical operators then you also get errors, even if the units match:" 425 | ] 426 | }, 427 | { 428 | "cell_type": "code", 429 | "collapsed": false, 430 | "input": [ 431 | "(1 % Meter) + (1 % Meter)" 432 | ], 433 | "language": "python", 434 | "metadata": {}, 435 | "outputs": [] 436 | }, 437 | { 438 | "cell_type": "markdown", 439 | "metadata": { 440 | "hidden": false 441 | }, 442 | "source": [ 443 | "Putting this together, I can calculate the value of $\\frac{1}{2} m v^2$ for $m = 1~{\\rm kg}$ and $v=c$:" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "collapsed": false, 449 | "input": [ 450 | "answer = 0.5 *| (1 % Kilo :@ Gram) |*| c |*| c\n", 451 | ":type answer\n", 452 | "answer" 453 | ], 454 | "language": "python", 455 | "metadata": {}, 456 | "outputs": [] 457 | }, 458 | { 459 | "cell_type": "markdown", 460 | "metadata": { 461 | "hidden": false 462 | }, 463 | "source": [ 464 | "This can be given an explicit type, rather than letting the compiler infer one:" 465 | ] 466 | }, 467 | { 468 | "cell_type": "code", 469 | "collapsed": false, 470 | "input": [ 471 | "answer2 :: Energy\n", 472 | "answer2 = answer\n", 473 | "answer2" 474 | ], 475 | "language": "python", 476 | "metadata": {}, 477 | "outputs": [] 478 | }, 479 | { 480 | "cell_type": "markdown", 481 | "metadata": { 482 | "hidden": false 483 | }, 484 | "source": [ 485 | "Using an explicit type catches dimensional errors in your formula, but you may have to use the\n", 486 | "`redim` function - as shown below - to \n", 487 | "ensure that the types can be compared: `redim` has a compile-time cost, when it forces the\n", 488 | "units to be compared, but there is *no* run-time cost to using it. It is needed because the type\n", 489 | "system can't easily match up units where only the order of the dimensions differs." 490 | ] 491 | }, 492 | { 493 | "cell_type": "code", 494 | "collapsed": false, 495 | "input": [ 496 | "-- This is going to fail because I have missed out multiplying by a velocity,\n", 497 | "-- so the units of the calculation are just MASS * LENGTH / TIME, which\n", 498 | "-- does not match energy. If redim is not included then you get, for this\n", 499 | "-- case, a more-verbose error, but there is the possibility in this case that\n", 500 | "-- it's just a limitation of the type system rather than an actual units\n", 501 | "-- mis-match.\n", 502 | "answer3 :: Energy\n", 503 | "answer3 = redim (0.5 *| (1 % Gram) |*| c)" 504 | ], 505 | "language": "python", 506 | "metadata": {}, 507 | "outputs": [] 508 | }, 509 | { 510 | "cell_type": "markdown", 511 | "metadata": { 512 | "hidden": false 513 | }, 514 | "source": [ 515 | "As previoulsy mentioned, `#` is used to extract the value in the given units (so it is similar in\n", 516 | "spirit to the `/~` operator of `dimensional-tf`). For this case it works whether an explicit type\n", 517 | "was given (`answer2`) or not (`answer`):" 518 | ] 519 | }, 520 | { 521 | "cell_type": "code", 522 | "collapsed": false, 523 | "input": [ 524 | "answer # Joule\n", 525 | "answer2 # Mega :@ Joule" 526 | ], 527 | "language": "python", 528 | "metadata": {}, 529 | "outputs": [] 530 | }, 531 | { 532 | "cell_type": "markdown", 533 | "metadata": { 534 | "hidden": false 535 | }, 536 | "source": [ 537 | "The current version of `units` (well, really, it's `units-defs`) does not contain some of the\n", 538 | "types I want to use (e.g. Parsec and Astronomical Unit). I can use the existing functionality\n", 539 | "to allow me to create a scale value such as" 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "collapsed": false, 545 | "input": [ 546 | "parsec :: Length\n", 547 | "parsec = 3.085678e16 % Meter" 548 | ], 549 | "language": "python", 550 | "metadata": {}, 551 | "outputs": [] 552 | }, 553 | { 554 | "cell_type": "markdown", 555 | "metadata": { 556 | "hidden": false 557 | }, 558 | "source": [ 559 | "which I can then use in equations" 560 | ] 561 | }, 562 | { 563 | "cell_type": "code", 564 | "collapsed": false, 565 | "input": [ 566 | "3 *| parsec" 567 | ], 568 | "language": "python", 569 | "metadata": {}, 570 | "outputs": [] 571 | }, 572 | { 573 | "cell_type": "code", 574 | "collapsed": false, 575 | "input": [ 576 | "(3 *| parsec) # Meter" 577 | ], 578 | "language": "python", 579 | "metadata": {}, 580 | "outputs": [] 581 | }, 582 | { 583 | "cell_type": "markdown", 584 | "metadata": { 585 | "hidden": false 586 | }, 587 | "source": [ 588 | "However, it's not a type, so I can not convert a length into parsecs using `#` (well, not\n", 589 | "without manually converting the answer using the correct scaling factor), as shown below\n", 590 | "(the error message is so large because I have made a logical mistake here, using the\n", 591 | "wrong \"thing\" on the right-hand side of the `#` operator, and the type system is therefore\n", 592 | "really confused):" 593 | ] 594 | }, 595 | { 596 | "cell_type": "code", 597 | "collapsed": false, 598 | "input": [ 599 | "(12.3e18 % Meter) # parsec" 600 | ], 601 | "language": "python", 602 | "metadata": {}, 603 | "outputs": [] 604 | }, 605 | { 606 | "cell_type": "markdown", 607 | "metadata": { 608 | "hidden": false 609 | }, 610 | "source": [ 611 | "It is possible, however, to create a type for parsecs. Following the \n", 612 | "[units tutorial](https://github.com/goldfirere/units/blob/master/README.md) I came up\n", 613 | "with the following, which relates `Parsec` to `Meter`." 614 | ] 615 | }, 616 | { 617 | "cell_type": "code", 618 | "collapsed": false, 619 | "input": [ 620 | "data Parsec = Parsec\n", 621 | "\n", 622 | "instance Unit Parsec where\n", 623 | " type BaseUnit Parsec = Meter\n", 624 | " \n", 625 | " -- Note the _ argument!\n", 626 | " conversionRatio _ = 3.085678e16" 627 | ], 628 | "language": "python", 629 | "metadata": {}, 630 | "outputs": [] 631 | }, 632 | { 633 | "cell_type": "markdown", 634 | "metadata": { 635 | "hidden": false 636 | }, 637 | "source": [ 638 | "The `conversionRatio` function has the following type:" 639 | ] 640 | }, 641 | { 642 | "cell_type": "code", 643 | "collapsed": false, 644 | "input": [ 645 | ":type conversionRatio" 646 | ], 647 | "language": "python", 648 | "metadata": {}, 649 | "outputs": [] 650 | }, 651 | { 652 | "cell_type": "markdown", 653 | "metadata": { 654 | "hidden": false 655 | }, 656 | "source": [ 657 | "which says that the first argument must be an instance of the `Unit` type class (which is what the `instance Unit ...` section above does), and returns a `Rational` value (which is a particular\n", 658 | "[representation of a floating-point number](http://hackage.haskell.org/package/base-4.7.0.2/docs/Prelude.html#t:Rational)).\n", 659 | "The documentation for `units` makes it clear that the first argument to this function must *never* be used (technically, I believe it's that this value should never be *evaluated*); to do this\n", 660 | "we use the \"placeholder\" syntax of `_`, which tells Haskell to ignore the argument. For users not used to Haskell, it might\n", 661 | "seem strange to ignore an argument to a function, or to not \"evaluate\" it. The reason for ignoring this argument is that it's actually the type of the argument that is important, rather than its value. The type tells the compiler which particular\n", 662 | "version of `conversionRatio` to use (it's a bit like how interfaces in Python or Java are used to select what code to use, but please don't hold strongly to this viewpoint as thinking about objects is likely to keep you confused when reading Haskell), and so it doesn't need to use the actual value. In such situations, it is common for the value to be set to `undefined`, which is a *special* value, in that\n", 663 | "\n", 664 | " - I don't really want to try and explain it here\n", 665 | " \n", 666 | " - it can be set to any type (i.e. it's polymorphic)\n", 667 | " \n", 668 | " - evaluating the value is going to cause an error (hence the requirement that user code should never, ever, be used)\n", 669 | " \n", 670 | "Here's an example of such an error, which isn't too informative, and you won't be seeing elsewhere in this notebook:" 671 | ] 672 | }, 673 | { 674 | "cell_type": "code", 675 | "collapsed": false, 676 | "input": [ 677 | "undefined" 678 | ], 679 | "language": "python", 680 | "metadata": {}, 681 | "outputs": [] 682 | }, 683 | { 684 | "cell_type": "markdown", 685 | "metadata": { 686 | "hidden": false 687 | }, 688 | "source": [ 689 | "With all this I can now create a length in parsecs:" 690 | ] 691 | }, 692 | { 693 | "cell_type": "code", 694 | "collapsed": false, 695 | "input": [ 696 | "bob = (3::Double) % Parsec" 697 | ], 698 | "language": "python", 699 | "metadata": {}, 700 | "outputs": [] 701 | }, 702 | { 703 | "cell_type": "markdown", 704 | "metadata": { 705 | "hidden": false 706 | }, 707 | "source": [ 708 | "The type, shown below, indicates that it's a length. I could have said\n", 709 | "\n", 710 | " bob :: Length\n", 711 | " bob = (3::Double) % Parsec\n", 712 | " \n", 713 | "which provides more-readable code, and error messages, but is the same as\n", 714 | "the version without the explicit type. The restriction of 3 to a `Double` type\n", 715 | "is just to simplify the types a little bit (it removes the\n", 716 | "`forall n. Fractional a =>` constraint on a type, since `Double` is an\n", 717 | "instance of the \n", 718 | "[`Fractional` type class](http://hackage.haskell.org/package/base-4.7.0.2/docs/Prelude.html#t:Fractional))." 719 | ] 720 | }, 721 | { 722 | "cell_type": "code", 723 | "collapsed": false, 724 | "input": [ 725 | ":type bob\n", 726 | ":type (3::Double) % Meter" 727 | ], 728 | "language": "python", 729 | "metadata": {}, 730 | "outputs": [] 731 | }, 732 | { 733 | "cell_type": "markdown", 734 | "metadata": { 735 | "hidden": false 736 | }, 737 | "source": [ 738 | "Its value is reported in metres, since that is the base unit of the Parsec type:" 739 | ] 740 | }, 741 | { 742 | "cell_type": "code", 743 | "collapsed": false, 744 | "input": [ 745 | "bob" 746 | ], 747 | "language": "python", 748 | "metadata": {}, 749 | "outputs": [] 750 | }, 751 | { 752 | "cell_type": "markdown", 753 | "metadata": { 754 | "hidden": false 755 | }, 756 | "source": [ 757 | "With this type, I can also convert other lengths into parsecs. The two forms below are the same, since `mega` can be considered to be a short form for saying `Mega :@`, and the `()` are not needed, since the `%` operator takes precedence over `#`, but I have left them in to make it clearer what is going on." 758 | ] 759 | }, 760 | { 761 | "cell_type": "code", 762 | "collapsed": false, 763 | "input": [ 764 | "(2.2e28 % Meter) # mega Parsec" 765 | ], 766 | "language": "python", 767 | "metadata": {}, 768 | "outputs": [] 769 | }, 770 | { 771 | "cell_type": "code", 772 | "collapsed": false, 773 | "input": [ 774 | "(2.2e28 % Meter) # Mega :@ Parsec" 775 | ], 776 | "language": "python", 777 | "metadata": {}, 778 | "outputs": [] 779 | }, 780 | { 781 | "cell_type": "markdown", 782 | "metadata": { 783 | "hidden": false 784 | }, 785 | "source": [ 786 | "I can do the same for the Hubble Constant,\n", 787 | "that is define a type, \n", 788 | "but it doesn't feel as sensible to me, since we do not really\n", 789 | "talk about units of `km/s/Mpc` in quite the same way as we do\n", 790 | "parsecs." 791 | ] 792 | }, 793 | { 794 | "cell_type": "code", 795 | "collapsed": false, 796 | "input": [ 797 | "-- Hubble constant in units of km/s/Mpc\n", 798 | "data HubbleConstant = HubbleConstant\n", 799 | "\n", 800 | "instance Unit HubbleConstant where\n", 801 | " type BaseUnit HubbleConstant = Hertz\n", 802 | " \n", 803 | " -- As shown in later examples, this could have used something\n", 804 | " -- like (1 % (Kilo @: Meter :/ Mega @: Parsec :/ Second))\n", 805 | " -- to calculate the conversion ratio, but I wanted to\n", 806 | " -- use an explicit value here:\n", 807 | " conversionRatio _ = 1e3 / (1e6 * 3.085678e16)" 808 | ], 809 | "language": "python", 810 | "metadata": {}, 811 | "outputs": [] 812 | }, 813 | { 814 | "cell_type": "markdown", 815 | "metadata": { 816 | "hidden": false 817 | }, 818 | "source": [ 819 | "With this, I can easily create a value equal to `70 km/sMpc`:" 820 | ] 821 | }, 822 | { 823 | "cell_type": "code", 824 | "collapsed": false, 825 | "input": [ 826 | "h70 = 70 % HubbleConstant\n", 827 | "h70\n", 828 | ":type h70" 829 | ], 830 | "language": "python", 831 | "metadata": {}, 832 | "outputs": [] 833 | }, 834 | { 835 | "cell_type": "markdown", 836 | "metadata": { 837 | "hidden": false 838 | }, 839 | "source": [ 840 | "Alternatively, a value can be created just by specifying the units\n", 841 | "directly, with the same result (i.e. the type is the same):" 842 | ] 843 | }, 844 | { 845 | "cell_type": "code", 846 | "collapsed": false, 847 | "input": [ 848 | "h70 = 70 % (Kilo :@ Meter :/ Second :/ Mega :@ Parsec)\n", 849 | "h70\n", 850 | ":type h70" 851 | ], 852 | "language": "python", 853 | "metadata": {}, 854 | "outputs": [] 855 | }, 856 | { 857 | "cell_type": "markdown", 858 | "metadata": { 859 | "hidden": false 860 | }, 861 | "source": [ 862 | "One advantage to having the `HubbleConstant` type is that I can convert a\n", 863 | "frequence into `km/s/Mpc`, as shown below, but I personally don't feel\n", 864 | "this is particularly compelling:" 865 | ] 866 | }, 867 | { 868 | "cell_type": "code", 869 | "collapsed": false, 870 | "input": [ 871 | "(7.2e-19 % Hertz) # HubbleConstant" 872 | ], 873 | "language": "python", 874 | "metadata": {}, 875 | "outputs": [] 876 | }, 877 | { 878 | "cell_type": "markdown", 879 | "metadata": { 880 | "hidden": false 881 | }, 882 | "source": [ 883 | "For this notebook, an alternative to creating the `HubbleConstant` unit \n", 884 | "would be to create a function which adds in the\n", 885 | "correct units, such as the `hubbleConstant` function below:" 886 | ] 887 | }, 888 | { 889 | "cell_type": "code", 890 | "collapsed": false, 891 | "input": [ 892 | "hubbleConstant x = x % (Kilo :@ Meter :/ Second :/ Mega :@ Parsec)\n", 893 | "hubbleConstant 70\n", 894 | ":t hubbleConstant" 895 | ], 896 | "language": "python", 897 | "metadata": {}, 898 | "outputs": [] 899 | }, 900 | { 901 | "cell_type": "markdown", 902 | "metadata": { 903 | "hidden": false 904 | }, 905 | "source": [ 906 | "As mentioned before, it seems to be personal preference for which to prefer (at least for the tasks I perform\n", 907 | "in this notebook), as the results are the same:" 908 | ] 909 | }, 910 | { 911 | "cell_type": "code", 912 | "collapsed": false, 913 | "input": [ 914 | "hubbleConstant 72.4\n", 915 | "72.4 % HubbleConstant" 916 | ], 917 | "language": "python", 918 | "metadata": {}, 919 | "outputs": [] 920 | }, 921 | { 922 | "cell_type": "heading", 923 | "level": 3, 924 | "metadata": { 925 | "hidden": false 926 | }, 927 | "source": [ 928 | "The angular-diameter and luminosity distances" 929 | ] 930 | }, 931 | { 932 | "cell_type": "markdown", 933 | "metadata": { 934 | "hidden": false 935 | }, 936 | "source": [ 937 | "It is now possible to calculate the \n", 938 | "[angular-diameter](http://en.wikipedia.org/wiki/Angular_diameter_distance)\n", 939 | "and \n", 940 | "[luminosity](http://en.wikipedia.org/wiki/Luminosity_distance)\n", 941 | "distances. As with the `dimensional-tf` versions, \n", 942 | "I have included explicit types to ensure that the inputs and outputs are as expected, since without the signatures\n", 943 | "the compiler would infer very-generic types. A complication is in having to remember which variant of the operators\n", 944 | "should be used (e.g. `*|` or `|*`), but the compiler complains quite loudly if you get it wrong (another reason for\n", 945 | "having the signatures). As previously mentioned, `redim` is essentially doing book-keeping at compile time; I didn't\n", 946 | "seem to need to include them here, but as they have no run-time cost it seems a sensible thing to include." 947 | ] 948 | }, 949 | { 950 | "cell_type": "code", 951 | "collapsed": false, 952 | "input": [ 953 | "-- angular-diameter and luminosity distances (units of the Hubble length)\n", 954 | "\n", 955 | "dcH om z = result (absolute 1.0e-6 (trap f 0 z))\n", 956 | " where\n", 957 | " -- unlike the previous notebook, I have made f a local definition\n", 958 | " -- here, so it does not need to be sent in the om value as\n", 959 | " -- it is already in scope\n", 960 | " f z = let ol = 1 - om\n", 961 | " t = (1 + z)^2 * (1 + om*z) - (2 + z) * ol * z\n", 962 | " in 1 / sqrt t\n", 963 | " \n", 964 | "daH om z = dcH om z / (1 + z)\n", 965 | " \n", 966 | "dlH om z = dcH om z * (1 + z)\n", 967 | "\n", 968 | "-- now the versions which return values in physical units\n", 969 | "\n", 970 | "da :: Double -> Frequency -> Double -> Length \n", 971 | "da om h0 z = redim (daH om z *| c |/| h0)\n", 972 | "\n", 973 | "dl :: Double -> Frequency -> Double -> Length\n", 974 | "dl om h0 z = redim (dlH om z *| c |/| h0)" 975 | ], 976 | "language": "python", 977 | "metadata": {}, 978 | "outputs": [] 979 | }, 980 | { 981 | "cell_type": "markdown", 982 | "metadata": { 983 | "hidden": false 984 | }, 985 | "source": [ 986 | "Now it's time to compare the results to those from `dimensional-tf` (I do not expect any significant numerical differences, what I am interested in is how to write and use the values). I use the `hubbleConstant` function here, but the result would have been\n", 987 | "the same if I had used `70 % HubbleConstant` instead." 988 | ] 989 | }, 990 | { 991 | "cell_type": "code", 992 | "collapsed": false, 993 | "input": [ 994 | "da 0.3 (hubbleConstant 70) 2 # Mega :@ Parsec" 995 | ], 996 | "language": "python", 997 | "metadata": {}, 998 | "outputs": [] 999 | }, 1000 | { 1001 | "cell_type": "markdown", 1002 | "metadata": { 1003 | "hidden": false 1004 | }, 1005 | "source": [ 1006 | "The next step is to calculate the transverse distances, by multiplying the angular-diameter distance `da` by\n", 1007 | "an angle in radians. The `dimensional-tf` package has in-built support for angles, but `units` does not. \n", 1008 | "For this section I treat angles as simple numbers (so do not take advantage of any dimensional support),\n", 1009 | "but do add in a simple type to represent angles, to better document the intent of the routines. I\n", 1010 | "try creating a `Radian` unit at the end of this notebook to compare to this approach.\n", 1011 | "\n", 1012 | "Here I create an `Angle` type which stores a single number as a `Double`, which I am going to\n", 1013 | "assume is in radians. To help enforce this, I also create several helper routines which will create\n", 1014 | "an `Angle` type given several different input values (e.g. a value in degrees or arc seconds). Since this\n", 1015 | "is being done in a single IHaskell notebook it is quite easy to subvert the safety provided by these\n", 1016 | "routines by creating an `Angle` value manually. In \"real\" Haskell code, the `Angle` data type would\n", 1017 | "be defined in a separate module which would only allow you to create values\n", 1018 | "using the routines such as `radian`, rather than exposing the ability to create an `Angle` type directly." 1019 | ] 1020 | }, 1021 | { 1022 | "cell_type": "code", 1023 | "collapsed": false, 1024 | "input": [ 1025 | "-- The Angle type has a single numeric value, which is taken to be the\n", 1026 | "-- value in radians.\n", 1027 | "\n", 1028 | "data Angle = Angle { toRadians :: Double }\n", 1029 | " deriving Show\n", 1030 | " \n", 1031 | "-- Convert a number in radians, degrees, arcminutes, arcseconds to an Angle data type\n", 1032 | "\n", 1033 | "radian :: Double -> Angle\n", 1034 | "radian x = Angle x\n", 1035 | "\n", 1036 | "degree :: Double -> Angle\n", 1037 | "degree x = radian (x * pi / 180)\n", 1038 | "\n", 1039 | "arcmin :: Double -> Angle\n", 1040 | "arcmin x = degree (x / 60)\n", 1041 | "\n", 1042 | "arcsec :: Double -> Angle\n", 1043 | "arcsec x = arcmin (x / 60)" 1044 | ], 1045 | "language": "python", 1046 | "metadata": {}, 1047 | "outputs": [] 1048 | }, 1049 | { 1050 | "cell_type": "markdown", 1051 | "metadata": { 1052 | "hidden": false 1053 | }, 1054 | "source": [ 1055 | "As a check, here's the output for these routines:" 1056 | ] 1057 | }, 1058 | { 1059 | "cell_type": "code", 1060 | "collapsed": false, 1061 | "input": [ 1062 | "radian 1\n", 1063 | "degree 1\n", 1064 | "arcmin 1\n", 1065 | "arcsec 1" 1066 | ], 1067 | "language": "python", 1068 | "metadata": {}, 1069 | "outputs": [] 1070 | }, 1071 | { 1072 | "cell_type": "markdown", 1073 | "metadata": { 1074 | "hidden": false 1075 | }, 1076 | "source": [ 1077 | "The value from an `Angle` can be retrieved using the `toRadians` function:" 1078 | ] 1079 | }, 1080 | { 1081 | "cell_type": "code", 1082 | "collapsed": false, 1083 | "input": [ 1084 | ":type toRadians\n", 1085 | "toRadians (degree 1)" 1086 | ], 1087 | "language": "python", 1088 | "metadata": {}, 1089 | "outputs": [] 1090 | }, 1091 | { 1092 | "cell_type": "markdown", 1093 | "metadata": { 1094 | "hidden": false 1095 | }, 1096 | "source": [ 1097 | "With these, the `adist` routine can be written as:" 1098 | ] 1099 | }, 1100 | { 1101 | "cell_type": "code", 1102 | "collapsed": false, 1103 | "input": [ 1104 | "adist :: Double -> Frequency -> Double -> Angle -> Length\n", 1105 | "adist om h0 z angle = da om h0 z |* toRadians angle" 1106 | ], 1107 | "language": "python", 1108 | "metadata": {}, 1109 | "outputs": [] 1110 | }, 1111 | { 1112 | "cell_type": "markdown", 1113 | "metadata": { 1114 | "hidden": false 1115 | }, 1116 | "source": [ 1117 | "Since the fourth argument has a type of `Angle`, I can not call it\n", 1118 | "with a number, otherwise the compiler complains:" 1119 | ] 1120 | }, 1121 | { 1122 | "cell_type": "code", 1123 | "collapsed": false, 1124 | "input": [ 1125 | "adist 0.3 (hubbleConstant 70) 2 1" 1126 | ], 1127 | "language": "python", 1128 | "metadata": {}, 1129 | "outputs": [] 1130 | }, 1131 | { 1132 | "cell_type": "markdown", 1133 | "metadata": { 1134 | "hidden": false 1135 | }, 1136 | "source": [ 1137 | "Supplying an actual `Angle` value allows me to compare to the `dimensional-tf` version; as above,\n", 1138 | "the numeric values are essentially the same" 1139 | ] 1140 | }, 1141 | { 1142 | "cell_type": "code", 1143 | "collapsed": false, 1144 | "input": [ 1145 | "adist 0.3 (hubbleConstant 70) 2 (arcsec 1) # Kilo :@ Parsec" 1146 | ], 1147 | "language": "python", 1148 | "metadata": {}, 1149 | "outputs": [] 1150 | }, 1151 | { 1152 | "cell_type": "code", 1153 | "collapsed": false, 1154 | "input": [ 1155 | "adist 0.3 (hubbleConstant 70) 2 (arcmin 2.4) # Mega :@ Parsec" 1156 | ], 1157 | "language": "python", 1158 | "metadata": {}, 1159 | "outputs": [] 1160 | }, 1161 | { 1162 | "cell_type": "markdown", 1163 | "metadata": { 1164 | "hidden": false 1165 | }, 1166 | "source": [ 1167 | "And now for the luminosity distance:" 1168 | ] 1169 | }, 1170 | { 1171 | "cell_type": "code", 1172 | "collapsed": false, 1173 | "input": [ 1174 | "dl 0.3 (hubbleConstant 70) 2" 1175 | ], 1176 | "language": "python", 1177 | "metadata": {}, 1178 | "outputs": [] 1179 | }, 1180 | { 1181 | "cell_type": "code", 1182 | "collapsed": false, 1183 | "input": [ 1184 | "dl 0.3 (hubbleConstant 70) 2 # Mega :@ Parsec" 1185 | ], 1186 | "language": "python", 1187 | "metadata": {}, 1188 | "outputs": [] 1189 | }, 1190 | { 1191 | "cell_type": "markdown", 1192 | "metadata": { 1193 | "hidden": false 1194 | }, 1195 | "source": [ 1196 | "Perhaps surprisingly, `units-defs` does not come with a definition of a year, which\n", 1197 | "means that calculating this distance in units of Giga light years is going to require\n", 1198 | "some set up. Although it is not clear what a good general-purpose definition is, I am going\n", 1199 | "to define one following the IAU recommendation, which also happens to be the\n", 1200 | "version that `dimension-tf` used." 1201 | ] 1202 | }, 1203 | { 1204 | "cell_type": "code", 1205 | "collapsed": false, 1206 | "input": [ 1207 | "-- Following the dimensional package, define a year as 365.25 days,\n", 1208 | "-- which is an IAU recommendation \n", 1209 | "-- http://www.iau.org/science/publications/proceedings_rules/units/\n", 1210 | "-- and who am I to argue with the demoters of Pluto?\n", 1211 | "--\n", 1212 | "data Year = Year\n", 1213 | "\n", 1214 | "-- I could have made the base unit be second and use\n", 1215 | "-- a conversion ratio of 31557600, but I wanted to see\n", 1216 | "-- how this sort of \"chaining\" of units worked.\n", 1217 | "instance Unit Year where\n", 1218 | " type BaseUnit Year = Hour\n", 1219 | " conversionRatio _ = 365.25 * 24\n", 1220 | "\n", 1221 | "-- Light years\n", 1222 | "data LightYear = LightYear\n", 1223 | "\n", 1224 | "instance Unit LightYear where\n", 1225 | " type BaseUnit LightYear = Meter\n", 1226 | " \n", 1227 | " -- How efficient is the following: is the number calculated at compile time\n", 1228 | " -- or evaluated at each conversion? This approach is in contrast to the\n", 1229 | " -- Unit instance of HubbleConstant, where I explicitly included\n", 1230 | " -- the numeric values.\n", 1231 | " --\n", 1232 | " -- Without the realToFrac function, the answer would be a Fractional,\n", 1233 | " -- but conversionRatio returns a Rational, so I have to do an\n", 1234 | " -- explicit type cast here.\n", 1235 | " --\n", 1236 | " conversionRatio _ = realToFrac (c # Meter :/ Year)" 1237 | ], 1238 | "language": "python", 1239 | "metadata": {}, 1240 | "outputs": [] 1241 | }, 1242 | { 1243 | "cell_type": "markdown", 1244 | "metadata": { 1245 | "hidden": false 1246 | }, 1247 | "source": [ 1248 | "After all that, I can finally calculate the distance in light years:" 1249 | ] 1250 | }, 1251 | { 1252 | "cell_type": "code", 1253 | "collapsed": false, 1254 | "input": [ 1255 | "dl 0.3 (hubbleConstant 70) 2 # Giga :@ LightYear" 1256 | ], 1257 | "language": "python", 1258 | "metadata": {}, 1259 | "outputs": [] 1260 | }, 1261 | { 1262 | "cell_type": "heading", 1263 | "level": 3, 1264 | "metadata": { 1265 | "hidden": false 1266 | }, 1267 | "source": [ 1268 | "Calculating luminosities" 1269 | ] 1270 | }, 1271 | { 1272 | "cell_type": "markdown", 1273 | "metadata": { 1274 | "hidden": false 1275 | }, 1276 | "source": [ 1277 | "The next step is to convert from flux to luminosity. This requires a non-SI unit, erg, which\n", 1278 | "is provided by `Data.Units.CGS`:" 1279 | ] 1280 | }, 1281 | { 1282 | "cell_type": "code", 1283 | "collapsed": false, 1284 | "input": [ 1285 | "import Data.Units.CGS (Erg(..))" 1286 | ], 1287 | "language": "python", 1288 | "metadata": {}, 1289 | "outputs": [] 1290 | }, 1291 | { 1292 | "cell_type": "markdown", 1293 | "metadata": { 1294 | "hidden": false 1295 | }, 1296 | "source": [ 1297 | "With this I can create energies and convert between Ergs and Joules:" 1298 | ] 1299 | }, 1300 | { 1301 | "cell_type": "code", 1302 | "collapsed": false, 1303 | "input": [ 1304 | "1 % Erg\n", 1305 | "\n", 1306 | "1 % Joule |/| 1 % Erg" 1307 | ], 1308 | "language": "python", 1309 | "metadata": {}, 1310 | "outputs": [] 1311 | }, 1312 | { 1313 | "cell_type": "markdown", 1314 | "metadata": { 1315 | "hidden": false 1316 | }, 1317 | "source": [ 1318 | "To make the type signature cleaner I invent the following type to represent fluxes:" 1319 | ] 1320 | }, 1321 | { 1322 | "cell_type": "code", 1323 | "collapsed": false, 1324 | "input": [ 1325 | "type EnergyFlux = Power %/ Area" 1326 | ], 1327 | "language": "python", 1328 | "metadata": {}, 1329 | "outputs": [] 1330 | }, 1331 | { 1332 | "cell_type": "markdown", 1333 | "metadata": { 1334 | "hidden": false 1335 | }, 1336 | "source": [ 1337 | "The luminosity is therefore just (again, ignoring the\n", 1338 | "[K correction](http://en.wikipedia.org/wiki/K_correction) term):" 1339 | ] 1340 | }, 1341 | { 1342 | "cell_type": "code", 1343 | "collapsed": false, 1344 | "input": [ 1345 | "luminosity :: Double -> Frequency -> Double -> EnergyFlux -> Power\n", 1346 | "luminosity om h0 z flux = redim (flux |*| area)\n", 1347 | " where\n", 1348 | " r = dl om h0 z\n", 1349 | " area = 4 * pi *| r |*| r" 1350 | ], 1351 | "language": "python", 1352 | "metadata": {}, 1353 | "outputs": [] 1354 | }, 1355 | { 1356 | "cell_type": "markdown", 1357 | "metadata": { 1358 | "hidden": false 1359 | }, 1360 | "source": [ 1361 | "Unfortunately I can't seem to create fluxes on the fly, since this causes\n", 1362 | "type errors because of the order of the units, as shown in the following\n", 1363 | "error message (which turns out to be because of a missing call to `redim`\n", 1364 | "for the flux rather than a problem with the units):" 1365 | ] 1366 | }, 1367 | { 1368 | "cell_type": "code", 1369 | "collapsed": false, 1370 | "input": [ 1371 | "luminosity 0.3 (hubbleConstant 70) 2 (2e-13 % (Erg :/ Centi :@ Meter :^ sTwo :/ Second))" 1372 | ], 1373 | "language": "python", 1374 | "metadata": {}, 1375 | "outputs": [] 1376 | }, 1377 | { 1378 | "cell_type": "markdown", 1379 | "metadata": { 1380 | "hidden": false 1381 | }, 1382 | "source": [ 1383 | "One way around this is to define a helper function which includes a call to `redim` (or to include the `redim` call\n", 1384 | "directly in the original call):" 1385 | ] 1386 | }, 1387 | { 1388 | "cell_type": "code", 1389 | "collapsed": false, 1390 | "input": [ 1391 | "ergFlux :: Double -> EnergyFlux\n", 1392 | "ergFlux x = redim (x % (Erg :/ Second :/ Centi :@ Meter :^ sTwo))" 1393 | ], 1394 | "language": "python", 1395 | "metadata": {}, 1396 | "outputs": [] 1397 | }, 1398 | { 1399 | "cell_type": "markdown", 1400 | "metadata": { 1401 | "hidden": false 1402 | }, 1403 | "source": [ 1404 | "With this definition, I can create a luminosity" 1405 | ] 1406 | }, 1407 | { 1408 | "cell_type": "code", 1409 | "collapsed": false, 1410 | "input": [ 1411 | "lum = luminosity 0.3 (hubbleConstant 70) 2 (ergFlux 2e-13)\n", 1412 | "lum" 1413 | ], 1414 | "language": "python", 1415 | "metadata": {}, 1416 | "outputs": [] 1417 | }, 1418 | { 1419 | "cell_type": "markdown", 1420 | "metadata": { 1421 | "hidden": false 1422 | }, 1423 | "source": [ 1424 | "and convert it to the units I require:" 1425 | ] 1426 | }, 1427 | { 1428 | "cell_type": "code", 1429 | "collapsed": false, 1430 | "input": [ 1431 | "lum # Watt\n", 1432 | "lum # (Erg :/ Second)" 1433 | ], 1434 | "language": "python", 1435 | "metadata": {}, 1436 | "outputs": [] 1437 | }, 1438 | { 1439 | "cell_type": "heading", 1440 | "level": 3, 1441 | "metadata": { 1442 | "hidden": false 1443 | }, 1444 | "source": [ 1445 | "The critical density" 1446 | ] 1447 | }, 1448 | { 1449 | "cell_type": "markdown", 1450 | "metadata": { 1451 | "hidden": false 1452 | }, 1453 | "source": [ 1454 | "The final part of the original notebook was to calculate the \n", 1455 | "[critical density](http://en.wikipedia.org/wiki/Friedmann_equations#Density_parameter)\n", 1456 | "of the Universe as a function\n", 1457 | "of Hubble's constant, using the equation:\n", 1458 | "\n", 1459 | "$$\\rho_{\\rm crit} = \\frac{3 H^2}{8 \\pi G}$$\n", 1460 | "\n", 1461 | "where $G = 6.67428 \\times 10^{-11}\\ {\\rm m}^3 / \\rm{kg} / \\rm{s}^2$:" 1462 | ] 1463 | }, 1464 | { 1465 | "cell_type": "code", 1466 | "collapsed": false, 1467 | "input": [ 1468 | "criticalDensity :: Frequency -> Density\n", 1469 | "criticalDensity h = redim (3 *| h |*| h |/| (8 * pi *| bigG))\n", 1470 | " where\n", 1471 | " -- bigG is only defined local to the criticalDensity routine\n", 1472 | " bigG = 6.67428e-11 % (Meter :^ sThree :/ Kilo :@ Gram :/ Second :^ sTwo)" 1473 | ], 1474 | "language": "python", 1475 | "metadata": {}, 1476 | "outputs": [] 1477 | }, 1478 | { 1479 | "cell_type": "markdown", 1480 | "metadata": { 1481 | "hidden": false 1482 | }, 1483 | "source": [ 1484 | "This lets me calculate the same value as with `dimensional-tf`:" 1485 | ] 1486 | }, 1487 | { 1488 | "cell_type": "code", 1489 | "collapsed": false, 1490 | "input": [ 1491 | "c70 = criticalDensity (70 % HubbleConstant)\n", 1492 | "c70" 1493 | ], 1494 | "language": "python", 1495 | "metadata": {}, 1496 | "outputs": [] 1497 | }, 1498 | { 1499 | "cell_type": "markdown", 1500 | "metadata": { 1501 | "hidden": false 1502 | }, 1503 | "source": [ 1504 | "To finish off I need to create a unit for Astronomical Units:" 1505 | ] 1506 | }, 1507 | { 1508 | "cell_type": "code", 1509 | "collapsed": false, 1510 | "input": [ 1511 | "-- From http://en.wikipedia.org/wiki/Astronomical_unit \n", 1512 | "data AstronomicalUnit = AstronomicalUnit\n", 1513 | "\n", 1514 | "instance Unit AstronomicalUnit where\n", 1515 | " type BaseUnit AstronomicalUnit = Meter\n", 1516 | " conversionRatio _ = 149597870700" 1517 | ], 1518 | "language": "python", 1519 | "metadata": {}, 1520 | "outputs": [] 1521 | }, 1522 | { 1523 | "cell_type": "markdown", 1524 | "metadata": { 1525 | "hidden": false 1526 | }, 1527 | "source": [ 1528 | "With this, I can convert to the (completely made up) units of kilo-tonnes per cubic au:" 1529 | ] 1530 | }, 1531 | { 1532 | "cell_type": "code", 1533 | "collapsed": false, 1534 | "input": [ 1535 | "c70 # (Kilo :@ Ton :/ (AstronomicalUnit :^ sThree))" 1536 | ], 1537 | "language": "python", 1538 | "metadata": {}, 1539 | "outputs": [] 1540 | }, 1541 | { 1542 | "cell_type": "heading", 1543 | "level": 2, 1544 | "metadata": { 1545 | "hidden": false 1546 | }, 1547 | "source": [ 1548 | "Representing angles" 1549 | ] 1550 | }, 1551 | { 1552 | "cell_type": "markdown", 1553 | "metadata": { 1554 | "hidden": false 1555 | }, 1556 | "source": [ 1557 | "The following is what I came up with when attempting to add in support for an \"angle dimension\". I make no guarantee that\n", 1558 | "it's sensible, but it seems to work." 1559 | ] 1560 | }, 1561 | { 1562 | "cell_type": "code", 1563 | "collapsed": false, 1564 | "input": [ 1565 | "-- This is based on the Number and Dimensionless types from Data.Metrology.Units \n", 1566 | "data AngleDim = AngleDim\n", 1567 | "\n", 1568 | "instance Dimension AngleDim where\n", 1569 | " type DimFactorsOf AngleDim = '[]\n", 1570 | "\n", 1571 | "type instance DefaultUnitOfDim AngleDim = Radian\n", 1572 | "\n", 1573 | "data Radian = Radian\n", 1574 | "instance Unit Radian where\n", 1575 | " type BaseUnit Radian = Canonical\n", 1576 | " type DimOfUnit Radian = AngleDim\n", 1577 | " type UnitFactorsOf Radian = '[]\n", 1578 | "\n", 1579 | "data Degree = Degree\n", 1580 | "instance Unit Degree where\n", 1581 | " type BaseUnit Degree = Radian\n", 1582 | " conversionRatio _ = realToFrac (pi / 180.0)\n", 1583 | "\n", 1584 | "data ArcMinute = ArcMinute\n", 1585 | "instance Unit ArcMinute where\n", 1586 | " type BaseUnit ArcMinute = Degree\n", 1587 | " conversionRatio _ = (1/60)\n", 1588 | "\n", 1589 | "data ArcSecond = ArcSecond\n", 1590 | "instance Unit ArcSecond where\n", 1591 | " type BaseUnit ArcSecond = ArcMinute\n", 1592 | " conversionRatio _ = (1/60)\n", 1593 | "\n", 1594 | "-- I have already defined an Angle, so I add Type to the name\n", 1595 | "-- here to have a different symbol name.\n", 1596 | "type AngleType = MkQu_U Radian" 1597 | ], 1598 | "language": "python", 1599 | "metadata": {}, 1600 | "outputs": [] 1601 | }, 1602 | { 1603 | "cell_type": "markdown", 1604 | "metadata": { 1605 | "hidden": false 1606 | }, 1607 | "source": [ 1608 | "With this, I can create a value in radians and convert it to different units:" 1609 | ] 1610 | }, 1611 | { 1612 | "cell_type": "code", 1613 | "collapsed": false, 1614 | "input": [ 1615 | "angle = 0.4 % Radian\n", 1616 | "angle" 1617 | ], 1618 | "language": "python", 1619 | "metadata": {}, 1620 | "outputs": [] 1621 | }, 1622 | { 1623 | "cell_type": "markdown", 1624 | "metadata": { 1625 | "hidden": false 1626 | }, 1627 | "source": [ 1628 | "As this is a dimensionless number, the first argument to `Qu` is empty:" 1629 | ] 1630 | }, 1631 | { 1632 | "cell_type": "code", 1633 | "collapsed": false, 1634 | "input": [ 1635 | ":type angle" 1636 | ], 1637 | "language": "python", 1638 | "metadata": {}, 1639 | "outputs": [] 1640 | }, 1641 | { 1642 | "cell_type": "code", 1643 | "collapsed": false, 1644 | "input": [ 1645 | "3 *| angle\n", 1646 | ":t 3 *| angle" 1647 | ], 1648 | "language": "python", 1649 | "metadata": { 1650 | "hidden": false 1651 | }, 1652 | "outputs": [] 1653 | }, 1654 | { 1655 | "cell_type": "markdown", 1656 | "metadata": { 1657 | "hidden": false 1658 | }, 1659 | "source": [ 1660 | "Since the angles are dimensionless, you do not need to use the special\n", 1661 | "operators on them, as shown below. I shall keep using the operators\n", 1662 | "from `units` just to act as a form of documentation. " 1663 | ] 1664 | }, 1665 | { 1666 | "cell_type": "code", 1667 | "collapsed": false, 1668 | "input": [ 1669 | "3 * angle\n", 1670 | ":t 3 * angle" 1671 | ], 1672 | "language": "python", 1673 | "metadata": { 1674 | "hidden": false 1675 | }, 1676 | "outputs": [] 1677 | }, 1678 | { 1679 | "cell_type": "markdown", 1680 | "metadata": { 1681 | "hidden": false 1682 | }, 1683 | "source": [ 1684 | "Values can be converted between units:" 1685 | ] 1686 | }, 1687 | { 1688 | "cell_type": "code", 1689 | "collapsed": false, 1690 | "input": [ 1691 | "(3 *| angle) # Degree\n", 1692 | "(1.2 % ArcMinute) # ArcSecond" 1693 | ], 1694 | "language": "python", 1695 | "metadata": { 1696 | "hidden": false 1697 | }, 1698 | "outputs": [] 1699 | }, 1700 | { 1701 | "cell_type": "markdown", 1702 | "metadata": { 1703 | "hidden": false 1704 | }, 1705 | "source": [ 1706 | "The `units` package defines a dimensionless type, `Number`, which can be used to\n", 1707 | "\"extract\" the numeric value from these angles." 1708 | ] 1709 | }, 1710 | { 1711 | "cell_type": "code", 1712 | "collapsed": false, 1713 | "input": [ 1714 | "(3 *| angle) # Number\n", 1715 | ":t (3 *| angle) # Number" 1716 | ], 1717 | "language": "python", 1718 | "metadata": {}, 1719 | "outputs": [] 1720 | }, 1721 | { 1722 | "cell_type": "markdown", 1723 | "metadata": { 1724 | "hidden": false 1725 | }, 1726 | "source": [ 1727 | "With all this I can write a version of `adist` that uses such an angle:" 1728 | ] 1729 | }, 1730 | { 1731 | "cell_type": "code", 1732 | "collapsed": false, 1733 | "input": [ 1734 | "adist2 :: Double -> Frequency -> Double -> AngleType -> Length\n", 1735 | "adist2 om h0 z angle = da om h0 z |*| angle" 1736 | ], 1737 | "language": "python", 1738 | "metadata": {}, 1739 | "outputs": [] 1740 | }, 1741 | { 1742 | "cell_type": "markdown", 1743 | "metadata": { 1744 | "hidden": false 1745 | }, 1746 | "source": [ 1747 | "The results are the same as above:" 1748 | ] 1749 | }, 1750 | { 1751 | "cell_type": "code", 1752 | "collapsed": false, 1753 | "input": [ 1754 | "adist2 0.3 (hubbleConstant 70) 2 (1 % ArcSecond) # Kilo :@ Parsec" 1755 | ], 1756 | "language": "python", 1757 | "metadata": {}, 1758 | "outputs": [] 1759 | }, 1760 | { 1761 | "cell_type": "code", 1762 | "collapsed": false, 1763 | "input": [ 1764 | "adist2 0.3 (hubbleConstant 70) 2 (2.4 % ArcMinute) # Mega :@ Parsec" 1765 | ], 1766 | "language": "python", 1767 | "metadata": {}, 1768 | "outputs": [] 1769 | }, 1770 | { 1771 | "cell_type": "markdown", 1772 | "metadata": { 1773 | "hidden": false 1774 | }, 1775 | "source": [ 1776 | "I did find one \"surprising\" thing with this version: even though the fourth argument is\n", 1777 | "given as `AngleType`, you can actually give a number, which the compiler will interpret\n", 1778 | "as a value in radians. So, repeating the calculation for one arcsecond, I get the same result,\n", 1779 | "after explicitly converting the angle to degrees):" 1780 | ] 1781 | }, 1782 | { 1783 | "cell_type": "code", 1784 | "collapsed": false, 1785 | "input": [ 1786 | "adist2 0.3 (hubbleConstant 70) 2 (pi/3600/180) # Kilo :@ Parsec" 1787 | ], 1788 | "language": "python", 1789 | "metadata": {}, 1790 | "outputs": [] 1791 | }, 1792 | { 1793 | "cell_type": "heading", 1794 | "level": 2, 1795 | "metadata": { 1796 | "hidden": false 1797 | }, 1798 | "source": [ 1799 | "Did I learn anything?" 1800 | ] 1801 | }, 1802 | { 1803 | "cell_type": "markdown", 1804 | "metadata": { 1805 | "hidden": false 1806 | }, 1807 | "source": [ 1808 | "Overall, I would say that there's not a huge difference in *using* the two packages `dimensional-tf` and `units`\n", 1809 | "(I best hurry and add a disclaimer pointing out that this really isn't enough code to really determine a\n", 1810 | "preference, and also that I don't want this to be an \"a is better than b\" style post).\n", 1811 | "\n", 1812 | "The two packages do things differently, which has implications on how things are defined, what needs to be written, how\n", 1813 | "readable are the error messages, and what is done in the value system as compared to the type system, but\n", 1814 | "for the purposes of this notebook the actual code implementing the equations are not too dissimilar:\n", 1815 | "\n", 1816 | " - `dimensional-tf`\n", 1817 | " \n", 1818 | "~~~~\n", 1819 | "bigG = 6.67428e-11 P.*~ (P.metre P.^ P.pos3 P./ P.kilo P.gram P./ P.second P.^ P.pos2)\n", 1820 | "criticalDensity h = P._3 P.* h P.^ P.pos2 P./ (P._8 P.* P.pi P.* bigG)\n", 1821 | "~~~~\n", 1822 | "\n", 1823 | " - `units`\n", 1824 | " \n", 1825 | "~~~~\n", 1826 | "bigG = 6.67428e-11 % (Meter :^ sThree :/ Kilo :@ Gram :/ Second :^ sTwo)\n", 1827 | "criticalDensity :: Frequency -> Density\n", 1828 | "criticalDensity h = redim (3 *| h |*| h |/| (8 * pi *| bigG))\n", 1829 | "~~~~\n", 1830 | "\n", 1831 | "As `dimensional-tf` is the older package, it has many of the units I needed, and it was easy to add new ones,\n", 1832 | "such as parsec. The `units` and `units-defs` packages are newer, and do not contain as many values or types,\n", 1833 | "but it is also not hard to add in new ones, as shown above.\n", 1834 | "\n", 1835 | "The `units` package avoids the need for the qualified terms that I used with the `dimensional-tf` version,\n", 1836 | "for instance the `P.XXX` forms above, but this is in part due to defining its own mathematical operators,\n", 1837 | "such as `|*` and `|/|`. The `units` version is slightly-more readable to me, but this is not enough code\n", 1838 | "to really decide if either has a strong advantage. There's also the fact that you benefit most from the\n", 1839 | "things that don't get shown here - i.e. all the wrong turns and errors that the type system complains about\n", 1840 | "until you get it right - and I really have not had enough experience of the two packages to make any comment\n", 1841 | "on that point.\n", 1842 | "\n", 1843 | "One concern, when using a units package, is that of numeric precision: if the values all get converted to some\n", 1844 | "base unit for each dimension, could large (or small) values lead to numerical issues. Since I used `Double`\n", 1845 | "types here, and the values didn't get too huge (in part because I never got around to calculating cosmological\n", 1846 | "volumes), this didn't seem to be a problem. Section 5.2 of \n", 1847 | "[\"Experience Report: Type-checking Polymorphic Units for Astrophysics Research in Haskell\"](http://www.cis.upenn.edu/~eir/papers/2014/units/units.pdf)\n", 1848 | "describes how `units` can handle this; I'm not sure how to cope with this in the `dimensional-tf` package. " 1849 | ] 1850 | }, 1851 | { 1852 | "cell_type": "heading", 1853 | "level": 2, 1854 | "metadata": { 1855 | "hidden": false 1856 | }, 1857 | "source": [ 1858 | "The end" 1859 | ] 1860 | }, 1861 | { 1862 | "cell_type": "markdown", 1863 | "metadata": { 1864 | "hidden": false 1865 | }, 1866 | "source": [ 1867 | "There you go; I hope you enjoyed it. If you have any questions, then please use the\n", 1868 | "[GitHub issues page](https://github.com/DougBurke/astro-haskell/issues) or\n", 1869 | "contact me on Twitter at\n", 1870 | "." 1871 | ] 1872 | } 1873 | ], 1874 | "metadata": {} 1875 | } 1876 | ] 1877 | } --------------------------------------------------------------------------------