├── .gitignore ├── LICENSE ├── README.md ├── compGeo.pdf └── source ├── data ├── ch2-5 │ └── volc_longlat.csv ├── ch5-3 │ ├── XG.txt │ ├── YG.txt │ └── ZG.txt ├── ch5-4 │ ├── js.txt │ ├── jtc.txt │ └── kp.txt ├── ch5-6 │ └── beasd.txt ├── ch5-exercise1 │ ├── sdtp.kml │ └── sdtp.txt ├── ch5-exercise2 │ ├── dpa.txt │ └── mlv.txt ├── ch5-exercise3 │ ├── XGE.txt │ ├── YGE.txt │ └── ZGE.txt ├── ch6-1 │ └── beasd.txt ├── ch6-2 │ └── jske.txt ├── ch6-exercise1 │ ├── csdtp.kml │ └── csdtp.txt ├── ch6-exercise2 │ ├── biaxial.txt │ ├── girdle.txt │ └── random.txt ├── ch6-exercise3 │ ├── fol.txt │ └── kfol.txt ├── ch8-1 │ └── jujuy.txt ├── ch8-2 │ └── tibet.txt ├── ch8-3 │ └── demfault.txt ├── ch8-exercise1 │ └── bar.txt ├── ch8-exercise2 │ └── andes.txt ├── ch8-exercise3 │ └── antithetic.txt ├── ch8-exercise4 │ └── trilobite.txt └── ch9-2 │ └── loads.txt ├── functions ├── CGeo_elastic.py ├── angles.py ├── angles_u.py ├── bingham.py ├── calc_mv.py ├── cart_to_sph.py ├── cart_to_sph_u.py ├── cauchy.py ├── dircos_axes.py ├── disloc2d.py ├── down_plunge.py ├── fin_strain.py ├── fit_plane.py ├── flex2d.py ├── general_shear.py ├── geogr_to_view.py ├── great_circle.py ├── grid_fin_strain.py ├── grid_inf_strain.py ├── hoop.py ├── inf_strain.py ├── lscov.py ├── mohr_circle_stress.py ├── outcrop_trace.py ├── pole.py ├── principal_stress.py ├── pt_axes.py ├── pure_shear.py ├── rotate.py ├── shear_on_plane.py ├── simple_shear.py ├── small_circle.py ├── sph_to_cart.py ├── sph_to_cart_u.py ├── st_coord_line.py ├── stereonet.py ├── three_points.py ├── three_points_u.py ├── transform_stress.py ├── true_thickness.py ├── true_thickness_u.py └── zero_twopi.py └── notebooks ├── ch1.ipynb ├── ch2-1.ipynb ├── ch2-2.ipynb ├── ch2-3.ipynb ├── ch2-4.ipynb ├── ch2-5.ipynb ├── ch3-fig6.ipynb ├── ch3-fig7b.ipynb ├── ch3-fig7c.ipynb ├── ch3.ipynb ├── ch4-1.ipynb ├── ch4-2.ipynb ├── ch4-3.ipynb ├── ch4-4.ipynb ├── ch4-5.ipynb ├── ch4-6.ipynb ├── ch5-1.ipynb ├── ch5-2.ipynb ├── ch5-3.ipynb ├── ch5-4.ipynb ├── ch5-5.ipynb ├── ch5-6.ipynb ├── ch6-1.ipynb ├── ch6-2.ipynb ├── ch7-1.ipynb ├── ch7-2.ipynb ├── ch7-3.ipynb ├── ch8-1.ipynb ├── ch8-2.ipynb ├── ch8-3.ipynb ├── ch8-4.ipynb ├── ch8-5.ipynb ├── ch8-6.ipynb ├── ch9-1.ipynb ├── ch9-2.ipynb ├── ch9-3.ipynb ├── ch9-4.ipynb └── mylib.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | __pycache__ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Nestor Cardozo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Computational Geosciences 2 | Welcome to the Computational Geosciences resource at the University of Stavanger (UiS), Norway. This is an educational project funded by the Faculty of Science and Technology at UiS. The resource is made by students and faculty from the departments of Energy Resources (IER) and Mechanical and Structural Engineering (IMBM). 3 | 4 | Students: Angela Hoch (IER), Adham Amer (IMBM), Vania Mansoor and Linda Olsen (IER). 5 | 6 | Postdocs: David Oakley (IER) 7 | 8 | Faculty: Nestor Cardozo (IER), Lisa Watson (IER), Wiktor Weibull (IER), and Knut Giljarhus (IMBM). 9 | 10 | Please feel free to use this material for teaching and research. If you have any comments or want to contribute to the resource, please contact me at [nestor.cardozo@uis.no](mailto:nestor.cardozo@uis.no) 11 | 12 | ## Manual: Working with the resource 13 | The resource consists of a book in pdf format (compGeo.pdf), and a source folder where data, functions and notebooks are included. The book is a large, full-resolution file (18 MB). 14 | 15 | The programming language of choice is Python, and our approach is as follows: We introduce briefly the theory and applications, implement them in Python functions, and illustrate them using Jupyter notebooks. 16 | 17 | ### Clone the repository 18 | The best way to work with the resource is to clone this repository. This saves all material to your local machine. It behaves almost like a copy. 19 | 1. Open a terminal. 20 | 2. Navigate to the folder where you would like to store the local copy of the repository. (`cd `) 21 | 3. Press the green button 'Code' on the right hand side and copy the path in the Clone section. 22 | 4. Execute the terminal command `git clone `. 23 | 5. Now you can start working with the resource files. They are saved in the folder you chose in step 2. 24 | 25 | ### Updating your local files 26 | Once in a while you should update your local files to the latest changes in the repository. This is important since we will be making changes and including new chapters along. 27 | 1. Open a terminal. 28 | 2. Navigate inside the folder of the repository on your machine. (`cd `) 29 | 3. Execute the terminal command `git pull` 30 | 31 | ## Background information 32 | The notebooks follow the directory structure of the resource, which is based on data, functions and notebooks folders. We recommend that you follow the same directory structure when running the notebooks. 33 | 34 | ## Current state 35 | The first nine chapters are completed. The latest revision of these chapters on Aug. 2024 was significant. Please update. The last chapter (Ch. 10) on the inversion problem is in progress. 36 | -------------------------------------------------------------------------------- /compGeo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nfcd/compGeo/a6f99922ed70807a6cd32bd761809732423a3d3c/compGeo.pdf -------------------------------------------------------------------------------- /source/data/ch5-4/js.txt: -------------------------------------------------------------------------------- 1 | -26 7340 8200 2 | 543 7160 8320 3 | 800 6680 8200 4 | 918 6611 8160 5 | 1002 6604 8200 6 | 1439 6424 8200 7 | 1835 6042 8000 8 | 2231 5611 7800 9 | 2668 5187 7600 10 | 2835 5062 7480 11 | 3050 4979 7600 12 | 3620 4743 7760 13 | 3911 4569 7640 14 | 4147 4486 7800 15 | 4307 4368 7720 16 | 4647 4305 7790 17 | 5029 4055 7600 18 | 5425 3868 7400 19 | 5668 3868 7400 20 | 5981 3903 7600 21 | 6418 4021 7800 22 | 6696 4125 8000 23 | 7071 4174 8120 24 | 7453 4111 8000 25 | 8272 3993 7820 26 | 9113 3819 7800 27 | 9536 3625 7720 28 | 9738 3486 7800 29 | 10134 3347 7900 30 | 10252 3514 8000 31 | 10272 3854 8130 32 | 10064 4458 8160 33 | 10064 5083 8000 34 | 10113 5618 7880 35 | 9877 5979 8000 36 | 9932 6618 7800 37 | 10085 6965 7600 38 | 10210 7208 7400 39 | 10224 7354 7300 40 | 10120 7472 7400 41 | 9800 7757 7600 42 | 9245 8167 7800 43 | 8807 8396 8000 44 | 8481 8660 8200 45 | 8550 9035 8000 46 | 8689 9312 7800 47 | 8703 9417 7750 48 | 8495 9875 7800 49 | 8571 10285 7600 50 | 8675 10618 7400 51 | 8738 10840 7280 52 | 8585 10972 7400 53 | 8293 11236 7600 54 | 8120 11805 7400 55 | 7835 12000 7600 56 | 7384 12187 7800 57 | 7356 12861 7600 58 | 7453 13271 7400 59 | 7592 13708 7200 60 | 7717 14000 7000 61 | 7891 14333 6800 62 | 7932 14562 6700 63 | 7786 14701 6800 64 | 7397 14958 7000 65 | 7050 15278 7200 66 | 6766 15833 7280 67 | 6717 16076 7200 68 | 6661 16264 7160 69 | 6502 16444 7240 70 | 6460 16611 7200 71 | 6509 16937 7000 72 | 6460 17097 7000 73 | 6266 17389 7160 74 | 6196 17903 7000 75 | 5946 18542 7150 76 | 5717 19021 7000 77 | 5481 19493 6680 78 | 5356 19687 6800 79 | 5036 19986 7000 80 | 4925 20410 6920 81 | 4696 20680 7000 82 | 4446 20930 6800 83 | 4217 21451 6600 84 | 4113 21812 6400 85 | 3981 22076 6280 86 | 3814 22187 6400 87 | 3446 22333 6600 88 | 2995 22465 6800 89 | 2675 22507 7000 90 | 2217 22500 7200 91 | 1863 22382 7200 92 | 1474 22458 7400 93 | 1279 22396 7400 94 | 1050 22500 7280 95 | 835 22812 7400 96 | -5 23299 7590 97 | -------------------------------------------------------------------------------- /source/data/ch5-4/jtc.txt: -------------------------------------------------------------------------------- 1 | -30 8303 8200 2 | 332 8435 8400 3 | 686 8574 8600 4 | 1019 8595 8800 5 | 1151 8477 8800 6 | 1422 8345 8600 7 | 1720 8081 8400 8 | 2068 7609 8200 9 | 2345 7095 8000 10 | 2672 6526 7800 11 | 2866 6220 7680 12 | 3158 6158 7800 13 | 3644 6047 8000 14 | 3880 6005 8080 15 | 4130 5866 8000 16 | 4609 5505 7800 17 | 5040 5248 7600 18 | 5470 4887 7520 19 | 5623 4880 7600 20 | 5984 5060 7800 21 | 6248 5206 8000 22 | 6554 5338 8200 23 | 7075 5595 8320 24 | 6998 5901 8400 25 | 6755 6088 8440 26 | 6908 6317 8490 27 | 6922 6519 8500 28 | 7227 6491 8400 29 | 7734 6449 8200 30 | 8116 6505 8000 31 | 8561 6727 7800 32 | 8575 6817 7750 33 | 8533 6935 7800 34 | 8193 7255 8000 35 | 7832 7547 8200 36 | 7533 7776 8400 37 | 7165 8116 8580 38 | 7075 8616 8400 39 | 7075 9019 8200 40 | 7047 9602 8000 41 | 7040 10088 7840 42 | 6727 10387 8000 43 | 6158 10831 8200 44 | 6095 10991 8200 45 | 6082 11415 8000 46 | 6193 12012 7800 47 | 6207 12276 7600 48 | 6283 12928 7400 49 | 6304 13290 7200 50 | 6276 13866 7000 51 | 6234 14067 6900 52 | 6109 14324 7000 53 | 5797 14699 7200 54 | 5345 15053 7400 55 | 4825 15658 7400 56 | 4227 16047 7600 57 | 3894 16401 7680 58 | 3804 16734 7600 59 | 3707 17262 7460 60 | 3498 17609 7520 61 | 3429 17901 7400 62 | 3193 18470 7360 63 | 2991 18706 7200 64 | 2929 19422 7000 65 | 2859 19658 6800 66 | 2998 20560 6600 67 | 2977 20866 6400 68 | 2179 21109 6600 69 | 1533 21415 6800 70 | 297 22172 7000 71 | -16 22373 7080 72 | -------------------------------------------------------------------------------- /source/data/ch5-4/kp.txt: -------------------------------------------------------------------------------- 1 | 106 6000 8000 2 | 613 5646 7800 3 | 1141 5278 7600 4 | 1453 5014 7600 5 | 2071 4583 7400 6 | 2759 4146 7240 7 | 3078 3930 7300 8 | 3328 3771 7200 9 | 3731 3576 7240 10 | 4106 3424 7200 11 | 4953 3250 7280 12 | 5168 3250 7180 13 | 5891 3340 7400 14 | 6272 3389 7600 15 | 6724 3403 7800 16 | 6981 3382 7900 17 | 7592 3271 7800 18 | 8113 3104 7600 19 | 8793 2875 7400 20 | 9543 2458 7280 21 | 10522 2007 7400 22 | 11022 1812 7600 23 | 11182 1764 7600 24 | 11738 1632 7400 25 | 12252 1535 7200 26 | 12974 1424 7000 27 | 13377 1424 6920 28 | 13550 1764 6920 29 | 13404 2528 6840 30 | 13259 2958 6880 31 | 13120 3326 7000 32 | 12946 3771 7120 33 | 12897 4042 7040 34 | 12647 4472 7200 35 | 12154 5153 7400 36 | 11946 5403 7480 37 | 11911 5590 7400 38 | 11891 6153 7200 39 | 11884 6625 7060 40 | 12286 7000 6960 41 | 12210 7062 7000 42 | 11897 7222 7120 43 | 11807 7389 7000 44 | 11745 7535 6920 45 | 11509 8028 7000 46 | 10870 8764 7200 47 | 10272 9354 7400 48 | 9828 9764 7590 49 | 9877 10194 7400 50 | 9904 10375 7280 51 | 9599 10646 7400 52 | 9585 11312 7200 53 | 9592 11569 7050 54 | 9397 11944 7080 55 | 9293 12590 7000 56 | 8793 14055 6800 57 | 8620 15083 6600 58 | 8509 15292 6600 59 | 8266 15535 6680 60 | 8238 15861 6600 61 | 7786 17083 6600 62 | 7613 17444 6580 63 | 7217 18472 6480 64 | 7064 18667 6560 65 | 6404 20055 6400 66 | 6272 20187 6400 67 | 5842 20465 6600 68 | 5682 20785 6450 69 | 5349 21132 6600 70 | 5085 21292 6720 71 | 4946 21632 6600 72 | 4828 22049 6400 73 | 4738 22361 6200 74 | 4425 22680 6400 75 | 3766 23271 6600 76 | 2988 23986 6760 77 | 2870 24271 6680 78 | -------------------------------------------------------------------------------- /source/data/ch5-6/beasd.txt: -------------------------------------------------------------------------------- 1 | 114.0 40.0 2 | 334.0 60.0 3 | 73.0 55.0 4 | 352.0 90.0 5 | 93.0 80.0 6 | 100.0 70.0 7 | 93.0 55.0 8 | 91.0 70.0 9 | 057.0 35.0 10 | 95.0 65.0 11 | 345.0 50.0 12 | 340.0 85.0 13 | 95.0 65.0 14 | 350.0 65.0 15 | 101.0 50.0 16 | 339.0 50.0 17 | 93.0 45.0 18 | 90.0 45.0 19 | 332.0 40.0 20 | 339.0 45.0 21 | 118.0 60.0 22 | 11.0 30.0 23 | 72.0 50.0 24 | 121.0 55.0 25 | 121.0 45.0 26 | 96.0 45.0 27 | 107.0 30.0 28 | 343.0 30.0 29 | 114.0 55.0 30 | 314.0 45.0 31 | 98.0 33.0 32 | 338.0 45.0 33 | 84.0 45.0 34 | 135.0 20.0 35 | 314.0 50.0 36 | 321.0 50.0 37 | 341.0 50.0 38 | 313.0 50.0 39 | 335.0 65.0 40 | 346.0 70.0 41 | 310.0 35.0 42 | 351.0 75.0 43 | 341.0 30.0 44 | 319.0 53.0 45 | 327.0 55.0 46 | 333.0 44.0 47 | 326.0 40.0 48 | 329.0 60.0 49 | 4.0 55.0 50 | 329.0 50.0 51 | 329.0 50.0 52 | 312.0 45.0 53 | 316.0 30.0 54 | 318.0 50.0 55 | 310.0 50.0 56 | 316.0 60.0 57 | 340.0 70.0 58 | 313.0 35.0 59 | 303.0 50.0 60 | 311.0 40.0 61 | 83.0 25.0 -------------------------------------------------------------------------------- /source/data/ch5-exercise2/dpa.txt: -------------------------------------------------------------------------------- 1 | 622101.998407 5678903.387159 2164.079999 622803.260277 5678452.064545 2285.999999 623136.342290 5677679.556884 2133.599999 622357.161440 5677899.114424 2133.599999 2 | 622101.998407 5678903.387159 2164.079999 622803.260277 5678452.064545 2285.999999 623136.342290 5677679.556884 2133.599999 622638.222740 5677869.001684 2285.999999 3 | 623963.890387 5676134.934119 2346.959999 624119.076035 5677185.325229 2590.799999 623136.342290 5677679.556884 2133.599999 623258.814418 5677419.477842 2438.399999 4 | 623963.890387 5676134.934119 2346.959999 624119.076035 5677185.325229 2590.799999 623136.342290 5677679.556884 2133.599999 623737.222932 5677178.434888 2590.799999 5 | 623963.890387 5676134.934119 2346.959999 624119.076035 5677185.325229 2590.799999 623136.342290 5677679.556884 2133.599999 623903.753736 5677004.770499 2743.199999 6 | 623963.890387 5676134.934119 2346.959999 624119.076035 5677185.325229 2590.799999 623136.342290 5677679.556884 2133.599999 623738.584087 5676710.835799 2590.799999 7 | 623963.890387 5676134.934119 2346.959999 624119.076035 5677185.325229 2590.799999 623136.342290 5677679.556884 2133.599999 623640.181624 5676312.595322 2529.839999 8 | 623963.890387 5676134.934119 2346.959999 624086.342458 5675309.552226 2285.999999 623905.560574 5674968.396350 2011.679999 623571.016533 5675787.556443 2285.999999 9 | 623963.890387 5676134.934119 2346.959999 624086.342458 5675309.552226 2285.999999 623905.560574 5674968.396350 2011.679999 623427.662023 5675473.020454 2133.599999 10 | 623963.890387 5676134.934119 2346.959999 624086.342458 5675309.552226 2285.999999 623905.560574 5674968.396350 2011.679999 623521.424908 5675037.909746 2103.119999 11 | 624011.258186 5674559.059917 1981.199999 624408.858920 5673618.396650 2042.159999 624376.563444 5672856.475728 1798.319999 623959.929101 5674149.380411 2133.599999 12 | 624011.258186 5674559.059917 1981.199999 624408.858920 5673618.396650 2042.159999 624376.563444 5672856.475728 1798.319999 624164.972013 5673823.778272 2133.599999 13 | 624011.258186 5674559.059917 1981.199999 624408.858920 5673618.396650 2042.159999 624376.563444 5672856.475728 1798.319999 624233.008393 5673536.701109 2133.599999 14 | 624011.258186 5674559.059917 1981.199999 624408.858920 5673618.396650 2042.159999 624376.563444 5672856.475728 1798.319999 624147.350117 5673138.873573 1981.199999 15 | 624638.218013 5672485.753979 1828.799999 625663.028394 5671860.952159 2285.999999 625974.425625 5670964.571871 2164.079999 624507.687188 5672026.973597 1828.799999 16 | 624638.218013 5672485.753979 1828.799999 625663.028394 5671860.952159 2285.999999 625974.425625 5670964.571871 2164.079999 624688.635458 5671373.363131 1889.759999 17 | 624638.218013 5672485.753979 1828.799999 625663.028394 5671860.952159 2285.999999 625974.425625 5670964.571871 2164.079999 624893.204592 5671064.685584 1981.199999 18 | 624638.218013 5672485.753979 1828.799999 625663.028394 5671860.952159 2285.999999 625974.425625 5670964.571871 2164.079999 625214.197154 5670976.255637 2133.599999 19 | 624638.218013 5672485.753979 1828.799999 625663.028394 5671860.952159 2285.999999 625974.425625 5670964.571871 2164.079999 625507.264786 5670972.167272 2285.999999 20 | 624638.218013 5672485.753979 1828.799999 625663.028394 5671860.952159 2285.999999 625974.425625 5670964.571871 2164.079999 625759.267375 5670920.026652 2407.919999 21 | 626669.398975 5669309.821788 2133.599999 626625.413084 5670490.458464 2468.879999 625974.425625 5670964.571871 2164.079999 625759.267375 5670920.026652 2407.919999 22 | 626669.398975 5669309.821788 2133.599999 626625.413084 5670490.458464 2468.879999 625974.425625 5670964.571871 2164.079999 626088.112488 5670572.490054 2438.399999 23 | 626669.398975 5669309.821788 2133.599999 626625.413084 5670490.458464 2468.879999 625974.425625 5670964.571871 2164.079999 626310.389049 5670379.121832 2590.799999 24 | 626669.398975 5669309.821788 2133.599999 626625.413084 5670490.458464 2468.879999 625974.425625 5670964.571871 2164.079999 626384.668013 5670168.770179 2438.399999 25 | 626669.398975 5669309.821788 2133.599999 626625.413084 5670490.458464 2468.879999 625974.425625 5670964.571871 2164.079999 626374.706089 5669794.349810 2438.399999 26 | 626669.398975 5669309.821788 2133.599999 626625.413084 5670490.458464 2468.879999 625974.425625 5670964.571871 2164.079999 626488.431319 5669257.746482 2285.999999 27 | 626669.398975 5669309.821788 2133.599999 627013.017565 5668894.721008 2316.479999 626945.404288 5668178.522947 2072.639999 626671.793212 5668948.500192 2438.399999 28 | 626669.398975 5669309.821788 2133.599999 627013.017565 5668894.721008 2316.479999 626945.404288 5668178.522947 2072.639999 626747.849833 5668678.671232 2438.399999 29 | 626669.398975 5669309.821788 2133.599999 627013.017565 5668894.721008 2316.479999 626945.404288 5668178.522947 2072.639999 626685.568933 5668349.468931 2285.999999 30 | 626669.398975 5669309.821788 2133.599999 627013.017565 5668894.721008 2316.479999 626945.404288 5668178.522947 2072.639999 626702.196105 5668077.842071 2285.999999 31 | 627129.217041 5667711.976617 2103.119999 627903.632653 5667228.707506 2499.359999 628361.166908 5665707.336084 2194.559999 627006.344045 5667563.855342 2285.999999 32 | 627129.217041 5667711.976617 2103.119999 627903.632653 5667228.707506 2499.359999 628361.166908 5665707.336084 2194.559999 627377.195185 5667230.334577 2438.399999 33 | 627129.217041 5667711.976617 2103.119999 627903.632653 5667228.707506 2499.359999 628361.166908 5665707.336084 2194.559999 627405.686077 5666848.518716 2285.999999 34 | 627129.217041 5667711.976617 2103.119999 627903.632653 5667228.707506 2499.359999 628361.166908 5665707.336084 2194.559999 627454.839995 5666059.214736 1981.199999 35 | 627129.217041 5667711.976617 2103.119999 627903.632653 5667228.707506 2499.359999 628361.166908 5665707.336084 2194.559999 627737.609722 5665973.987635 2133.599999 36 | 628470.646704 5664464.919663 1889.759999 628771.546927 5665187.859318 2377.439999 628361.166908 5665707.336084 2194.559999 627667.865064 5665610.542185 1981.199999 37 | 628470.646704 5664464.919663 1889.759999 628771.546927 5665187.859318 2377.439999 628361.166908 5665707.336084 2194.559999 627746.942363 5665098.470211 1828.799999 38 | 628470.646704 5664464.919663 1889.759999 628771.546927 5665187.859318 2377.439999 628361.166908 5665707.336084 2194.559999 627935.351302 5664478.964237 1737.359999 39 | 628470.646704 5664464.919663 1889.759999 628853.267682 5664306.026111 2194.559999 628686.441579 5663642.174949 1767.839999 627935.351302 5664478.964237 1737.359999 40 | 628470.646704 5664464.919663 1889.759999 628853.267682 5664306.026111 2194.559999 628686.441579 5663642.174949 1767.839999 628202.581556 5663781.093469 1706.879999 41 | 628470.646704 5664464.919663 1889.759999 628853.267682 5664306.026111 2194.559999 628686.441579 5663642.174949 1767.839999 628323.026085 5663444.518356 1676.399999 42 | 629437.887697 5662086.843336 1645.920000 629591.336164 5663336.965679 2316.479999 628686.441579 5663642.174949 1767.839999 628323.026085 5663444.518356 1676.399999 43 | 629437.887697 5662086.843336 1645.920000 629591.336164 5663336.965679 2316.479999 628686.441579 5663642.174949 1767.839999 628457.070513 5663082.805477 1645.920000 44 | 629437.887697 5662086.843336 1645.920000 629591.336164 5663336.965679 2316.479999 628686.441579 5663642.174949 1767.839999 628625.049122 5662717.825789 1615.440000 45 | 629437.887697 5662086.843336 1645.920000 629591.336164 5663336.965679 2316.479999 628686.441579 5663642.174949 1767.839999 628852.702569 5662205.806371 1584.960000 46 | 629437.887697 5662086.843336 1645.920000 629591.336164 5663336.965679 2316.479999 628686.441579 5663642.174949 1767.839999 628990.552284 5661856.995679 1554.480000 47 | 629437.887697 5662086.843336 1645.920000 630140.850807 5661860.877953 2072.639999 630306.538025 5660020.527569 1463.040000 628990.552284 5661856.995679 1554.480000 48 | 629437.887697 5662086.843336 1645.920000 630140.850807 5661860.877953 2072.639999 630306.538025 5660020.527569 1463.040000 629055.629668 5661527.283098 1493.520000 49 | 629437.887697 5662086.843336 1645.920000 630140.850807 5661860.877953 2072.639999 630306.538025 5660020.527569 1463.040000 629196.720180 5661212.493416 1493.520000 50 | 629437.887697 5662086.843336 1645.920000 630140.850807 5661860.877953 2072.639999 630306.538025 5660020.527569 1463.040000 629596.217978 5660913.875967 1645.920000 51 | 629437.887697 5662086.843336 1645.920000 630140.850807 5661860.877953 2072.639999 630306.538025 5660020.527569 1463.040000 629614.596277 5660587.000848 1524.000000 52 | 629437.887697 5662086.843336 1645.920000 630140.850807 5661860.877953 2072.639999 630306.538025 5660020.527569 1463.040000 629866.739348 5659824.958950 1371.600000 53 | 631146.689406 5658620.975783 1310.640000 631795.422261 5658082.961907 1645.920000 632040.486217 5657414.181486 1432.560000 630914.938788 5658282.521571 1310.640000 54 | 631146.689406 5658620.975783 1310.640000 631795.422261 5658082.961907 1645.920000 632040.486217 5657414.181486 1432.560000 631210.299189 5657917.065390 1371.600000 55 | 631146.689406 5658620.975783 1310.640000 631795.422261 5658082.961907 1645.920000 632040.486217 5657414.181486 1432.560000 631606.837864 5657295.179521 1402.080000 56 | 633085.107708 5655144.708495 1615.440000 632954.059278 5656114.461538 1706.879999 632040.486217 5657414.181486 1432.560000 631852.424593 5657034.522863 1524.000000 57 | 633085.107708 5655144.708495 1615.440000 632954.059278 5656114.461538 1706.879999 632040.486217 5657414.181486 1432.560000 632189.459273 5656695.773113 1676.399999 58 | 633085.107708 5655144.708495 1615.440000 632954.059278 5656114.461538 1706.879999 632040.486217 5657414.181486 1432.560000 632706.759378 5656009.407606 1783.079999 59 | 633085.107708 5655144.708495 1615.440000 632954.059278 5656114.461538 1706.879999 632040.486217 5657414.181486 1432.560000 632854.337277 5655478.126999 1767.839999 60 | 633085.107708 5655144.708495 1615.440000 632954.059278 5656114.461538 1706.879999 632040.486217 5657414.181486 1432.560000 632866.224197 5655223.301345 1676.399999 61 | 633085.107708 5655144.708495 1615.440000 633451.158219 5654122.274439 1798.319999 633472.787124 5652839.051360 1554.480000 633065.973246 5654650.975822 1828.799999 62 | 633085.107708 5655144.708495 1615.440000 633451.158219 5654122.274439 1798.319999 633472.787124 5652839.051360 1554.480000 633234.935789 5654256.332555 1920.239999 63 | 633085.107708 5655144.708495 1615.440000 633451.158219 5654122.274439 1798.319999 633472.787124 5652839.051360 1554.480000 633301.125627 5653888.415851 1828.799999 64 | 633085.107708 5655144.708495 1615.440000 633451.158219 5654122.274439 1798.319999 633472.787124 5652839.051360 1554.480000 633322.425176 5653604.125805 1950.719999 65 | 633085.107708 5655144.708495 1615.440000 633451.158219 5654122.274439 1798.319999 633472.787124 5652839.051360 1554.480000 633297.814283 5653293.060556 1828.799999 66 | 633085.107708 5655144.708495 1615.440000 633451.158219 5654122.274439 1798.319999 633472.787124 5652839.051360 1554.480000 633229.639860 5653019.036632 1676.399999 67 | 633085.107708 5655144.708495 1615.440000 633451.158219 5654122.274439 1798.319999 633472.787124 5652839.051360 1554.480000 633265.734257 5652667.145105 1676.399999 68 | 633801.582116 5651785.855332 1676.399999 633753.865500 5652243.591037 1737.359999 633472.787124 5652839.051360 1554.480000 633441.987169 5652310.969754 1676.399999 69 | 633801.582116 5651785.855332 1676.399999 633753.865500 5652243.591037 1737.359999 633472.787124 5652839.051360 1554.480000 633579.728842 5652106.683113 1828.799999 70 | 633801.582116 5651785.855332 1676.399999 633753.865500 5652243.591037 1737.359999 633472.787124 5652839.051360 1554.480000 633579.728842 5652106.683113 1828.799999 71 | 633801.582116 5651785.855332 1676.399999 633753.865500 5652243.591037 1737.359999 633472.787124 5652839.051360 1554.480000 633666.307909 5651769.116132 1859.279999 72 | -------------------------------------------------------------------------------- /source/data/ch5-exercise2/mlv.txt: -------------------------------------------------------------------------------- 1 | 622316.863251 5676979.587543 2285.999999 623325.627099 5676749.734400 2773.679999 622830.044972 5675583.181463 2255.519999 622197.015304 5676729.510835 2438.399999 2 | 622316.863251 5676979.587543 2285.999999 623325.627099 5676749.734400 2773.679999 622830.044972 5675583.181463 2255.519999 622219.982389 5676390.047165 2285.999999 3 | 622316.863251 5676979.587543 2285.999999 623325.627099 5676749.734400 2773.679999 622830.044972 5675583.181463 2255.519999 622647.874422 5676419.618557 2712.719999 4 | 622316.863251 5676979.587543 2285.999999 623325.627099 5676749.734400 2773.679999 622830.044972 5675583.181463 2255.519999 622625.287047 5676040.570792 2529.839999 5 | 622316.863251 5676979.587543 2285.999999 623325.627099 5676749.734400 2773.679999 622830.044972 5675583.181463 2255.519999 622598.231090 5675529.573364 2407.919999 6 | 622829.943827 5675587.406938 2255.519999 623903.673503 5673760.945254 2407.919999 623242.505126 5672878.536993 1828.799999 622598.060305 5675533.797172 2407.919999 7 | 622829.943827 5675587.406938 2255.519999 623903.673503 5673760.945254 2407.919999 623242.505126 5672878.536993 1828.799999 622983.229356 5674864.881221 2468.879999 8 | 622829.943827 5675587.406938 2255.519999 623903.673503 5673760.945254 2407.919999 623242.505126 5672878.536993 1828.799999 622797.592173 5674544.791205 2285.999999 9 | 622829.943827 5675587.406938 2255.519999 623903.673503 5673760.945254 2407.919999 623242.505126 5672878.536993 1828.799999 622795.038463 5673924.009813 2133.599999 10 | 622829.943827 5675587.406938 2255.519999 623903.673503 5673760.945254 2407.919999 623242.505126 5672878.536993 1828.799999 622726.423157 5673382.090775 1981.199999 11 | 622829.943827 5675587.406938 2255.519999 623903.673503 5673760.945254 2407.919999 623242.505126 5672878.536993 1828.799999 623088.228083 5673490.439799 2194.559999 12 | 622829.943827 5675587.406938 2255.519999 623903.673503 5673760.945254 2407.919999 623242.505126 5672878.536993 1828.799999 622898.246496 5673174.583614 1981.199999 13 | 622829.943827 5675587.406938 2255.519999 623903.673503 5673760.945254 2407.919999 623242.505126 5672878.536993 1828.799999 622853.456435 5672828.939843 1859.279999 14 | 624482.121484 5670334.185641 1981.199999 624074.180909 5672324.677878 2133.599999 623242.505126 5672878.536993 1828.799999 623032.459774 5672664.109513 1981.199999 15 | 624482.121484 5670334.185641 1981.199999 624074.180909 5672324.677878 2133.599999 623242.505126 5672878.536993 1828.799999 623653.114174 5672069.974138 2133.599999 16 | 624482.121484 5670334.185641 1981.199999 624074.180909 5672324.677878 2133.599999 623242.505126 5672878.536993 1828.799999 623866.306789 5672037.949099 2285.999999 17 | 624482.121484 5670334.185641 1981.199999 624074.180909 5672324.677878 2133.599999 623242.505126 5672878.536993 1828.799999 624007.761580 5671710.472350 2285.999999 18 | 624482.121484 5670334.185641 1981.199999 624074.180909 5672324.677878 2133.599999 623242.505126 5672878.536993 1828.799999 624063.846891 5671397.514867 2285.999999 19 | 624482.121484 5670334.185641 1981.199999 624074.180909 5672324.677878 2133.599999 623242.505126 5672878.536993 1828.799999 624247.680205 5670790.690109 2225.039999 20 | 624482.121484 5670334.185641 1981.199999 624074.180909 5672324.677878 2133.599999 623242.505126 5672878.536993 1828.799999 624203.393304 5670287.722931 2133.599999 21 | 624482.121484 5670334.185641 1981.199999 625366.909181 5670275.025003 2468.879999 625193.169092 5669700.242160 2194.559999 624298.902380 5669933.441337 2042.159999 22 | 624482.121484 5670334.185641 1981.199999 625366.909181 5670275.025003 2468.879999 625193.169092 5669700.242160 2194.559999 624502.290521 5669663.104066 2072.639999 23 | 624482.121484 5670334.185641 1981.199999 625366.909181 5670275.025003 2468.879999 625193.169092 5669700.242160 2194.559999 624853.154058 5669430.955280 2133.599999 24 | 625520.169605 5669271.958896 2225.039999 626243.923285 5668778.651023 2590.799999 626101.333934 5668013.565268 2164.079999 625172.350229 5669121.510547 2285.999999 25 | 625520.169605 5669271.958896 2225.039999 626243.923285 5668778.651023 2590.799999 626101.333934 5668013.565268 2164.079999 625205.093781 5668880.092103 2285.999999 26 | 625520.169605 5669271.958896 2225.039999 626243.923285 5668778.651023 2590.799999 626101.333934 5668013.565268 2164.079999 625139.351941 5668525.349224 2072.639999 27 | 625520.169605 5669271.958896 2225.039999 626243.923285 5668778.651023 2590.799999 626101.333934 5668013.565268 2164.079999 625352.103861 5668365.752392 2225.039999 28 | 625520.169605 5669271.958896 2225.039999 626243.923285 5668778.651023 2590.799999 626101.333934 5668013.565268 2164.079999 625406.321839 5668116.513800 2133.599999 29 | 625520.169605 5669271.958896 2225.039999 626243.923285 5668778.651023 2590.799999 626101.333934 5668013.565268 2164.079999 625430.642206 5667730.362934 1950.719999 30 | 626883.386154 5666289.093807 2133.599999 626745.031274 5667500.889725 2468.879999 626101.333934 5668013.565268 2164.079999 625430.642206 5667730.362934 1950.719999 31 | 626883.386154 5666289.093807 2133.599999 626745.031274 5667500.889725 2468.879999 626101.333934 5668013.565268 2164.079999 625711.063120 5667440.918844 2133.599999 32 | 626883.386154 5666289.093807 2133.599999 626745.031274 5667500.889725 2468.879999 626101.333934 5668013.565268 2164.079999 625639.537105 5666996.685756 1859.279999 33 | 626883.386154 5666289.093807 2133.599999 626745.031274 5667500.889725 2468.879999 626101.333934 5668013.565268 2164.079999 625995.477584 5666875.324397 2072.639999 34 | 626883.386154 5666289.093807 2133.599999 626745.031274 5667500.889725 2468.879999 626101.333934 5668013.565268 2164.079999 625987.719724 5666568.942991 1981.199999 35 | 626883.386154 5666289.093807 2133.599999 626745.031274 5667500.889725 2468.879999 626101.333934 5668013.565268 2164.079999 626347.478050 5666464.721023 2164.079999 36 | 626883.386154 5666289.093807 2133.599999 626745.031274 5667500.889725 2468.879999 626101.333934 5668013.565268 2164.079999 626265.203351 5665952.122448 1920.239999 37 | 628501.286337 5661753.316253 1737.359999 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628262.083312 5661941.956306 1981.199999 38 | 628501.286337 5661753.316253 1737.359999 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628443.766485 5661407.302308 1981.199999 39 | 628501.286337 5661753.316253 1737.359999 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628407.059055 5660934.301108 1828.799999 40 | 628501.286337 5661753.316253 1737.359999 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628352.160209 5660499.122215 1676.399999 41 | 628501.286337 5661753.316253 1737.359999 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628326.328082 5660230.448506 1584.960000 42 | 628501.286337 5661753.316253 1737.359999 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628338.082612 5659839.765676 1524.000000 43 | 628501.286337 5661753.316253 1737.359999 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628378.870800 5659471.056664 1463.040000 44 | 629381.732082 5658730.848003 1341.120000 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628378.870800 5659471.056664 1463.040000 45 | 629381.732082 5658730.848003 1341.120000 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628477.329128 5659019.002196 1402.080000 46 | 629381.732082 5658730.848003 1341.120000 628901.082683 5660455.626215 1554.480000 629366.899195 5659789.031821 1676.399999 628582.128472 5658639.427662 1341.120000 47 | 632363.696877 5655701.856035 2011.679999 632418.571080 5655006.187924 1767.839999 632837.871715 5654189.474752 2103.119999 632128.789092 5655325.106594 1981.199999 48 | 632363.696877 5655701.856035 2011.679999 632418.571080 5655006.187924 1767.839999 632837.871715 5654189.474752 2103.119999 632171.098755 5654905.386149 1859.279999 49 | 632363.696877 5655701.856035 2011.679999 632418.571080 5655006.187924 1767.839999 632837.871715 5654189.474752 2103.119999 632374.496732 5654494.686697 1981.199999 50 | 632363.696877 5655701.856035 2011.679999 632418.571080 5655006.187924 1767.839999 632837.871715 5654189.474752 2103.119999 632516.707330 5654282.033608 2133.599999 51 | 632363.696877 5655701.856035 2011.679999 632418.571080 5655006.187924 1767.839999 632837.871715 5654189.474752 2103.119999 632637.138337 5654089.965216 2255.519999 52 | 632717.520572 5653110.339317 1767.839999 632418.571080 5655006.187924 1767.839999 632837.871715 5654189.474752 2103.119999 632637.138337 5654089.965216 2255.519999 53 | 632717.520572 5653110.339317 1767.839999 632418.571080 5655006.187924 1767.839999 632837.871715 5654189.474752 2103.119999 632617.164148 5653766.339415 2164.079999 54 | 632717.520572 5653110.339317 1767.839999 632418.571080 5655006.187924 1767.839999 632837.871715 5654189.474752 2103.119999 632481.258369 5653486.026907 1981.199999 55 | -------------------------------------------------------------------------------- /source/data/ch6-1/beasd.txt: -------------------------------------------------------------------------------- 1 | 114.0 40.0 2 | 334.0 60.0 3 | 73.0 55.0 4 | 352.0 90.0 5 | 93.0 80.0 6 | 100.0 70.0 7 | 93.0 55.0 8 | 91.0 70.0 9 | 057.0 35.0 10 | 95.0 65.0 11 | 345.0 50.0 12 | 340.0 85.0 13 | 95.0 65.0 14 | 350.0 65.0 15 | 101.0 50.0 16 | 339.0 50.0 17 | 93.0 45.0 18 | 90.0 45.0 19 | 332.0 40.0 20 | 339.0 45.0 21 | 118.0 60.0 22 | 11.0 30.0 23 | 72.0 50.0 24 | 121.0 55.0 25 | 121.0 45.0 26 | 96.0 45.0 27 | 107.0 30.0 28 | 343.0 30.0 29 | 114.0 55.0 30 | 314.0 45.0 31 | 98.0 33.0 32 | 338.0 45.0 33 | 84.0 45.0 34 | 135.0 20.0 35 | 314.0 50.0 36 | 321.0 50.0 37 | 341.0 50.0 38 | 313.0 50.0 39 | 335.0 65.0 40 | 346.0 70.0 41 | 310.0 35.0 42 | 351.0 75.0 43 | 341.0 30.0 44 | 319.0 53.0 45 | 327.0 55.0 46 | 333.0 44.0 47 | 326.0 40.0 48 | 329.0 60.0 49 | 4.0 55.0 50 | 329.0 50.0 51 | 329.0 50.0 52 | 312.0 45.0 53 | 316.0 30.0 54 | 318.0 50.0 55 | 310.0 50.0 56 | 316.0 60.0 57 | 340.0 70.0 58 | 313.0 35.0 59 | 303.0 50.0 60 | 311.0 40.0 61 | 83.0 25.0 -------------------------------------------------------------------------------- /source/data/ch6-2/jske.txt: -------------------------------------------------------------------------------- 1 | 485717.2 4788543.3 2289.4 2 | 485761.9 4788518.8 2284.0 3 | 485798.4 4788504.5 2271.8 4 | 485816.7 4788494.2 2268.3 5 | 485836.9 4788467.9 2265.6 6 | 485851.1 4788445.4 2261.8 7 | 485859.2 4788421.1 2258.8 8 | 485865.1 4788374.3 2250.7 9 | 485879.2 4788333.7 2237.5 10 | 485899.4 4788270.7 2207.9 11 | 485915.5 4788234.0 2188.6 12 | 485935.7 4788201.6 2166.5 13 | 485951.9 4788175.1 2159.6 14 | 485962.0 4788136.4 2166.1 15 | 485974.1 4788099.9 2170.6 16 | 485984.1 4788049.1 2168.1 17 | 485986.1 4788006.5 2158.6 18 | 485992.0 4787959.7 2127.2 19 | 485996.0 4787937.4 2119.1 20 | 486016.3 4787915.0 2121.2 21 | 486036.5 4787884.4 2151.7 22 | 486056.7 4787853.9 2161.6 23 | 486073.0 4787825.5 2158.6 24 | 486095.2 4787780.8 2145.5 25 | 486113.4 4787754.3 2114.1 26 | 486154.0 4787725.8 2154.3 27 | 486188.4 4787701.3 2168.7 28 | 486208.7 4787670.8 2175.6 29 | 486218.8 4787646.4 2175.0 30 | 486216.5 4787555.0 2172.1 31 | 486210.2 4787488.0 2153.5 32 | 486208.0 4787406.8 2128.4 33 | 486203.7 4787331.7 2116.5 34 | 486203.5 4787270.7 2099.8 35 | 486209.4 4787207.7 2065.8 36 | 486243.9 4787167.0 2066.1 37 | 486288.5 4787124.3 2054.7 38 | 486353.5 4787099.7 2089.3 39 | 486459.1 4787071.1 2137.6 40 | 486491.6 4787056.8 2155.8 41 | 486544.3 4787020.1 2195.1 42 | 486578.7 4786957.0 2170.8 43 | 486572.3 4786837.2 2189.0 44 | 486545.6 4786750.0 2156.3 45 | 486527.2 4786689.2 2129.5 46 | 486527.0 4786656.6 2112.2 47 | 486549.2 4786585.5 2106.7 48 | 486583.6 4786512.2 2118.4 49 | 486597.6 4786439.1 2081.8 50 | 486603.5 4786388.4 2072.1 51 | 486601.3 4786303.1 2040.5 52 | 486582.8 4786224.0 2022.3 53 | 486572.5 4786181.3 2010.2 54 | 486564.2 4786138.7 1999.3 55 | 486586.5 4786130.4 2003.1 56 | 486619.0 4786099.9 2002.2 57 | 486643.4 4786069.5 2004.1 58 | 486675.8 4786043.0 2033.0 59 | 486706.1 4785985.9 2065.0 60 | 486730.4 4785943.3 2076.3 61 | 486791.3 4785933.0 2103.0 62 | 486862.4 4785920.6 2130.6 63 | 486941.6 4785910.3 2169.1 64 | 487031.0 4785889.7 2205.3 65 | 487100.1 4785869.2 2199.0 66 | 487163.1 4785846.7 2206.7 67 | 487230.0 4785836.5 2247.9 68 | 487303.2 4785809.8 2291.4 69 | 487353.9 4785771.2 2292.2 70 | 487388.3 4785722.3 2278.4 71 | -------------------------------------------------------------------------------- /source/data/ch6-exercise1/csdtp.txt: -------------------------------------------------------------------------------- 1 | 139.0 24.9 2 | 135.0 25.2 3 | 143.5 23.6 4 | 138.5 24.2 5 | 141.9 27.2 6 | 143.4 26.7 7 | 145.4 30.3 8 | 143.0 31.8 9 | 147.3 32.6 10 | 144.2 29.2 11 | 152.2 37.3 12 | 154.9 28.3 13 | 153.1 31.1 14 | 152.9 28.5 15 | 154.3 33.8 16 | 152.9 26.1 17 | 150.1 29.4 18 | 158.5 35.5 19 | 155.2 32.4 20 | 147.8 35.4 21 | 149.6 31.4 22 | 145.0 35.5 23 | 144.6 30.4 24 | 141.8 39.4 25 | 136.7 33.3 26 | 134.6 30.2 27 | 134.8 31.6 28 | 131.2 33.3 29 | 135.9 29.6 30 | 135.0 34.3 31 | 133.0 34.1 32 | 133.5 32.1 33 | 132.8 32.7 34 | 133.0 33.7 35 | 131.5 34.1 36 | 136.3 29.7 37 | 135.5 29.3 38 | 134.9 40.8 39 | 128.3 28.9 40 | 140.8 29.4 41 | 140.5 24.9 42 | 149.0 27.9 43 | 144.7 21.6 44 | 148.7 28.3 45 | 149.7 33.8 46 | 149.2 25.6 47 | 153.8 27.8 48 | 149.5 28.1 49 | 149.2 31.9 50 | 156.0 26.2 51 | 159.2 26.2 52 | 153.4 30.5 53 | 157.3 34.1 54 | 169.6 30.8 55 | 224.4 20.9 56 | 248.2 19.7 57 | 267.1 22.2 58 | 274.6 27.3 59 | 286.6 29.4 60 | 285.1 34.7 61 | 288.4 30.2 62 | 289.2 59.1 63 | 297.9 52.5 64 | 295.8 64.2 65 | 287.7 68.4 66 | 305.5 61.0 67 | 308.1 74.1 68 | 298.6 66.2 69 | 300.3 75.6 70 | 303.4 68.2 71 | 305.8 70.9 72 | 309.8 71.3 73 | 311.3 65.2 74 | 310.4 72.9 75 | 307.7 64.8 -------------------------------------------------------------------------------- /source/data/ch6-exercise2/biaxial.txt: -------------------------------------------------------------------------------- 1 | 297.7 19.2 2 | 301.1 11.8 3 | 299.1 7.1 4 | 303.5 19.4 5 | 306.8 22.8 6 | 306.8 18 7 | 305.2 7.1 8 | 311.5 21.1 9 | 310.1 13.5 10 | 315.2 18.8 11 | 316.5 12.6 12 | 318.9 13.8 13 | 312.6 7.9 14 | 316.1 4.1 15 | 320.2 3.1 16 | 124.6 25.5 17 | 118.4 21.4 18 | 116 10.9 19 | 120.2 10.3 20 | 120 3.8 21 | 121.8 16.3 22 | 124.7 13 23 | 125.1 7.5 24 | 128 19.6 25 | 130.2 13 26 | 133.5 18.3 27 | 134.8 11 28 | 131.1 7.4 29 | 136.5 4.1 30 | 132.5 5.7 -------------------------------------------------------------------------------- /source/data/ch6-exercise2/girdle.txt: -------------------------------------------------------------------------------- 1 | 214.3 5.2 2 | 217.2 22.5 3 | 222.7 16.1 4 | 226.1 26 5 | 234.4 24 6 | 239 42 7 | 241.5 32.5 8 | 255.7 39.2 9 | 246.2 51.6 10 | 254.7 48.2 11 | 269.5 40.2 12 | 266.7 58.4 13 | 278.8 50.8 14 | 292.4 51.5 15 | 293.9 42.7 16 | 306.3 57.2 17 | 311.9 49.6 18 | 322 38.3 19 | 336.2 50.6 20 | 334.8 41.5 21 | 353 46.9 22 | 349 40.6 23 | 347.6 31.9 24 | 8.7 38.3 25 | 356.9 33.2 26 | 18.1 26.1 27 | 9.1 29.6 28 | 10.3 21.6 29 | 20.9 14.7 30 | 24.9 5 -------------------------------------------------------------------------------- /source/data/ch6-exercise2/random.txt: -------------------------------------------------------------------------------- 1 | 211.9 7.1 2 | 182.5 25.6 3 | 153.9 33.3 4 | 140.3 5.9 5 | 123.6 17.6 6 | 120.4 39.6 7 | 159.2 59.3 8 | 217.1 42.8 9 | 241.7 19.9 10 | 265.6 3.5 11 | 256.3 48.6 12 | 237.7 77 13 | 96.1 58 14 | 102 11.7 15 | 84.6 35.3 16 | 46.3 76.8 17 | 277.5 38.8 18 | 282.6 27.7 19 | 305.3 41.3 20 | 319.9 56.9 21 | 55.8 55.1 22 | 51.2 49.4 23 | 64.7 25 24 | 40.6 13.9 25 | 16.5 44.7 26 | 346.2 47.2 27 | 312 20.1 28 | 336.2 21.1 29 | 6.9 17.6 30 | 349.4 3.8 -------------------------------------------------------------------------------- /source/data/ch8-1/jujuy.txt: -------------------------------------------------------------------------------- 1 | 7 57 137 50 TL 2 | 20 78 156 73 TL 3 | 11 60 133 56 TL 4 | 28 47 113 47 TR 5 | 343 69 123 59 TL 6 | 348 40 104 37 TL 7 | 197 76 226 63 NL 8 | 175 78 210 70 NL 9 | 11 84 110 84 TL 10 | 3 88 181 51 TL 11 | 175 75 290 74 NR 12 | 5 67 131 62 TL 13 | 11 82 90 82 TR 14 | 61 58 73 18 TR 15 | 75 56 170 56 TL 16 | 217 75 236 50 TR 17 | 206 78 219 47 TR 18 | 158 57 281 52 TL 19 | 313 81 327 56 TR 20 | 265 41 50 27 TL 21 | 315 70 22 68 TR 22 | 151 70 285 63 TL 23 | 150 65 305 42 TL 24 | 124 57 288 23 TL 25 | 130 71 274 60 TL 26 | 30 54 130 54 TL 27 | 354 35 150 16 TL 28 | 298 40 300 2 TR 29 | 15 72 155 63 TL 30 | 270 80 273 15 TR 31 | 5 39 74 37 TR 32 | 212 15 274 13 TR 33 | 284 65 301 32 TR 34 | 166 74 215 69 TR 35 | 133 51 275 37 TL 36 | 132 45 282 27 TL 37 | 165 47 300 37 TL 38 | 150 51 245 51 TL 39 | 194 84 282 84 TR 40 | 185 70 262 70 TR 41 | 179 75 250 74 TR 42 | 170 45 280 43 TL 43 | 180 50 270 50 TL 44 | 165 58 273 57 TL 45 | 163 55 258 55 TL 46 | 160 60 278 57 TL 47 | 159 58 259 58 TL 48 | 143 42 255 40 TL 49 | 182 62 245 59 TR 50 | 187 51 260 50 TR 51 | 179 57 308 50 TL 52 | 70 40 181 38 TL 53 | 45 40 143 40 TL 54 | 20 22 90 21 TR 55 | 53 40 105 33 TR 56 | 210 49 311 48 TL 57 | 195 69 292 69 TL 58 | 198 85 288 85 TL -------------------------------------------------------------------------------- /source/data/ch8-exercise1/bar.txt: -------------------------------------------------------------------------------- 1 | 195 65 297 64 N 2 | 358 62 75 61 N 3 | 349 59 55 57 N 4 | 355 76 37 70 N 5 | 202 72 331 67 N 6 | 346 60 66 60 N 7 | 208 65 325 62 N 8 | 155 58 263 57 N 9 | 025 70 93 69 N 10 | 190 68 307 66 N -------------------------------------------------------------------------------- /source/data/ch8-exercise2/andes.txt: -------------------------------------------------------------------------------- 1 | 766675.5419 6591081.799 0.004 0.0004 2 | 554820.5314 6582059.947 0.0079 0.0004 3 | 504828.6513 6587208.948 0.0095 0.0024 4 | 529832.6757 6583799.326 0.0096 0.004 5 | 543621.4449 6649070.067 0.0081 0.0029 6 | 557483.1739 6655222.842 0.0082 0.002 7 | 577539.7532 6671932.992 0.0072 0.0014 8 | 520970.3832 6652273.054 0.0093 0.0017 9 | 499451.5459 6655218.589 0.0112 0.0035 10 | 493156.6307 6658618.389 0.0125 0.0037 11 | 492689.0719 6609770.667 0.0109 0.0026 12 | 634814.5884 6589984.398 0.0077 0.001 13 | 599037.65 6663891.897 0.0054 0.0009 14 | 485476.6844 6340465.34 0.0085 0.002 15 | 493222.4659 6315407.516 0.01 0.003 16 | 515341.2929 6332348.956 0.0083 0.0017 17 | 519034.1932 6341832.305 0.0056 0.0007 18 | 519866.7734 6338094.595 0.0068 0.0023 19 | 525964.9096 6322293.235 0.0055 0.0018 20 | 524343.0689 6321987.001 0.0063 0.0029 21 | 519923.7383 6320300.807 0.0079 0.0022 22 | 531662.4074 6502876.199 0.0112 0.0011 23 | 315367.5744 7003038.658 0.0257 0.0084 24 | 157875.0487 5994012.262 0.0387 0.0103 25 | 572797.2229 6503400.58 0.0071 0.0006 26 | 360052.836 6979809.939 0.0209 0.0066 27 | 261321.0036 6484178.74 0.0252 0.007 28 | 460422.9486 6528829.097 0.0161 0.0039 29 | 189542.7611 6086338.841 0.0372 0.0093 30 | 311710.966 6551717.786 0.0254 0.0079 31 | 387940.0588 6794194.915 0.0213 0.0051 32 | 367677.7939 6970265.839 0.0209 0.0059 33 | 558055.4409 5998777.568 0.0053 -0.001 34 | 836323.0038 6720376.91 0.0033 0.0008 35 | 556338.3611 6737196.115 0.0081 0.0012 36 | 444952.7851 6934165.589 0.0198 0.004 37 | 285680.9823 6137411.371 0.028 0.0068 38 | 282440.6924 6847777.406 0.0289 0.0072 39 | 409231.4094 7041382.755 0.0225 0.0051 40 | 394470.5422 6683027.116 0.0184 0.0065 41 | 220017.6672 6185775.868 0.0312 0.0115 42 | 450937.4456 6046217.098 0.0093 -0.0005 43 | 602125.3059 6535677.395 0.0074 0.0012 44 | 327735.0953 6656790.076 0.0209 0.0087 45 | 828212.9537 6313362.059 0.0029 -0.0003 46 | 261224.4634 6352369.839 0.0285 0.0123 47 | 545414.8602 6139441.966 0.005 0.0013 48 | 427908.0858 6645206.717 0.0154 0.0052 49 | 579429.6148 6533457.411 0.0098 0.0014 50 | 259930.4824 6613053.697 0.0233 0.0084 51 | 454300.4229 7082771.492 0.0202 0.0075 52 | 539829.2767 7021044.262 0.0169 0.008 53 | 422074.1784 6883533.519 0.0194 0.0046 54 | 933072.5647 6472991.646 0.0026 -0.0001 55 | 291912.3165 6412395.837 0.0225 0.0083 56 | 287865.5906 6037156.352 0.0292 0.0055 57 | 980611.4349 6902970.32 0.0043 0.0007 58 | 255589.8449 6275365.408 0.0239 0.0109 59 | 344382.2493 6330811.24 0.0193 0.0046 60 | 357492.1306 6210515.856 0.0177 0.0077 61 | 329970.9613 6764111.301 0.0209 0.005 62 | 820193.5152 7037823.838 0.0043 0.0011 63 | 345887.0121 6121848.573 0.0221 0.0087 64 | 654544.2666 6872967.188 0.008 0.0019 65 | 282922.2844 6739028.954 0.0205 0.0073 66 | 293562.01 6919599.77 0.0262 0.0088 67 | 874679.7569 7025349.678 0.0027 -0.0012 68 | 467304.0614 6382703.598 0.0107 0.003 69 | 531479.5747 7843819.637 0.0183 0.0083 70 | 233124.8697 8174101.733 0.0132 0.0033 71 | 362762.1941 7954000.887 0.0228 0.0077 72 | 404613.6022 8031712.425 0.0177 0.0081 73 | 350967.0905 8119912.544 0.0096 0.006 74 | -29729.0187 8446784.004 0.0132 0.002 75 | -525200.8951 8941645.107 0.0063 0.0069 76 | 553415.8101 8197814.415 0.0056 0.003 77 | 904946.602 8127599.59 0.0002 0.0041 78 | 478863.5321 7976611.524 0.0157 0.0102 79 | 708977.4977 7765126.661 0.0088 0.0073 80 | 320113.8126 7997921.525 0.0206 0.0083 81 | 797821.6169 8096166.608 0.0018 0.0078 82 | 563866.9162 8120347.191 0.0078 0.0087 83 | 637317.2837 7965067.31 0.0127 0.0082 84 | 91401.4019 8327080.007 0.0139 0.0042 85 | 786395.4949 8040952.072 0.008 0.0049 86 | 175206.348 8505583.75 0.0071 0.0061 87 | 997606.6625 7614862.728 0.0041 0.0003 88 | 456571.6248 8596006.441 0.0001 0.0025 89 | 436626.2423 7965454.35 0.0158 0.0047 90 | -175888.636 8654700.767 0.0114 0.0032 91 | 1123692.008 7944279.997 0.0036 0.0014 92 | 385148.4152 7754937.599 0.0254 0.0054 93 | 83514.7474 8171725.822 0.0229 0.0019 94 | 349688.4777 8308007.59 0.0044 0.0047 95 | 649800.9278 8230560.09 0.0011 0.0077 96 | 348865.1709 8440764.253 0.0043 0.0012 97 | 603653.2887 7633252.15 0.0107 0.0037 98 | 743946.1963 8041515.57 0.0062 0.0065 99 | 376302.9155 7544546.09 0.0309 0.0077 100 | 553442.824 8208876.474 0.0048 0.0041 101 | 245022.7013 8074596.479 0.0247 0.0061 102 | 845995.3352 7818354.723 0.0061 0.0056 103 | 239567.8858 8572704.622 0.001 0.0057 104 | 520785.7942 7677839.361 0.0232 0.005 105 | -301462.057 8450849.013 0.0267 0.0016 106 | 374152.5245 7832339.575 0.028 0.0103 107 | 1163462.539 7597766.25 0.0018 0.0003 108 | 385826.0739 7655321.182 0.0314 0.009 109 | -128507.7871 9066902.73 0.0028 0.0005 110 | 108913.4385 8593336.04 0.0043 0.0048 111 | -304491.7119 8551129.616 0.0257 0.0028 112 | 816933.478 8782489.169 0.0009 0.0053 113 | 672578.572 8418499.476 0.0022 0.0025 114 | 521112.0864 7954483.271 0.0163 0.0062 115 | 671458.7565 8263600.548 0.002 0.0037 116 | 436811.5181 7910130.096 0.022 0.0091 117 | -373485.9859 8660706.437 0.0183 0.0006 118 | 1370990.619 8001627.684 0.001 0.0009 119 | 572406.8932 7600214.684 0.0163 0.0062 120 | 900182.3712 7894847.598 0.0114 0.0024 121 | -89812.448 8245462.162 0.0242 0.001 122 | 903909.3691 7606380.484 0.0078 0.0009 123 | 858841.4391 7452194.112 0.0098 0.0017 124 | 872744.8435 8117113.386 0.0036 0.0011 125 | 1006571.952 7925551.893 0.0088 0.004 126 | 374786.8696 7743796.582 0.0288 0.011 127 | 861181.2657 7551950.446 0.0092 0.0015 128 | -211910.6182 8364416.384 0.0218 0.0014 -------------------------------------------------------------------------------- /source/data/ch8-exercise4/trilobite.txt: -------------------------------------------------------------------------------- 1 | 255.21469671 23.32213210 2 | 264.14267472 14.69663235 3 | 285.31370450 16.17705497 4 | 309.78045529 23.43781973 5 | 338.59213906 34.70233370 6 | 357.68409767 52.68702211 7 | 364.21117350 65.16818595 8 | 363.63187769 73.45250194 9 | 355.49564684 83.98332701 10 | 375.21191470 79.81213989 11 | 377.39405756 88.28955448 12 | 375.76554980 98.35046339 13 | 385.82645871 99.97897116 14 | 392.73973175 106.93725766 15 | 393.14528169 114.36546030 16 | 400.38038574 116.72134901 17 | 405.32396726 125.39186222 18 | 410.20318259 134.98285499 19 | 411.27174729 146.15734205 20 | 410.75681768 153.52117849 21 | 419.10549989 153.17999475 22 | 422.33685470 159.88081644 23 | 421.62882649 170.00609155 24 | 427.28043623 168.55132964 25 | 430.78860859 184.52131309 26 | 431.00105994 194.71095440 27 | 436.00900767 202.46098805 28 | 437.93368573 214.62032086 29 | 435.57779700 221.85542491 30 | 438.01740467 226.65092129 31 | 437.90802501 241.44296037 32 | 433.51807858 251.31077068 33 | 433.13188137 256.83364802 34 | 442.27231073 258.39778958 35 | 443.59834023 265.89035842 36 | 441.96983247 275.95126733 37 | 441.98918521 288.90234731 38 | 435.75827968 298.64142522 39 | 432.61064380 303.97120395 40 | 441.94417176 302.77390684 41 | 442.09225691 313.88402772 42 | 439.67200198 322.03961131 43 | 430.87275638 328.82415194 44 | 422.99399033 335.67305878 45 | 436.86554987 335.71807224 46 | 444.22938631 336.23300184 47 | 441.42293419 349.91146277 48 | 450.41527839 340.36548347 49 | 456.64618394 330.62640557 50 | 462.12404782 344.88416230 51 | 460.45052660 368.81663074 52 | 456.48548286 399.06372367 53 | 453.95584828 422.01134636 54 | 448.53604264 446.60682956 55 | 436.47978151 473.51318801 56 | 422.77565986 497.52937540 57 | 406.50319816 518.59102552 58 | 377.81393883 545.25927191 59 | 351.73803301 561.01049604 60 | 320.39671467 572.69360475 61 | 281.02854513 580.11549943 62 | 237.37980882 582.61316533 63 | 208.05319544 578.71248780 64 | 167.01781265 570.29313143 65 | 145.44123293 561.38450617 66 | 118.79233927 545.64632681 67 | 97.45387161 520.10470335 68 | 71.77047097 490.55933066 69 | 56.68226159 468.22970927 70 | 51.31377737 439.17991345 71 | 40.40306307 396.79284043 72 | 44.64492436 375.81490926 73 | 46.06098077 355.56435904 74 | 59.35324450 363.89368850 75 | 75.60004549 369.65467789 76 | 65.39105144 356.91604926 77 | 57.66667850 335.10135748 78 | 59.29518626 325.04044857 79 | 70.46967334 323.97188386 80 | 57.37050821 312.88111574 81 | 54.05543445 294.14969363 82 | 63.32459620 293.87287609 83 | 56.73315417 282.31219181 84 | 53.88799656 270.08849279 85 | 54.27419377 264.56561545 86 | 60.78191686 264.09569931 87 | 54.78912337 257.20177901 88 | 64.38011612 252.32256368 89 | 70.16045826 249.02684267 90 | 61.34185991 242.86030333 91 | 57.38312414 233.33367677 92 | 59.26909671 219.59084964 93 | 66.03428460 215.43901526 94 | 63.06039459 205.05627535 95 | 70.34051209 193.54060454 96 | 68.15836923 185.06318993 97 | 66.16932497 173.82433667 98 | 69.63879185 163.89216016 99 | 73.10825872 153.95998365 100 | 80.00217902 147.96719017 101 | 91.04793368 148.73958458 102 | 86.83173311 142.89487625 103 | 82.74426493 135.20920879 104 | 87.19857756 124.42091893 105 | 96.40337312 125.06458094 106 | 94.22123026 116.58716634 107 | 98.61117669 106.71935603 108 | 104.52025123 101.58267590 109 | 109.76631101 92.69971135 110 | 120.61896708 96.23354443 111 | 125.47882966 92.87345721 112 | 118.69428902 84.07421161 113 | 117.30389331 77.50212232 114 | 128.80021139 71.83115984 115 | 135.11483588 74.12268235 116 | 134.11063737 62.02771573 117 | 143.29608018 49.72029776 118 | 155.24296166 37.60597839 119 | 168.64460505 31.14326876 120 | 185.00078569 22.11221906 121 | 198.27369668 17.49046854 122 | 221.47878417 16.33818489 123 | 234.23676554 19.08027082 124 | 234.23676554 19.08027082 125 | 249.05928530 19.32392196 126 | 249.05928530 19.32392196 127 | 232.64009015 20.75251131 128 | 235.58214778 39.52391964 129 | 238.55603781 49.90665955 130 | 227.46526969 63.00582467 131 | 228.40510198 76.02127085 132 | 223.28777460 83.06327629 133 | 222.70847879 91.34759230 134 | 222.06481679 100.55238784 135 | 221.09932377 114.35958117 136 | 218.61470265 123.43564433 137 | 220.06946456 129.08725407 138 | 217.92602718 146.51199942 139 | 220.04380384 155.90989358 140 | 217.36608413 167.74739539 141 | 216.78678832 176.03171140 142 | 213.72287139 193.39209055 143 | 213.09856213 215.54796607 144 | 212.21678807 241.38575983 145 | 206.64889727 254.87112216 146 | 205.68340426 268.67831549 147 | 204.46044645 286.16742704 148 | 204.73726400 295.43658880 149 | 204.69225054 309.30814833 150 | 202.99937658 320.28953680 151 | 207.02247855 328.89568380 152 | 199.87109345 338.57039551 153 | 199.03433284 350.53662972 154 | 202.45878626 354.47601276 155 | 197.81137502 368.02574129 156 | 199.07303833 376.43878968 157 | 188.11100261 387.69699570 158 | 189.39201865 409.06112407 159 | 188.94145525 415.50448096 160 | 173.82758516 419.99749908 161 | 154.64559964 429.75592974 162 | 149.86945601 445.14661737 163 | 148.38903339 466.31764715 164 | 159.51850700 479.12064198 165 | 174.52299744 489.41966295 166 | 187.34534502 491.24126931 167 | 205.49747132 496.21051155 168 | 216.88440974 505.33158816 169 | 237.79797471 510.49392900 170 | 257.91979252 513.75094452 171 | 274.14724077 506.56085394 172 | 278.53718721 496.69304364 173 | 290.03350528 491.02208116 174 | 306.92396828 487.57827500 175 | 326.10595379 477.81984434 176 | 340.08689298 463.07281871 177 | 345.12050142 444.00021285 178 | 339.57827134 430.66293566 179 | 333.97167506 418.24613804 180 | 319.35338184 402.42423974 181 | 308.62945817 397.04944755 182 | 295.20846204 390.56107720 183 | 288.61702001 379.00039294 184 | 292.81386784 371.89402130 185 | 286.54425681 355.73093924 186 | 292.51769755 349.67377956 187 | 287.89594703 336.40086858 188 | 286.50555132 329.82877930 189 | 282.20563180 311.95347053 190 | 286.40247963 304.84709889 191 | 281.78072911 291.57418791 192 | 285.57202699 277.03961363 193 | 278.25320400 262.65312449 194 | 285.34022290 253.89889234 195 | 276.90782175 242.20947567 196 | 282.08951533 234.24699066 197 | 283.11937455 219.51931778 198 | 285.86146047 206.76133640 199 | 283.80805001 196.44296269 200 | 283.78869727 183.49188271 201 | 281.79965301 172.25302945 202 | 282.31458262 164.88919301 203 | 278.42021304 154.44208689 204 | 272.36305337 148.46864615 205 | 281.88967992 144.50991038 206 | 281.48412997 137.08170774 207 | 272.60116542 131.83564795 208 | 275.87753369 124.66491011 209 | 276.64992810 113.61915545 210 | 279.86193018 107.36889716 211 | 274.78961625 100.53934307 212 | 276.09629301 95.08083194 213 | 275.56201065 89.49358840 214 | 274.30034736 81.08054000 215 | 272.11820449 72.60312540 216 | 269.07994828 63.14086504 217 | 264.00763435 56.31131095 218 | 265.89360691 42.56848382 219 | 266.40853652 35.20464737 220 | 261.14312400 31.13653195 -------------------------------------------------------------------------------- /source/data/ch9-2/loads.txt: -------------------------------------------------------------------------------- 1 | 50000.00 60000.00 1000.00 2700.00 2 | 60000.00 70000.00 1500.00 2700.00 3 | 70000.00 80000.00 2000.00 2700.00 4 | 80000.00 90000.00 2500.00 2700.00 5 | 90000.00 100000.00 3000.00 2700.00 6 | 100000.00 110000.00 3500.00 2700.00 7 | 110000.00 120000.00 3000.00 2700.00 8 | 120000.00 130000.00 2500.00 2700.00 9 | 130000.00 140000.00 2000.00 2400.00 10 | 140000.00 150000.00 1500.00 2400.00 11 | 150000.00 160000.00 1000.00 2400.00 12 | 160000.00 170000.00 500.00 2400.00 13 | -------------------------------------------------------------------------------- /source/functions/CGeo_elastic.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | # The Hilbert transform is used to create the wavelet 4 | from scipy.signal import hilbert 5 | 6 | """ 7 | This program simulates wave propagation in elastic media 8 | It has four classes with their own data and functions: 9 | 10 | Source: simulates the wave source 11 | Derivatives: Solves derivatives by finite differences 12 | Elastic_model: Sets the elastic model and makes plots 13 | Elastic_waves: Simulates the wave propagation 14 | 15 | Theory and numerical solution from Virieux (1986) 16 | 17 | Written by Wiktor Weibull (wiktor.w.weibull@uis.no) 18 | """ 19 | 20 | class Source: 21 | def __init__(self, nt, dt, sx, sz): 22 | self.nt = nt 23 | self.dt = dt 24 | self.sx = sx 25 | self.sz = sz 26 | self.t = np.linspace(0, (nt-1)*dt, nt) 27 | self.wav = np.zeros([nt]) 28 | 29 | def Ricker(self, f0, t0, phase): 30 | arg = np.pi*np.pi*f0*f0*(self.t-t0)**2 31 | self.wav[:] = (1-2*arg)*np.exp(-arg) 32 | # Rotate phase 33 | self.wav = np.real(np.fft.ifft(np.fft.fft(hilbert(self.wav)*(np.exp(1j*phase))))) 34 | return self.wav, self.t 35 | 36 | def plot(self): 37 | fig, ax = plt.subplots(figsize=(8,6)) 38 | ax.plot(self.t,self.wav) 39 | ax.set_xlabel("Time (s)") 40 | ax.set_ylabel("Amplitude") 41 | ax.set_title("Wavelet") 42 | ax.grid() 43 | return fig, ax 44 | 45 | class Derivatives: 46 | def __init__(self): 47 | pass 48 | 49 | # compute discrete forward derivatives 50 | def Dxfw(self,M, dx): 51 | nz,nx = M.shape 52 | D = np.zeros(M.shape) 53 | D[0:nz,0:nx-1] = M[0:nz,1:nx] - M[0:nz,0:nx-1] 54 | D = D/(dx) 55 | return D 56 | 57 | def Dzfw(self,M, dz): 58 | nz,nx = M.shape 59 | D = np.zeros(M.shape) 60 | D[0:nz-1,0:nx] = M[1:nz,0:nx] - M[0:nz-1,0:nx] 61 | D = D/(dz) 62 | return D 63 | 64 | # compute discrete backward derivatives 65 | def Dxbw(self,M, dx): 66 | nz,nx = M.shape 67 | D = np.zeros(M.shape) 68 | D[0:nz,1:nx] = M[0:nz,1:nx] - M[0:nz,0:nx-1] 69 | D = D/(dx) 70 | return D 71 | 72 | def Dzbw(self,M, dz): 73 | nz,nx = M.shape 74 | D = np.zeros(M.shape) 75 | D[1:nz,0:nx] = M[1:nz,0:nx] - M[0:nz-1,0:nx] 76 | D = D/(dz) 77 | return D 78 | 79 | class Elastic_model: 80 | def __init__(self, vp, vs, rho, dx, dz, ox, oz): 81 | self.vp = vp 82 | self.vs = vs 83 | self.rho = rho 84 | nz, nx = vp.shape 85 | self.nz = nz 86 | self.nx = nx 87 | self.dx = dx 88 | self.dz = dz 89 | self.ox = ox 90 | self.oz = oz 91 | 92 | for i in range(nx): 93 | for j in range(nz): 94 | if(vp[j,i] < vs[j,i]*np.sqrt(4./3.)): 95 | raise Exception("This elastic model is not physically possible" 96 | "since the Possion ratio is less than 0.5.") 97 | 98 | # computing moduli 99 | self.L2M = rho*vp*vp 100 | self.M = rho*vs*vs 101 | self.L = self.L2M - 2*self.M; 102 | 103 | # staggering 104 | self.B_x = 1./rho 105 | self.B_x[:,0:nx-1] = 1./(0.5*(rho[:,0:nx-1]+rho[:,1:nx])) 106 | self.B_z = 1./rho 107 | self.B_z[0:nz-1,:] = 1./(0.5*(rho[0:nz-1,:]+rho[1:nz,:])) 108 | self.Mxz = self.M 109 | self.Mxz[:,0:nx-1] = 0.5*(self.M[:,0:nx-1] + self.M[:,1:nx]) 110 | self.Mxz[0:nz-1,:] = 0.5*(self.Mxz[0:nz-1,:] + self.Mxz[1:nz,:]) 111 | 112 | # adding free surface condition at the top 113 | self.B_x[1,:] = 2.0*self.B_x[1,:] 114 | self.L[1,:] = 0.0 115 | self.L2M[1,:] = self.M[1,:] 116 | 117 | def plot(self): 118 | x0 = self.ox 119 | xend = x0 + (self.nx-1)*self.dx 120 | z0 = self.oz 121 | zend = z0 + (self.nz-1)*self.dz 122 | fig, ax = plt.subplots(1,3,sharey=True,figsize=(15,7.5)) 123 | im0 = ax[0].imshow(self.vp, extent=[x0, xend, zend, z0], aspect=2, cmap="rainbow") 124 | ax[0].set_xlabel("X (m)") 125 | ax[0].set_ylabel("Z (m)") 126 | ax[0].set_title("P-wave velocity"); 127 | cbar0=fig.colorbar(im0, ax=ax[0], orientation="horizontal") 128 | cbar0.ax.set_xlabel("Vp (m/s)") 129 | im1 = ax[1].imshow(self.vs, extent=[x0, xend, zend, z0], aspect=2, cmap="rainbow") 130 | ax[1].set_xlabel("X (m)") 131 | ax[1].set_title("S-wave velocity"); 132 | cbar1=fig.colorbar(im1, ax=ax[1], orientation="horizontal") 133 | cbar1.ax.set_xlabel("Vs (m/s)") 134 | im2 = ax[2].imshow(self.rho, extent=[x0, xend, zend, z0], aspect=2, cmap="rainbow") 135 | ax[2].set_xlabel("X (m)") 136 | ax[2].set_title("Density"); 137 | cbar2=fig.colorbar(im2, ax=ax[2], orientation="horizontal") 138 | cbar2.ax.set_xlabel(r"rho (kg/m$^3$)") 139 | return fig, ax 140 | 141 | class Elastic_waves: 142 | def __init__(self, Model, nt, dt): 143 | self.nz = Model.nz 144 | self.nx = Model.nx 145 | self.dx = Model.dx 146 | self.dz = Model.dz 147 | self.ox = Model.ox 148 | self.oz = Model.oz 149 | 150 | self.Sxx = np.zeros([self.nz,self.nx]) # stress component 151 | self.Szz = np.zeros([self.nz,self.nx]) # stress component 152 | self.Sxz = np.zeros([self.nz,self.nx]) # stress component 153 | self.Vx = np.zeros([self.nz,self.nx]) # particle velocity component 154 | self.Vz = np.zeros([self.nz,self.nx]) # particle velocity component 155 | self.nt=nt 156 | self.dt=dt 157 | 158 | def Courant_stability(self, vpmax): 159 | dtstab = np.sqrt(self.dx**2 + self.dz**2)/(np.sqrt(2)*vpmax) 160 | return dtstab 161 | 162 | def insertPressure(self, source, it): 163 | sx = source.sx 164 | sz = source.sz 165 | wav = source.wav[it] 166 | self.Sxx[sz,sx] = self.Sxx[sz,sx] + self.dt*wav 167 | self.Szz[sz,sx] = self.Szz[sz,sx] + self.dt*wav 168 | 169 | def insertForce(self, source, model, direction, it): 170 | sx = source.sx 171 | sz = source.sz 172 | wav = source.wav[it] 173 | dx = np.sin(direction*np.pi/180.) 174 | dz = -np.cos(direction*np.pi/180.) 175 | Bx = model.B_x 176 | Bz = model.B_z 177 | self.Vx[sz,sx-1] = self.Vx[sz,sx-1] + dx*0.5*self.dt*Bx[sz,sx-1]*wav 178 | self.Vx[sz,sx] = self.Vx[sz,sx] + dx*0.5*self.dt*Bx[sz,sx]*wav 179 | self.Vz[sz-1,sx] = self.Vz[sz-1,sx] + dz*0.5*self.dt*Bz[sz-1,sx]*wav 180 | self.Vz[sz,sx] = self.Vz[sz,sx] + dz*0.5*self.dt*Bz[sz,sx]*wav 181 | 182 | def insertStress(self, source, which, it): 183 | sx = source.sx 184 | sz = source.sz 185 | wav = source.wav[it] 186 | if(which == "xx"): 187 | self.Sxx[sz,sx] = self.Sxx[sz,sx] + self.dt*wav 188 | if(which == "zz"): 189 | self.Szz[sz,sx] = self.Szz[sz,sx] + self.dt*wav 190 | if(which == "xz"): 191 | self.Sxz[sz,sx] = self.Sxz[sz,sx] + 0.25*self.dt*wav 192 | self.Sxz[sz-1,sx] = self.Sxz[sz-1,sx] + 0.25*self.dt*wav 193 | self.Sxz[sz,sx-1] = self.Sxz[sz,sx-1] + 0.25*self.dt*wav 194 | self.Sxz[sz-1,sx-1] = self.Sxz[sz-1,sx-1] + 0.25*self.dt*wav 195 | 196 | def recordPressure(self, rz): 197 | return (self.Sxx[rz,:] + self.Szz[rz,:]) 198 | 199 | def recordVelocity(self, rz, direction): 200 | record = np.zeros([self.nx]) 201 | if(direction == "x"): 202 | record[1:self.nx] = 0.5*self.Vx[rz,0:self.nx-1]+0.5*self.Vx[rz,1:self.nx] 203 | return(record) 204 | if(direction == "z"): 205 | record[0:self.nx] = 0.5*self.Vz[rz-1,:]+0.5*self.Vz[rz,:] 206 | return(record) 207 | 208 | def forwardStep(self,Derivative,Model): 209 | self.forwardstepVelocity(Derivative,Model) 210 | self.forwardstepStress(Derivative,Model) 211 | 212 | # solve elastodynamic equations 213 | def forwardstepVelocity(self,Derivative,Model): 214 | dx = self.dx 215 | dz = self.dz 216 | dt = self.dt 217 | nx = self.nx 218 | nz = self.nz 219 | 220 | # forward step Vx 221 | der = Derivative.Dxfw(self.Sxx,dx) 222 | self.Vx = self.Vx + dt*Model.B_x*der 223 | 224 | der = Derivative.Dzbw(self.Sxz,dz) 225 | self.Vx = self.Vx + dt*Model.B_x*der 226 | 227 | # forward step Vz 228 | der = Derivative.Dxbw(self.Sxz,dx) 229 | self.Vz = self.Vz + dt*Model.B_z*der 230 | 231 | der = Derivative.Dzfw(self.Szz,dz) 232 | self.Vz = self.Vz + dt*Model.B_z*der 233 | 234 | # solve elastodynamic equations 235 | def forwardstepStress(self,Derivative,Model): 236 | dx = self.dx 237 | dz = self.dz 238 | dt = self.dt 239 | nx = self.nx 240 | nz = self.nz 241 | 242 | # forward step normal stresses 243 | der = Derivative.Dxbw(self.Vx,dx) 244 | self.Sxx = self.Sxx + dt*Model.L2M*der 245 | self.Szz = self.Szz + dt*Model.L*der 246 | 247 | der = Derivative.Dzbw(self.Vz,dz) 248 | self.Sxx = self.Sxx + dt*Model.L*der 249 | self.Szz = self.Szz + dt*Model.L2M*der 250 | 251 | # free surface condition at the top 252 | self.Szz[1,:] = 0.0 253 | 254 | # forward step shear stress 255 | der = Derivative.Dxfw(self.Vz,dx) 256 | self.Sxz = self.Sxz + dt*Model.Mxz*der 257 | 258 | der = Derivative.Dzfw(self.Vx,dz) 259 | self.Sxz = self.Sxz + dt*Model.Mxz*der 260 | -------------------------------------------------------------------------------- /source/functions/angles.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from sph_to_cart import sph_to_cart 3 | from cart_to_sph import cart_to_sph 4 | from pole import pole_from_plane, plane_from_pole 5 | 6 | # Python functions based on the Matlab function 7 | # Angles in Allmendinger et al. (2012) 8 | 9 | def angle_bw_lines(trd1, plg1, trd2, plg2): 10 | """ 11 | angle_bw_lines returns the angle between two lines 12 | of trend and plunge trd1, plg1, trd2, and plg2 13 | Input and output angles are in radians 14 | """ 15 | # convert lines to directions cosines and numpy arrays 16 | cn1, ce1, cd1 = sph_to_cart(trd1, plg1) 17 | u = np.array([cn1, ce1, cd1]) 18 | cn2, ce2, cd2 = sph_to_cart(trd2, plg2) 19 | v = np.array([cn2, ce2, cd2]) 20 | # angle between lines is arccosine of their dot product 21 | return np.arccos(np.dot(u, v)) 22 | 23 | def angle_bw_planes(str1, dip1, str2, dip2): 24 | """ 25 | angle_bw_planes returns the angle between two planes 26 | of strike and dip str1, dip1, str2, and dip2 27 | Input and output angles are in radians 28 | """ 29 | # compute poles to lines 30 | p1_trd, p1_plg = pole_from_plane(str1, dip1) 31 | p2_trd, p2_plg = pole_from_plane(str2, dip2) 32 | # find angle between poles 33 | angle = angle_bw_lines(p1_trd,p1_plg,p2_trd,p2_plg) 34 | # angle between planes is the complementary angle 35 | return (np.pi - angle) 36 | 37 | def pole_from_lines(trd1, plg1, trd2, plg2): 38 | """ 39 | pole_from_lines compute the pole to a plane given 40 | two lines on the plane, with trend and plunge trd1, plg1, 41 | trd2, and plg2 42 | Input and output angles are in radians 43 | """ 44 | # convert lines to direction cosines and numpy arrays 45 | cn1, ce1, cd1 = sph_to_cart(trd1, plg1) 46 | u = np.array([cn1, ce1, cd1]) 47 | cn2, ce2, cd2 = sph_to_cart(trd2, plg2) 48 | v = np.array([cn2, ce2, cd2]) 49 | # normal is cross product between vectors 50 | p = np.cross(u, v) 51 | # make pole a unit vector 52 | norm = np.linalg.norm(p) 53 | p = p/norm 54 | # if pole points upwards, make it point downwards 55 | if p[2] < 0: 56 | p *= -1.0 57 | # return trend and plunge of pole 58 | return cart_to_sph(p[0], p[1], p[2]) 59 | 60 | def plane_from_app_dips(trd1, plg1, trd2, plg2): 61 | """ 62 | plane_from_app_dips returns the strike and dip of a plane 63 | from two apparent dips with trend and plunge trd1, plg1, 64 | trd2, and plg2 65 | Input and output angles are in radians 66 | """ 67 | # compute pole to plane from apparent dips (lines) 68 | p_trd, p_pg = pole_from_lines(trd1,plg1,trd2,plg2) 69 | # return strike and dip of plane 70 | return plane_from_pole(p_trd, p_pg) 71 | 72 | def int_bw_planes(str1, dip1, str2, dip2): 73 | """ 74 | int_bw_planes returns the intersection between two planes 75 | of strike and dip str1, dip1, str2, dip2 76 | Input and output angles are in radians 77 | """ 78 | # compute poles to planes 79 | p1_trd, p1_plg = pole_from_plane(str1, dip1) 80 | p2_trd, p2_plg = pole_from_plane(str2, dip2) 81 | # intersection is normal to poles 82 | return pole_from_lines(p1_trd,p1_plg,p2_trd,p2_plg) -------------------------------------------------------------------------------- /source/functions/angles_u.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from uncertainties import umath # From E. Lebigot 3 | from sph_to_cart_u import sph_to_cart_u 4 | from cart_to_sph_u import cart_to_sph_u 5 | from pole import pole_from_plane, plane_from_pole 6 | 7 | # these functions are similar to the ones in the module 8 | # angles.py. However, in this case input and output 9 | # values have uncertainties 10 | 11 | def angle_bw_lines_u(trd1, plg1, trd2, plg2): 12 | """ 13 | angle_bw_lines_u returns the angle between two lines 14 | of trend and plunge trd1, plg1, trd2, and plg2 15 | Input and output angles are in radians 16 | """ 17 | # convert lines to directions cosines and numpy arrays 18 | cn1, ce1, cd1 = sph_to_cart_u(trd1, plg1) 19 | u = np.array([cn1, ce1, cd1]) 20 | cn2, ce2, cd2 = sph_to_cart_u(trd2, plg2) 21 | v = np.array([cn2, ce2, cd2]) 22 | # angle between lines is arccosine of their dot product 23 | return umath.acos(np.dot(u, v)) 24 | 25 | def angle_bw_planes_u(stk1, dip1, stk2, dip2): 26 | """ 27 | angle_bw_planes_u returns the angle between two planes 28 | of strike and dip stk1, dip1, stk2, and dip2 29 | Input and output angles are in radians 30 | """ 31 | # compute poles to lines 32 | p1_trd, p1_plg = pole_from_plane(stk1, dip1) 33 | p2_trd, p2_plg = pole_from_plane(stk2, dip2) 34 | # find angle between poles 35 | angle = angle_bw_lines_u(p1_trd,p1_plg,p2_trd,p2_plg) 36 | # angle between planes is the complementary angle 37 | return (np.pi - angle) 38 | 39 | def pole_from_lines_u(trd1, plg1, trd2, plg2): 40 | """ 41 | pole_from_lines_u compute the pole to a plane given 42 | two lines on the plane, with trend and plunge trd1, plg1, 43 | trd2, and plg2 44 | Input and output angles are in radians 45 | """ 46 | # convert lines to direction cosines and numpy arrays 47 | cn1, ce1, cd1 = sph_to_cart_u(trd1, plg1) 48 | u = np.array([cn1, ce1, cd1]) 49 | cn2, ce2, cd2 = sph_to_cart_u(trd2, plg2) 50 | v = np.array([cn2, ce2, cd2]) 51 | # normal is cross product between vectors 52 | p = np.cross(u, v) 53 | # make pole a unit vector by dividing it 54 | # by its magnitude 55 | norm = umath.sqrt(np.dot(p, p)) 56 | p = p/norm 57 | # if pole points upwards, make it point downwards 58 | if p[2] < 0: 59 | p *= -1.0 60 | # return trend and plunge of pole 61 | return cart_to_sph_u(p[0], p[1], p[2]) 62 | 63 | def plane_from_app_dips_u(trd1, plg1, trd2, plg2): 64 | """ 65 | plane_from_app_dips_u returns the strike and dip of a plane 66 | from two apparent dips with trend and plunge trd1, plg1, 67 | trd2, and plg2 68 | Input and output angles are in radians 69 | """ 70 | # compute pole to plane from apparent dips (lines) 71 | p_trd, p_plg = pole_from_lines_u(trd1, plg1, trd2, plg2) 72 | # return strike and dip of plane 73 | return plane_from_pole(p_trd, p_plg) 74 | 75 | def int_bw_planes_u(stk1, dip1, stk2, dip2): 76 | """ 77 | int_bw_planes_u returns the intersection between two planes 78 | of strike and dip stk1, dip1, stk2, dip2 79 | Input and output angles are in radians 80 | """ 81 | # compute poles to planes 82 | p1_trd, p1_plg = pole_from_plane(stk1, dip1) 83 | p2_trd, p2_plg = pole_from_plane(stk2, dip2) 84 | # intersection is normal to poles 85 | return pole_from_lines_u(p1_trd,p1_plg,p2_trd,p2_plg) -------------------------------------------------------------------------------- /source/functions/bingham.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | from sph_to_cart import sph_to_cart 5 | from cart_to_sph import cart_to_sph 6 | from zero_twopi import zero_twopi 7 | from stereonet import stereonet 8 | from great_circle import great_circle 9 | from st_coord_line import st_coord_line 10 | 11 | 12 | def bingham(T,P,stype,ax): 13 | """ 14 | bingham calculates and plots a cylindrical best fit to 15 | a distribution of poles to bedding. The statistical 16 | routine is based on algorithms in Fisher et al. (1988) 17 | 18 | USE: ev, conf, bf = bingham(T,P,stype,ax) 19 | 20 | T and P = Vectors of lines trends and plunges 21 | respectively 22 | stype = Stereonet type: 0 = equal angle, 1 = equal area 23 | ax = axis handle for the plot 24 | 25 | ev = 3 x 3 matrix with eigenvalues (column 1), trends 26 | (column 2) and plunges (column 3) of the eigenvectors. 27 | Maximum eigenvalue and corresponding eigenvector are 28 | in row 1, intermediate in row 2, and minimum in row 3. 29 | 30 | conf = 2 x 2 matrix with the maximum (column 1) and 31 | minimum (column 2) radius of the 95% elliptical 32 | confidence cone around the eigenvector corresponding 33 | to the largest (row 1), and lowest (row 2) eigenvalue 34 | 35 | bf = 1 x 2 vector containing the strike and dip 36 | (right hand rule) of the best fit great circle to 37 | the distribution of lines 38 | 39 | NOTE: Input/Output trends and plunges, as well as 40 | confidence cones are in radians. bingham plots the 41 | input poles, eigenvectors and best fit great circle 42 | in an equal area stereonet. 43 | 44 | Python function translated from the Matlab function 45 | Bingham in Allmendinger et al. (2012) 46 | """ 47 | # some constants 48 | pi = np.pi 49 | east = pi/2 50 | twopi = pi*2 51 | 52 | # number of lines 53 | nlines = len(T) 54 | 55 | # initialize the orientation matrix 56 | a = np.zeros((3,3)) 57 | 58 | # fill the orientation matrix with the sums of the 59 | # squares (for the principal diagonal) and the products 60 | # of the direction cosines of each line. cn, ce and cd 61 | # are the north, east and down direction cosines 62 | for i in range(nlines): 63 | cn,ce,cd = sph_to_cart(T[i],P[i]) 64 | a[0,0] = a[0,0] + cn*cn 65 | a[0,1] = a[0,1] + cn*ce 66 | a[0,2] = a[0,2] + cn*cd 67 | a[1,1] = a[1,1] + ce*ce 68 | a[1,2] = a[1,2] + ce*cd 69 | a[2,2] = a[2,2] + cd*cd 70 | 71 | # the orientation matrix is symmetric so the off-diagonal 72 | # components can be equated 73 | a[1,0] = a[0,1] 74 | a[2,0] = a[0,2] 75 | a[2,1] = a[1,2] 76 | 77 | # calculate the eigenvalues and eigenvectors of the 78 | # orientation matrix using function eigh. 79 | # D is a vector of eigenvalues and V is a full matrix 80 | # whose columns are the corresponding eigenvectors 81 | D, V = np.linalg.eigh(a) 82 | 83 | # normalize the eigenvalues by the number of lines and 84 | # convert the corresponding eigenvectors to the lower 85 | # hemisphere 86 | for i in range(3): 87 | D[i] = D[i]/nlines 88 | if V[2,i] < 0: 89 | V[0,i] = -V[0,i] 90 | V[1,i] = -V[1,i] 91 | V[2,i] = -V[2,i] 92 | 93 | # initialize ev 94 | ev = np.zeros((3,3)) 95 | # fill ev 96 | ev[0,0] = D[2] # Maximum eigenvalue 97 | ev[1,0] = D[1] # Intermediate eigenvalue 98 | ev[2,0] = D[0] # Minimum eigenvalue 99 | # trend and plunge of largest eigenvalue: column 3 of V 100 | ev[0,1], ev[0,2] = cart_to_sph(V[0,2], V[1,2], 101 | V[2,2]) 102 | # trend and plunge of interm. eigenvalue: column 2 of V 103 | ev[1,1], ev[1,2] = cart_to_sph(V[0,1], V[1,1], 104 | V[2,1]) 105 | # trend and plunge of minimum eigenvalue: column 1 of V 106 | ev[2,1], ev[2,2] = cart_to_sph(V[0,0], V[1,0], 107 | V[2,0]) 108 | 109 | # initialize conf 110 | conf = np.zeros((2,2)) 111 | # if there are more than 25 lines, calculate confidence 112 | # cones at the 95% confidence level. The algorithm is 113 | # explained in Fisher et al. (1987) 114 | if nlines >= 25: 115 | e11 = e22 = e12 = d11 = d22 = d12 = 0 116 | en11 = 1/(nlines*(ev[2,0]-ev[0,0])**2) 117 | en22 = 1/(nlines*(ev[1,0]-ev[0,0])**2) 118 | en12 = 1/(nlines*(ev[2,0]-ev[0,0])*(ev[1,0]- 119 | ev[0,0])) 120 | dn11 = en11 121 | dn22 = 1/(nlines*(ev[2,0]-ev[1,0])**2) 122 | dn12 = 1/(nlines*(ev[2,0]-ev[1,0])*(ev[2,0]- 123 | ev[0,0])) 124 | vec = np.zeros((3,3)) 125 | for i in range(3): 126 | vec[i,0] = np.sin(ev[i,2]+east)*np.cos(twopi- 127 | ev[i,1]) 128 | vec[i,1] = np.sin(ev[i,2]+east)*np.sin(twopi- 129 | ev[i,1]) 130 | vec[i,2] = np.cos(ev[i,2]+east) 131 | for i in range(nlines): 132 | c1 = np.sin(P[i]+east)*np.cos(twopi-T[i]) 133 | c2 = np.sin(P[i]+east)*np.sin(twopi-T[i]) 134 | c3 = np.cos(P[i]+east) 135 | u1x = vec[2,0]*c1 + vec[2,1]*c2 + vec[2,2]*c3 136 | u2x = vec[1,0]*c1 + vec[1,1]*c2 + vec[1,2]*c3 137 | u3x = vec[0,0]*c1 + vec[0,1]*c2 + vec[0,2]*c3 138 | e11 = u1x*u1x * u3x*u3x + e11 139 | e22 = u2x*u2x * u3x*u3x + e22 140 | e12 = u1x*u2x * u3x*u3x + e12 141 | d11 = e11 142 | d22 = u1x*u1x * u2x*u2x + d22 143 | d12 = u2x*u3x * u1x*u1x + d12 144 | e22 = en22*e22 145 | e11 = en11*e11 146 | e12 = en12*e12 147 | d22 = dn22*d22 148 | d11 = dn11*d11 149 | d12 = dn12*d12 150 | d = -2*np.log(.05)/nlines 151 | # initialize f 152 | f = np.zeros((2,2)) 153 | if abs(e11*e22-e12*e12) >= 0.000001: 154 | f[0,0] = (1/(e11*e22-e12*e12)) * e22 155 | f[1,1] = (1/(e11*e22-e12*e12)) * e11 156 | f[0,1] = -(1/(e11*e22-e12*e12)) * e12 157 | f[1,0] = f[0,1] 158 | # calculate the eigenvalues and eigenvectors 159 | # of the matrix f using function eigh 160 | # The next lines follow steps 1-4 outlined 161 | # on pp. 34-35 of Fisher et al. (1987) 162 | DD, _ = np.linalg.eigh(f) 163 | if DD[0] > 0 and DD[1] > 0: 164 | if d/DD[0] <= 1 and d/DD[1] <= 1: 165 | conf[0,1] = np.arcsin(np.sqrt(d/DD[1])) 166 | conf[0,0] = np.arcsin(np.sqrt(d/DD[0])) 167 | # repeat the process for the eigenvector 168 | # corresponding to the smallest eigenvalue 169 | if abs(d11*d22-d12*d12) >= 0.000001: 170 | f[0,0] = (1/(d11*d22-d12*d12)) * d22 171 | f[1,1] = (1/(d11*d22-d12*d12)) * d11 172 | f[0,1] = -(1/(d11*d22-d12*d12)) * d12 173 | f[1,0] = f[0,1] 174 | DD, _ = np.linalg.eigh(f) 175 | if DD[0] > 0.0 and DD[1] > 0.0: 176 | if d/DD[0] <= 1 and d/DD[1] <= 1: 177 | conf[1,1] = np.arcsin(np.sqrt(d/DD[1])) 178 | conf[1,0] = np.arcsin(np.sqrt(d/DD[0])) 179 | 180 | # calculate the best fit great circle 181 | # to the distribution of points 182 | bf = np.zeros(2) 183 | bf[0] = zero_twopi(ev[2,1] + east) 184 | bf[1] = east - ev[2,2] 185 | 186 | # Plot stereonet 187 | stereonet(0, 90*pi/180, 10*pi/180, stype, ax) 188 | 189 | # Plot lines 190 | for i in range(nlines): 191 | xp,yp = st_coord_line(T[i],P[i],stype) 192 | ax.plot(xp,yp,"k.") 193 | 194 | # Plot eigenvectors 195 | for i in range(3): 196 | xp,yp = st_coord_line(ev[i,1],ev[i,2],stype) 197 | ax.plot(xp,yp,"rs") 198 | ax.text(xp-0.03,yp+0.03,str(i+1),c="r") 199 | 200 | # Plot best fit great circle 201 | path = great_circle(bf[0],bf[1],stype) 202 | ax.plot(path[:,0],path[:,1],"r") 203 | 204 | return ev, conf, bf -------------------------------------------------------------------------------- /source/functions/calc_mv.py: -------------------------------------------------------------------------------- 1 | import math 2 | from sph_to_cart import sph_to_cart 3 | from cart_to_sph import cart_to_sph 4 | 5 | def calc_mv(T,P): 6 | """ 7 | calc_mv calculates the mean vector for a group of lines. 8 | It calculates the trend (trd) and plunge (plg) of the 9 | mean vector, its mean resultant length (rave), and 10 | Fisher statistics (concentration factor (conc), 99 (d99) 11 | and 95 (d95) % uncertainty cones); for a series of lines 12 | whose trends and plunges are stored in the arrays T and P 13 | 14 | NOTE: Input/Output values are in radians 15 | 16 | Python function translated from the Matlab function 17 | CalcMV in Allmendinger et al. (2012) 18 | """ 19 | # number of lines 20 | nlines = len(T) 21 | 22 | # initialize the 3 direction cosines which contain the 23 | # sums of the individual vectors 24 | cn_sum = ce_sum = cd_sum = 0.0 25 | 26 | # now add up all the individual vectors 27 | for i in range(nlines): 28 | cn,ce,cd = sph_to_cart(T[i],P[i]) 29 | cn_sum += cn 30 | ce_sum += ce 31 | cd_sum += cd 32 | 33 | # r is the length of the resultant vector and 34 | # rave is the mean resultant length 35 | r = math.sqrt(cn_sum*cn_sum+ce_sum*ce_sum+cd_sum*cd_sum) 36 | rave = r/nlines 37 | # if rave is lower than 0.1, the mean vector is 38 | # insignificant, return error 39 | if rave < 0.1: 40 | raise ValueError("Mean vector is insignificant") 41 | #Else 42 | else: 43 | # divide the resultant vector by its length to get 44 | # the direction cosines of the unit vector 45 | cn_sum /= r 46 | ce_sum /= r 47 | cd_sum /= r 48 | # convert the mean vector to the lower hemisphere 49 | if cd_sum < 0.0: 50 | cn_sum *= -1.0 51 | ce_sum *= -1.0 52 | cd_sum *= -1.0 53 | # convert the mean vector to trend and plunge 54 | trd, plg = cart_to_sph(cn_sum,ce_sum,cd_sum) 55 | # if there are enough measurements calculate the 56 | # Fisher statistics (Fisher et al., 1987) 57 | conc = d99 = d95 = 0.0 58 | if r < nlines: 59 | if nlines < 16: 60 | afact = 1.0-(1.0/nlines) 61 | conc = (nlines/(nlines-r))*afact**2 62 | else: 63 | conc = (nlines-1.0)/(nlines-r) 64 | if rave >= 0.65 and rave < 1.0: 65 | afact = 1.0/0.01 66 | bfact = 1.0/(nlines-1.0) 67 | d99 = math.acos(1.0-((nlines-r)/r)*(afact**bfact-1.0)) 68 | afact = 1.0/0.05 69 | d95 = math.acos(1.0-((nlines-r)/r)*(afact**bfact-1.0)) 70 | 71 | return trd, plg, rave, conc, d99, d95 -------------------------------------------------------------------------------- /source/functions/cart_to_sph.py: -------------------------------------------------------------------------------- 1 | import math 2 | from zero_twopi import zero_twopi 3 | 4 | def cart_to_sph(cn,ce,cd): 5 | """ 6 | cart_to_sph converts from Cartesian to spherical coordinates 7 | 8 | cart_to_sph(cn,ce,cd) returns the trend (trd) 9 | and plunge (plg) of a line with north (cn), 10 | east (ce), and down (cd) direction cosines 11 | 12 | NOTE: Trend and plunge are returned in radians 13 | 14 | Python function translated from the Matlab function 15 | CartToSph in Allmendinger et al. (2012) 16 | """ 17 | pi = math.pi 18 | # plunge 19 | plg = math.asin(cd) 20 | 21 | # trend: If north direction cosine is zero, trend 22 | # is east or west. Choose which one by the sign of 23 | # the east direction cosine 24 | if cn == 0.0: 25 | if ce < 0.0: 26 | trd = 3.0/2.0 * pi # trend is west 27 | else: 28 | trd = pi/2.0 # trend is east 29 | else: 30 | trd = math.atan(ce/cn) 31 | if cn < 0.0: 32 | trd += pi 33 | # make sure trend is between 0 and 2*pi 34 | trd = zero_twopi(trd) 35 | 36 | return trd, plg -------------------------------------------------------------------------------- /source/functions/cart_to_sph_u.py: -------------------------------------------------------------------------------- 1 | import math 2 | from uncertainties import umath # From E. Lebigot 3 | from zero_twopi import zero_twopi 4 | 5 | def cart_to_sph_u(cn,ce,cd): 6 | """ 7 | cart_to_sph_u converts from Cartesian to spherical coordinates 8 | 9 | cart_to_sph_u(cn,ce,cd) returns the trend (trd) 10 | and plunge (plg) of a line for input north (cn), 11 | east (ce), and down (cd) direction cosines. 12 | Input and output values have uncertainties 13 | 14 | NOTE: Trend and plunge are returned in radians 15 | 16 | Based on Python function cart_to_sph 17 | """ 18 | pi = math.pi 19 | # plunge 20 | plg = umath.asin(cd) 21 | 22 | # trend: If north direction cosine is zero, trend 23 | # is east or west. Choose which one by the sign of 24 | # the east direction cosine 25 | if cn == 0.0: 26 | if ce < 0.0: 27 | trd = 3.0/2.0 * pi # trend is west 28 | else: 29 | trd = pi/2.0 # trend is east 30 | # else 31 | else: 32 | trd = umath.atan(ce/cn) 33 | if cn < 0.0: 34 | # add pi 35 | trd += pi 36 | # make sure trend is between 0 and 2*pi 37 | trd = zero_twopi(trd) 38 | 39 | return trd, plg -------------------------------------------------------------------------------- /source/functions/cauchy.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from dircos_axes import dircos_axes 4 | from sph_to_cart import sph_to_cart 5 | from pole import pole_from_plane 6 | 7 | def cauchy(stress,tx1,px1,tx3,stk,dip): 8 | """ 9 | Given the stress tensor in a X1X2X3 coordinate system, 10 | cauchy computes the X1X2X3 tractions on an arbitrarily 11 | oriented plane 12 | 13 | USE: t,pt = cauchy(stress,tx1,px1,tx3,stk,dip) 14 | 15 | stress = 3 x 3 stress tensor 16 | tx1 = trend of X1 17 | px1 = plunge of X1 18 | tx3 = trend of X3 19 | stk = strike of plane 20 | dip = dip of plane 21 | t = 1 x 3 vector with X1, X2 and X3 tractions 22 | pt = 1 x 3 vector with direction cosines of pole 23 | to plane with respect to X1X2X3 24 | 25 | NOTE = Plane orientation follows the right hand rule 26 | Input/Output angles are in radians 27 | 28 | Python function translated from the Matlab function 29 | Cauchy in Allmendinger et al. (2012) 30 | """ 31 | # compute direction cosines of X1X2X3 with respect 32 | # to NED 33 | dc = dircos_axes(tx1,px1,tx3) 34 | 35 | # calculate direction cosines of pole to plane 36 | trd, plg = pole_from_plane(stk,dip) 37 | p = np.zeros(3) 38 | p[0],p[1],p[2] = sph_to_cart(trd,plg) 39 | 40 | # transform pole to plane to stress coordinates X1X2X3 41 | # The transformation matrix is the direction cosines of 42 | # X1X2X3 43 | pt = np.zeros(3) 44 | for i in range(3): 45 | for j in range(3): 46 | pt[i] = dc[i,j]*p[j] + pt[i] 47 | 48 | # calculate the tractions in stress coordinates X1X2X3 49 | t = np.zeros(3) 50 | # compute tractions using Cauchy"s law 51 | for i in range(3): 52 | for j in range(3): 53 | t[i] = stress[i,j]*pt[j] + t[i] 54 | 55 | return t, pt -------------------------------------------------------------------------------- /source/functions/dircos_axes.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from sph_to_cart import sph_to_cart 4 | 5 | def dircos_axes(tx1,px1,tx3): 6 | """ 7 | dircos_axes calculates the direction cosines of a right 8 | handed, orthogonal X1X2X3 cartesian coordinate system 9 | of any orientation with respect to NED 10 | 11 | USE: dc = dircos_axes(tx1,px1,tx3) 12 | 13 | tx1 = trend of X1 14 | px1 = plunge of X1 15 | tx3 = trend of X3 16 | dc = 3 x 3 matrix containing the direction cosines 17 | of X1 (row 1), X2 (row 2), and X3 (row 3) 18 | 19 | Note: Input angles should be in radians 20 | 21 | Python function translated from the Matlab function 22 | DirCosAxes in Allmendinger et al. (2012) 23 | """ 24 | # some constants 25 | east = np.pi/2.0 26 | west = 3.0*east 27 | # tolerance for near zero values 28 | tol = 1e-6 29 | 30 | # initialize matrix of direction cosines 31 | dc = np.zeros((3,3)) 32 | 33 | # direction cosines of X1 34 | dc[0,0],dc[0,1],dc[0,2] = sph_to_cart(tx1,px1) 35 | 36 | # calculate plunge of axis 3 37 | # if axis 1 is horizontal 38 | if abs(px1) < tol: 39 | dt = abs(tx1-tx3) 40 | if abs(dt - east) < tol or abs(dt - west) < tol: 41 | px3 = 0.0 42 | else: 43 | px3 = east 44 | # else 45 | else: 46 | # since dot product X1 and X3 = 0 47 | px3 = np.arctan(-(dc[0,0]*np.cos(tx3) 48 | + dc[0,1]*np.sin(tx3))/dc[0,2]) 49 | 50 | # direction cosines of X3 51 | dc[2,0],dc[2,1],dc[2,2] = sph_to_cart(tx3,px3) 52 | 53 | # X2 is the cross product of X3 and X1 54 | dc[1,:] = np.cross(dc[2,:],dc[0,:]) 55 | # make it a unit vector 56 | dc[1,:] = dc[1,:]/np.linalg.norm(dc[1,:]) 57 | 58 | return dc -------------------------------------------------------------------------------- /source/functions/disloc2d.py: -------------------------------------------------------------------------------- 1 | from numpy import arctan,arctan2,sin,cos,sign,sqrt,pi,mod,log 2 | 3 | def disloc2d(tip,base,slip,nu,obsx,obsy): 4 | """ 5 | This function calculates displacements on a 2D planar 6 | fault of finite extent, modeled by two edge dislocations 7 | in a homogeneous, isotropic elastic half space 8 | 9 | Arguments: 10 | tip = tuple of (x,y) coordinates of the fault tip 11 | base = tuple of (x,y) coordinates of the fault base 12 | slip = slip on the fault, + for reverse, - for normal 13 | nu = Poisson's ratio (scalar) 14 | obsx = x coordinates of observation points 15 | obsy = y coordinates of observation points 16 | Returns: 17 | ux = x components of displ. vectors at obs. points 18 | uy = y components of displ. vectors at obs. points 19 | 20 | disloc2d uses function displacement 21 | 22 | Written by David Oakley (david.o.oakley@uis.no) 23 | """ 24 | dip = arctan2(tip[1]-base[1],tip[0]-base[0]) 25 | s1 = slip*cos(dip) 26 | s2 = slip*sin(dip) 27 | [ux_part1,uy_part1] = displacement(tip[0],tip[1],s1,s2, 28 | nu,obsx,obsy) 29 | [ux_part2,uy_part2] = displacement(base[0],base[1],-s1,-s2, 30 | nu,obsx,obsy) 31 | ux = ux_part1+ux_part2 32 | uy = uy_part1+uy_part2 33 | return ux,uy 34 | 35 | def displacement(xi1,xi2,s1,s2,nu,x1,x2): 36 | """ 37 | Calculate displacements (u1,u2) in 2D in a half space at 38 | points (x1,x2) due to an edge dislocation at (xi1,xi2) with 39 | slip vector (s1,s2). This uses Eqs from Segall (2010), 40 | as corrected in the Errata to that book. Indices 1 and 2 41 | correspond to the x and y directions respectively. 42 | Arguments: 43 | xi1 = x coordinate of the dislocation tip 44 | xi2 = y coordinate of the dislocation tip 45 | x1 = x coordinates of observation points 46 | x2 = y coordinates of observation points 47 | s1 = x component of slip vector 48 | s2 = y component of slip vector 49 | nu = Poisson"s ratio 50 | Returns: 51 | ux = x components of displ. vectors at obs. points 52 | uy = y components of displ. vectors at obs. points 53 | """ 54 | # this occurs if the fault dips to the left 55 | if sign(s1)==sign(s2): 56 | # flip to the sign convention that Segall"s equations use 57 | s1 = -s1 58 | s2 = -s2 59 | # these equations are written for xi1 = 0. If that's not 60 | # the case, this makes it equivalent to that case 61 | x1 = x1-xi1 62 | r1_sq = x1**2.+(x2-xi2)**2. 63 | r2_sq = x1**2.+(x2+xi2)**2. 64 | r1 = sqrt(r1_sq) 65 | r2 = sqrt(r2_sq) 66 | log_r2_r1 = log(r2/r1) 67 | # calculate the angles relative to the vertical axis 68 | theta1 = arctan2(x1,(x2-xi2)) 69 | theta2 = arctan2(x1,(x2+xi2)) 70 | dip = arctan(s2/s1) 71 | # this puts dip in the range [0,pi] 72 | if dip<0: 73 | dip=dip+pi 74 | # the following puts the atan branch cuts along the fault 75 | # by rotating theta1 to point down the fault and theta2 to 76 | # point up (above the half space) along it 77 | # Shift theta1 to be measured up from the fault 78 | theta1 = theta1+pi/2.+dip 79 | # shift theta2 to be measured from a line pointing up 80 | # opposite the fault from the image dislocation 81 | theta2 = theta2+dip-pi/2. 82 | theta1 = mod(theta1,2.*pi) 83 | theta2 = mod(theta2,2.*pi) 84 | # make a correction for rounding errors that can occur 85 | # when theta1 or theta2 is very close to 0 or 2*pi. 86 | theta1[2.*pi-theta1<1e-10] = 0. 87 | theta2[2.*pi-theta2<1e-10] = 0. 88 | theta_diff = theta1-theta2 89 | # these are two very long equations.... 90 | u1 = (-s1/(pi*(1.-nu)))*(((1.-nu)/2.)*(-theta_diff)+x1*(x2-xi2)/(4.*r1_sq) - x1*(x2+(3.-4.*nu)*xi2)/(4.*r2_sq)+xi2*x2*x1*(x2+xi2)/r2_sq**2.) + (s2/(pi*(1.-nu)))*(((1.-2.*nu)/4.)*log_r2_r1-(x2-xi2)**2./(4.*r1_sq) + (x2**2.+xi2**2.-4.*(1.-nu)*xi2*(x2+xi2))/(4.*r2_sq)+x2*xi2*(x2+xi2)**2./r2_sq**2.) 91 | u2 = (-s1/(pi*(1.-nu)))*(((1.-2.*nu)/4.)*log_r2_r1+(x2-xi2)**2./(4.*r1_sq) - ((x2+xi2)**2.-2.*xi2**2.-2.*(1.-2.*nu)*xi2*(x2+xi2))/(4.*r2_sq) + x2*xi2*(x2+xi2)**2./r2_sq**2.)+(s2/(pi*(1.-nu)))*(((1.-nu)/2.)*(theta_diff) + x1*(x2-xi2)/(4.*r1_sq)-x1*(x2+(3.-4.*nu)*xi2)/(4.*r2_sq)-xi2*x2*x1*(x2+xi2)/r2_sq**2.) 92 | 93 | return u1, u2 -------------------------------------------------------------------------------- /source/functions/down_plunge.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def down_plunge(bs,trd,plg): 4 | """ 5 | down_plunge constructs the down plunge projection of a bed 6 | 7 | bs is a npoints x 3 array, which holds npoints 8 | on the digitized bed, each point defined by 9 | 3 coordinates: X1 = East, X2 = North, X3 = Up 10 | 11 | trd and plg are the trend and plunge of the fold axis 12 | and they should be entered in radians 13 | 14 | dpbs are the bed's transformed coordinates 15 | 16 | Python function translated from the Matlab function 17 | DownPlunge in Allmendinger et al. (2012) 18 | """ 19 | # number of points in bed 20 | nvtex = bs.shape[0] 21 | 22 | # allocate some arrays 23 | a=np.zeros((3,3)) 24 | dpbs = np.zeros((np.shape(bs))) 25 | 26 | # calculate the transformation matrix a(i,j) 27 | a[0,0] = np.sin(trd)*np.sin(plg) 28 | a[0,1] = np.cos(trd)*np.sin(plg) 29 | a[0,2] = np.cos(plg) 30 | a[1,0] = np.cos(trd) 31 | a[1,1] = -np.sin(trd) 32 | a[2,0] = np.sin(trd)*np.cos(plg) 33 | a[2,1] = np.cos(trd)*np.cos(plg) 34 | a[2,2] = -np.sin(plg) 35 | 36 | # perform transformation 37 | for nv in range(nvtex): 38 | for i in range(3): 39 | dpbs[nv,i] = 0.0 40 | for j in range(3): 41 | dpbs[nv,i] = a[i,j]*bs[nv,j] + dpbs[nv,i] 42 | 43 | return dpbs -------------------------------------------------------------------------------- /source/functions/fin_strain.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from cart_to_sph import cart_to_sph 4 | 5 | def fin_strain(e,frame): 6 | """ 7 | fin_strain computes finite strain from an input 8 | displacement gradient tensor 9 | 10 | USE: eps,pstrain,dilat,maxsh = fin_strain(e,frame) 11 | 12 | e = 3 x 3 Lagrangian or Eulerian displacement gradient 13 | tensor 14 | frame = Reference frame. 0 = undeformed (Lagrangian) 15 | state, 1 = deformed (Eulerian) state 16 | eps = 3 x 3 Lagrangian or Eulerian strain tensor 17 | pstrain = 3 x 3 matrix with magnitude (column 1), trend 18 | (column 2) and plunge (column 3) of maximum 19 | (row 1), intermediate (row 2), and minimum 20 | (row 3) elongations 21 | dilat = dilatation 22 | maxsh = 1 x 2 vector with max. shear strain and 23 | orientation with respect to maximum principal 24 | strain direction. Only valid in 2D 25 | 26 | NOTE: Output angles are in radians 27 | 28 | Python function translated from the Matlab function 29 | FinStrain in Allmendinger et al. (2012) 30 | """ 31 | # initialize variables 32 | eps = np.zeros((3,3)) 33 | pstrain = np.zeros((3,3)) 34 | maxsh = np.zeros(2) 35 | 36 | # compute strain tensor 37 | for i in range(3): 38 | for j in range(3): 39 | eps[i,j] = 0.5*(e[i][j]+e[j][i]) 40 | for k in range(3): 41 | # if undeformed reference frame: 42 | # Lagrangian strain tensor 43 | if frame == 0: 44 | eps[i,j] = eps[i,j] + 0.5*(e[k][i]*e[k][j]) 45 | # if deformed reference frame: 46 | # Eulerian strain tensor 47 | elif frame == 1: 48 | eps[i,j] = eps[i,j] - 0.5*(e[k][i]*e[k][j]) 49 | 50 | # compute principal elongations and orientations 51 | D, V = np.linalg.eigh(eps) 52 | 53 | # Principal elongations 54 | for i in range(3): 55 | ind = 2-i 56 | # magnitude 57 | # if undeformed reference frame: 58 | # Lagrangian strain tensor 59 | if frame == 0: 60 | pstrain[i,0] = np.sqrt(1.0+2.0*D[ind])-1.0 61 | # if deformed reference frame: 62 | # Eulerian strain tensor 63 | elif frame == 1: 64 | pstrain[i,0] = np.sqrt(1.0/(1.0-2.0*D[ind]))-1.0 65 | # orientations 66 | pstrain[i,1],pstrain[i,2] = cart_to_sph(V[0,ind], 67 | V[1,ind],V[2,ind]) 68 | 69 | # dilatation 70 | dilat = (1.0+pstrain[0,0])*(1.0+pstrain[1,0])* \ 71 | (1.0+pstrain[2,0]) - 1.0 72 | 73 | # maximum shear strain: This only works if plane strain 74 | lmax = (1.0+pstrain[0,0])**2 # maximum quad. elongation 75 | lmin = (1.0+pstrain[2,0])**2 # minimum quad. elongation 76 | # maximum shear strain: Ramsay (1967) Eq. 3.46 77 | maxsh[0] = (lmax-lmin)/(2.0*np.sqrt(lmax*lmin)) 78 | # angle of maximum shear strain with respect to maximum 79 | # principal strain. Ramsay (1967) Eq. 3.45 80 | # if undeformed reference frame 81 | if frame == 0: 82 | maxsh[1] = np.pi/4.0 83 | # if deformed reference frame 84 | elif frame == 1: 85 | maxsh[1] = np.arctan(np.sqrt(lmin/lmax)) 86 | 87 | return eps, pstrain, dilat, maxsh -------------------------------------------------------------------------------- /source/functions/fit_plane.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from pole import plane_from_pole 4 | from cart_to_sph import cart_to_sph 5 | 6 | def fit_plane(pts): 7 | """ 8 | fit_plane computes the best-fit plane for a group of 9 | points (position vectors) on the plane 10 | 11 | USE: stk, dip, stdev = fit_plane(pts) 12 | 13 | pts is a n x 3 matrix containing the East (column 1), 14 | North (column 2), and Up (column 3) coordinates 15 | of n points on the plane 16 | 17 | stk and dip are returned in radians 18 | 19 | stdev is the standard deviation of the distance of 20 | each point from the best-fit plane 21 | """ 22 | # compute the centroid of the selected points 23 | avge = np.mean(pts[:,0]) 24 | avgn = np.mean(pts[:,1]) 25 | avgu = np.mean(pts[:,2]) 26 | 27 | # compute the points vectors minus the centroid 28 | pts[:,0] = pts[:,0] - avge 29 | pts[:,1] = pts[:,1] - avgn 30 | pts[:,2] = pts[:,2] - avgu 31 | 32 | # compute the covariance/orientation matrix 33 | a = np.zeros((3,3)) 34 | for i in range(pts.shape[0]): 35 | ce = pts[i,0] 36 | cn = pts[i,1] 37 | cu = pts[i,2] 38 | # compute orientation matrix 39 | a[0,0] = a[0,0] + ce*ce 40 | a[0,1] = a[0,1] + ce*cn 41 | a[0,2] = a[0,2] + ce*cu 42 | a[1,1] = a[1,1] + cn*cn 43 | a[1,2] = a[1,2] + cn*cu 44 | a[2,2] = a[2,2] + cu*cu 45 | # the orientation matrix is symmetric so the 46 | # off-diagonal components are equal 47 | a[1,0] = a[0,1] 48 | a[2,0] = a[0,2] 49 | a[2,1] = a[1,2] 50 | 51 | # calculate the eigenvalues and eigenvectors of the 52 | # orientation matrix: use function eigh 53 | D, V = np.linalg.eigh(a) 54 | 55 | # calculate pole to best-fit plane = lowest eigenvalue 56 | # vector in N, E, D coordinates 57 | cn = V[1,0] 58 | ce = V[0,0] 59 | cd = -V[2,0] 60 | 61 | # find trend and plunge of pole to best fit plane 62 | trd, plg =cart_to_sph(cn,ce,cd) 63 | 64 | # find Best fit plane 65 | stk, dip = plane_from_pole(trd,plg) 66 | 67 | # calculate standard deviation = square root of 68 | # minimum eigenvalue 69 | stdev = np.sqrt(D[0]) 70 | 71 | return stk, dip, stdev -------------------------------------------------------------------------------- /source/functions/flex2d.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import matplotlib.ticker as ticker 4 | 5 | def flex2d(geom,elas,loads,fig,ax): 6 | """ 7 | flex2d computes the deflection profile produced by 8 | a group of load columns on an elastic lithosphere 9 | resting on a fluid-like foundation 10 | (i.e. astenosphere). The program is based on 11 | Hetenyi (1946) solution for an infinite elastic 12 | beam on a fluid-like foundation 13 | 14 | USE: w, wp = flex2d(geom,elas,loads,ax) 15 | 16 | geom: A 1 x 2 vector with the lateral extent of the 17 | domain in meters, and the distance between 18 | points (x interval) in meters where the 19 | deflection will be computed 20 | elas: A 1 x 4 vector with the Young Modulus (in Pa), 21 | Poisson ratio, Elastic thickness in meters, 22 | and density of the foundation in kg/m^3 23 | loads: A nloads x 4 vector with the left x coordinate, 24 | right x coordinate, height, and density of each 25 | load column. Lengths should be in meters and 26 | density in kg/m^3 27 | fig: a figure handle for the plots 28 | ax: an array of two axis handles for the plots 29 | w: The deflection of the lithosphere in meters at the 30 | points specified by the extent and x interval 31 | wp: 3 x 2 matrix with key deflection parameters: 32 | 1st row is the maximum deflection (maximum basin 33 | depth) and x at this location 34 | 2nd row is the zero deflection and x at this 35 | location (basin width) 36 | 3rd row is the minimum deflection (forebulge) 37 | and x at this location 38 | """ 39 | # geometry 40 | extent = geom[0] # extent in x 41 | xint = geom[1] # interval in x 42 | x = np.arange(0,extent+1,xint) # points in x 43 | w = np.zeros(len(x)) # initialize displacement 44 | 45 | # elastic and flexural parameters 46 | E = elas[0] # Young Modulus 47 | v = elas[1] # Poisson ratio 48 | h = elas[2] # elastic thickness 49 | # flexural rigidity 50 | rigid = (E*h*h*h)/(12*(1.0-v*v)) 51 | densup = elas[3] # density of foundation 52 | g = 9.81 # gravity 53 | k = densup*g # support of foundation 54 | # flexural parameter 55 | alpha = ((4*rigid)/(k))**(1/4) 56 | 57 | # loads 58 | lxmin = loads[:,0] 59 | lxmax = loads[:,1] 60 | lh = loads[:,2] 61 | ldens = loads[:,3] 62 | 63 | # compute deflection profile 64 | # for all the loads columns 65 | for i in range(len(lxmin)): 66 | q = lh[i] * ldens[i] * 9.81 67 | # for all points in x 68 | for j in range(len(x)): 69 | tolf = abs(x[j] - lxmin[i]) 70 | tort = abs(x[j] - lxmax[i]) 71 | dA = np.exp(-tolf/alpha)*np.cos(tolf/alpha) 72 | dB = np.exp(-tort/alpha)*np.cos(tort/alpha) 73 | # if below the load 74 | if x[j] >= lxmin[i] and x[j] <= lxmax[i]: 75 | w[j] = w[j] + (q/(2.0*k))*(2.0-dA-dB) 76 | # if to the left of the load 77 | elif x[j] < lxmin[i]: 78 | w[j] = w[j] + (q/(2.0*k))*(dA-dB) 79 | # if to the right of the load 80 | elif x[j] > lxmax[i]: 81 | w[j] = w[j] - (q/(2.0*k))*(dA-dB) 82 | 83 | # key deflection parameters 84 | wp = np.zeros((3,2)) 85 | # maximum basin depth 86 | v = max(w) 87 | ind = w.argmax() 88 | wp[0,0] = v 89 | wp[0,1] = x[ind] 90 | # basin width 91 | ind = np.where(w <=0)[0] 92 | wp[1,0] = 0.0 93 | wp[1,1] = x[ind[0]] 94 | # forebulge 95 | v = min(w) 96 | ind = w.argmin() 97 | wp[2,0] = v 98 | wp[2,1] = x[ind] 99 | 100 | # plot 101 | # input loads 102 | for i in range(len(lxmin)): 103 | xcol = [lxmin[i], lxmin[i], lxmax[i], lxmax[i]] 104 | ycol = [0, lh[i], lh[i], 0] 105 | ax[0].plot(xcol,ycol,"k-") 106 | maxlh = max(lh)*1.25 107 | ax[0].axis([0, geom[0], -maxlh, maxlh]) 108 | ax[0].grid() 109 | ax[0].set_xlabel("m") 110 | ax[0].set_ylabel("m") 111 | ax[0].xaxis.set_major_formatter(ticker.FormatStrFormatter 112 | ("%0.0e")) 113 | ax[0].set_title("Input loads",fontweight="bold") 114 | 115 | # deflected loads plus deflection 116 | wl = np.interp(lxmin,x,w) 117 | wr = np.interp(lxmax,x,w) 118 | for i in range(len(lxmin)): 119 | xcol = [lxmin[i], lxmin[i], lxmax[i], lxmax[i]] 120 | ycol = [-wl[i], lh[i]-wl[i], lh[i]-wr[i], -wr[i]] 121 | ax[1].plot(xcol,ycol,"k-") 122 | ax[1].plot(x,-w,"b-") 123 | ax[1].axis([0, geom[0], -maxlh, maxlh]) 124 | ax[1].grid() 125 | ax[1].set_xlabel("m") 126 | ax[1].set_ylabel("m") 127 | ax[1].xaxis.set_major_formatter(ticker.FormatStrFormatter 128 | ("%0.0e")) 129 | ax[1].set_title("Deflected loads",fontweight="bold") 130 | fig.subplots_adjust(hspace=0.6) 131 | 132 | return w, wp -------------------------------------------------------------------------------- /source/functions/general_shear.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | def general_shear(pts,st1,gamma,kk,ninc,ax): 5 | """ 6 | general_shear computes displacement paths, kinematic 7 | vorticity numbers and progressive finite strain 8 | history, for general shear with a pure shear stretch, 9 | no area change, and a single shear strain 10 | 11 | USE: paths,wk,pfs = 12 | general_shear(pts,st1,gamma,kk,ninc,ax) 13 | 14 | pts = npoints x 2 matrix with X1 and X3 coord. of points 15 | st1 = Pure shear stretch parallel to shear zone 16 | gamma = Engineering shear strain 17 | kk = An integer that indicates whether the maximum 18 | finite stretch is parallel (kk = 0), or 19 | perpendicular (kk=1) to the shear direction 20 | ninc = number of strain increments 21 | ax = an array of two axis handles for the plots 22 | paths = displacement paths of points 23 | wk = Kinematic vorticity number 24 | pfs = progressive finite strain history. column 1 = 25 | orientation of maximum stretch with respect to 26 | X1, column 2 = maximum stretch magnitude 27 | 28 | NOTE: Intermediate principal stretch is 1.0 (Plane 29 | strain). Output orientations are in radians 30 | 31 | Python function translated from the Matlab function 32 | GeneralShear in Allmendinger et al. (2012) 33 | """ 34 | # compute minimum principal stretch and incr. stretches 35 | st1inc =st1**(1.0/ninc) 36 | st3 =1.0/st1 37 | st3inc =st3**(1.0/ninc) 38 | 39 | # incremental engineering shear strain 40 | gammainc = gamma/ninc 41 | 42 | # initialize displacement paths 43 | npts = pts.shape[0] # number of points 44 | paths = np.zeros((ninc+1,npts,2)) 45 | paths[0,:,:] = pts # initial points of paths 46 | 47 | # calculate incremental deformation gradient tensor 48 | # if max. stretch parallel to shear direction Eq. 8.45 49 | if kk == 0: 50 | F=np.zeros((2,2)) 51 | F[0,]=[st1inc, (gammainc*(st1inc-st3inc))/ 52 | (2.0*np.log(st1inc))] 53 | F[1,]=[0.0, st3inc] 54 | # if max. stretch perpendicular to shear direction Eq. 8.46 55 | elif kk == 1: 56 | F=np.zeros((2,2)) 57 | F[0,]= [st3inc, (gammainc*(st3inc-st1inc))/ 58 | (2.0*np.log(st3inc))] 59 | F[1,]= [0.0, st1inc] 60 | 61 | # compute displacement paths 62 | for i in range(npts): # for all points 63 | for j in range(ninc+1): # for all strain increments 64 | for k in range(2): 65 | for L in range(2): 66 | paths[j,i,k] = F[k,L]*paths[j-1,i,L] + paths[j,i,k] 67 | # plot displacement path of point 68 | xx = paths[:,i,0] 69 | yy = paths[:,i,1] 70 | ax[0].plot(xx,yy,"k.-") 71 | 72 | # plot initial and final polygons 73 | inpol = np.zeros((npts+1,2)) 74 | inpol[0:npts,]=paths[0,0:npts,:] 75 | inpol[npts,] = inpol[0,] 76 | ax[0].plot(inpol[:,0],inpol[:,1],"b-") 77 | finpol = np.zeros((npts+1,2)) 78 | finpol[0:npts,]=paths[ninc,0:npts,:] 79 | finpol[npts,] = finpol[0,] 80 | ax[0].plot(finpol[:,0],finpol[:,1],"r-") 81 | 82 | # set axes 83 | ax[0].set_xlabel(r"$\mathbf{X_1}$") 84 | ax[0].set_ylabel(r"$\mathbf{X_3}$") 85 | ax[0].grid() 86 | ax[0].axis("equal") 87 | 88 | # determine the eigenvectors of the flow (apophyses) 89 | # since F is not symmetrical, use function eig 90 | _,V = np.linalg.eig(F) 91 | theta2 = np.arctan(V[1,1]/V[0,1]) 92 | wk = np.cos(theta2) 93 | 94 | # initalize progressive finite strain history. 95 | # we are not including the initial state 96 | pfs = np.zeros((ninc,ninc)) 97 | 98 | # calculate progressive finite strain history 99 | for i in range(1,ninc+1): 100 | # determine the finite deformation gradient tensor 101 | finF = np.linalg.matrix_power(F, i) 102 | # determine Green deformation tensor 103 | G = np.dot(finF,finF.conj().transpose()) 104 | # stretch magnitude and orientation: Maximum 105 | # eigenvalue and their corresponding eigenvectors 106 | # of Green deformation tensor 107 | D, V = np.linalg.eigh(G) 108 | pfs[i-1,0] = np.arctan(V[1,1]/V[0,1]) 109 | pfs[i-1,1] = np.sqrt(D[1]) 110 | 111 | # plot progressive finite strain history 112 | ax[1].plot(pfs[:,0]*180/np.pi,pfs[:,1],"k.-") 113 | ax[1].set_xlabel(r"$\Theta\;(\circ)$") 114 | ax[1].set_ylabel("Maximum finite stretch") 115 | ax[1].set_xlim(-90,90) 116 | ax[1].set_ylim(1,max(pfs[:,1])+0.5) 117 | ax[1].grid() 118 | 119 | return paths, wk, pfs -------------------------------------------------------------------------------- /source/functions/geogr_to_view.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from sph_to_cart import sph_to_cart 3 | from cart_to_sph import cart_to_sph 4 | from zero_twopi import zero_twopi 5 | 6 | def geogr_to_view(trd,plg,trdv,plgv): 7 | """ 8 | geogr_to_view transforms a line from NED to View 9 | direction coordinates 10 | trd = trend of line 11 | plg = plunge of line 12 | trdv = trend of view direction 13 | plgv = plunge of view direction 14 | rtrd and rplg are the new trend and plunge of the line 15 | in the view direction. 16 | 17 | NOTE: Input/Output angles are in radians 18 | 19 | Python function translated from the Matlab function 20 | GeogrToView in Allmendinger et al. (2012) 21 | """ 22 | # some constants 23 | east = np.pi/2.0 24 | 25 | # make transformation matrix between NED and view direction 26 | a = np.zeros((3,3)) 27 | a[2,0], a[2,1], a[2,2] = sph_to_cart(trdv,plgv) 28 | temp1 = trdv + east 29 | temp2 = 0.0 30 | a[1,0], a[1,1], a[1,2] = sph_to_cart(temp1,temp2) 31 | temp1 = trdv 32 | temp2 = plgv - east 33 | a[0,0], a[0,1], a[0,2] = sph_to_cart(temp1,temp2) 34 | 35 | # direction cosines of line 36 | dc = np.zeros(3) 37 | dc[0], dc[1], dc[2] = sph_to_cart(trd,plg) 38 | 39 | # transform line 40 | ndc = np.zeros(3) 41 | for i in range(3): 42 | ndc[i] = a[i,0]*dc[0] + a[i,1]*dc[1]+ a[i,2]*dc[2] 43 | 44 | # compute line from new direction cosines 45 | rtrd, rplg = cart_to_sph(ndc[0],ndc[1],ndc[2]) 46 | 47 | # take care of negative plunges 48 | if rplg < 0.0 : 49 | rtrd = zero_twopi(rtrd+np.pi) 50 | rplg *= -1.0 51 | 52 | return rtrd, rplg -------------------------------------------------------------------------------- /source/functions/great_circle.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from pole import pole_from_plane 3 | from rotate import rotate 4 | from st_coord_line import st_coord_line 5 | 6 | def great_circle(stk,dip,stype): 7 | """ 8 | great_circle computes the great circle path of a plane 9 | on an equal angle or equal area stereonet of unit radius 10 | 11 | stk = strike of plane 12 | dip = dip of plane 13 | stype = Stereonet type: 0 = equal angle, 1 = equal area 14 | path = x and y coordinates of points in great circle path 15 | 16 | NOTE: stk and dip should be entered in radians. 17 | and follow the RHR convention 18 | 19 | Python function translated from the Matlab function 20 | great_circle in Allmendinger et al. (2012) 21 | """ 22 | pi = np.pi 23 | # Compute the pole to the plane. This will be the axis of 24 | # rotation to make the great circle 25 | trda, plga = pole_from_plane(stk,dip) 26 | 27 | # Now pick the stk line at the intersection of the 28 | # great circle with the primitive of the stereonet 29 | trd, plg = stk, 0.0 30 | 31 | # To make the great circle, rotate the line 180 degrees 32 | # in increments of 1 degree 33 | rot = np.radians(np.arange(0,181,1)) 34 | path = np.zeros((rot.shape[0],2)) 35 | 36 | for i in range(rot.shape[0]): 37 | # Avoid joining ends of path 38 | if rot[i] == pi: 39 | rot[i] = rot[i] * 0.9999 40 | # Rotate line 41 | rtrd, rplg = rotate(trda,plga,rot[i],trd,plg,"a") 42 | # Calculate stereonet coordinates of rotated line 43 | # and add to great circle path 44 | path[i,0], path[i,1] = st_coord_line(rtrd,rplg,stype) 45 | 46 | return path -------------------------------------------------------------------------------- /source/functions/grid_fin_strain.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import matplotlib.colors as mcolors 4 | from matplotlib.patches import Polygon 5 | from matplotlib.collections import PatchCollection 6 | from scipy.spatial import Delaunay 7 | from lscov import lscov 8 | from fin_strain import fin_strain 9 | 10 | def grid_fin_strain(pos,disp,frame,k,par,plotpar,plotst,fig,ax): 11 | """ 12 | grid_fin_strain computes the finite strain of a group 13 | of points with displacements in x and y. 14 | Strain in z is assumed to be zero (plane strain) 15 | 16 | USE: cent,eps,pstrain,dilat,maxsh = grid_fin_strain(pos, 17 | disp,frame,k,par,plotpar,plotst,fig,ax) 18 | 19 | pos = npoints x 2 matrix with x and y position 20 | of points 21 | disp = nstations x 2 matrix with x and y 22 | displacements of points 23 | frame = Reference frame. 0 = undeformed (Lagrangian) 24 | state, 1 = deformed (Eulerian) state 25 | k = Type of computation: Delaunay (k = 0), nearest 26 | neighbor (k = 1), or distance weighted (k = 2) 27 | par = Parameters for nearest neighbor or distance 28 | weighted computation. If Delaunay (k = 0), enter 29 | a scalar corresponding to the minimum internal 30 | angle of a triangle valid for computation. 31 | If nearest neighbor (k = 1), input a 1 x 3 vector 32 | with grid spacing, number of nearest neighbors, 33 | and maximum distance to neighbors. If distance 34 | weighted (k = 2), input a 1 x 2 vector with grid 35 | spacing and distance weighting factor alpha 36 | plotpar = Parameter to color the cells: Max elongation 37 | (plotpar = 0), minimum elongation 38 | (plotpar = 1), dilatation (plotpar = 2), 39 | or max. shear strain (plotpar = 3) 40 | plotst = A flag to plot the stations (1) or not (0) 41 | fig = figure handle for the plot 42 | ax = axis handle for the plot 43 | cent = ncells x 2 matrix with x and y positions of the 44 | cells centroids 45 | eps = 3 x 3 x ncells array with strain tensors of 46 | the cells 47 | pstrain = 3 x 3 x ncells array with magnitude and 48 | orientation of principal strains of the cells 49 | dilat = ncells x 1 vector with dilatation of the cells 50 | maxsh = ncells x 2 matrix with max. shear strain and 51 | orientation with respect to maximum principal 52 | strain direction, of the cells. 53 | Only valid for plane strain 54 | 55 | NOTE: Input/Output angles are in radians. Output 56 | azimuths are given with respect to y 57 | pos, disp, grid spacing, max. distance to 58 | neighbors, and alpha should be in the same 59 | length units 60 | """ 61 | # if Delaunay 62 | if k == 0: 63 | # indexes of triangles vertices 64 | # use function Delaunay 65 | tri = Delaunay(pos) 66 | inds = tri.simplices 67 | # number of cells 68 | ncells = inds.shape[0] 69 | # number of stations per cell = 3 70 | nstat = 3 71 | # centers of cells 72 | cent = np.zeros((ncells,2)) 73 | for i in range(ncells): 74 | # triangle vertices 75 | v1x=pos[inds[i,0],0] 76 | v2x=pos[inds[i,1],0] 77 | v3x=pos[inds[i,2],0] 78 | v1y=pos[inds[i,0],1] 79 | v2y=pos[inds[i,1],1] 80 | v3y=pos[inds[i,2],1] 81 | # center of cell 82 | cent[i,0]=(v1x + v2x + v3x)/3.0 83 | cent[i,1]=(v1y + v2y + v3y)/3.0 84 | # triangle internal angles 85 | s1 = np.sqrt((v3x-v2x)**2 + (v3y-v2y)**2) 86 | s2 = np.sqrt((v1x-v3x)**2 + (v1y-v3y)**2) 87 | s3 = np.sqrt((v2x-v1x)**2 + (v2y-v1y)**2) 88 | a1 = np.arccos((v2x-v1x)*(v3x-v1x)/(s3*s2)+\ 89 | (v2y-v1y)*(v3y-v1y)/(s3*s2)) 90 | a2 = np.arccos((v3x-v2x)*(v1x-v2x)/(s1*s3)+\ 91 | (v3y-v2y)*(v1y-v2y)/(s1*s3)) 92 | a3 = np.arccos((v2x-v3x)*(v1x-v3x)/(s1*s2)+\ 93 | (v2y-v3y)*(v1y-v3y)/(s1*s2)) 94 | # if any of the internal angles is less than 95 | # specified minimum, invalidate triangle 96 | if a1 < par or a2 < par or a3 < par: 97 | inds[i,:] = np.zeros(3) 98 | # if nearest neighbor or distance weighted 99 | else: 100 | # construct grid 101 | xmin = min(pos[:,0]); xmax = max(pos[:,0]) 102 | ymin = min(pos[:,1]); ymax = max(pos[:,1]) 103 | cellsx = int(np.ceil((xmax-xmin)/par[0])) 104 | cellsy = int(np.ceil((ymax-ymin)/par[0])) 105 | xgrid = np.arange(xmin,(xmin+(cellsx+1)*par[0]),par[0]) 106 | ygrid = np.arange(ymin,(ymin+(cellsy+1)*par[0]),par[0]) 107 | XX,YY = np.meshgrid(xgrid,ygrid) 108 | # number of cells 109 | ncells = cellsx * cellsy 110 | # number of stations per cell (nstat) and 111 | # other parameters 112 | # if nearest neighbor 113 | if k == 1: 114 | nstat = par[1] # max neighbors 115 | sqmd = par[2]**2 # max squared distance 116 | # if distance weighted 117 | elif k == 2: 118 | nstat = pos.shape[0] # all stations 119 | dalpha = 2.0*par[1]*par[1] # 2*alpha*alpha 120 | # cells' centers 121 | cent = np.zeros((ncells,2)) 122 | count = 0 123 | for i in range(cellsy): 124 | for j in range(cellsx): 125 | cent[count,0] = (XX[i,j]+XX[i,j+1])/2.0 126 | cent[count,1] = (YY[i,j]+YY[i+1,j])/2.0 127 | count += 1 128 | # initialize stations indexes for cells to -1 129 | inds = np.ones((ncells,nstat), dtype=int)*-1 130 | # initialize weight matrix for distance weighted 131 | wv = np.zeros((ncells,nstat*2)) 132 | # for all cells set stations indexes 133 | for i in range(ncells): 134 | # initialize sq distances to -1.0 135 | sds = np.ones(nstat)*-1.0 136 | # for all stations 137 | for j in range(pos.shape[0]): 138 | # square distance from cell center to station 139 | dx = cent[i,0] - pos[j,0] 140 | dy = cent[i,1] - pos[j,1] 141 | sd = dx**2 + dy**2 142 | # if nearest neighbor 143 | if k == 1: 144 | # if within the max sq distance 145 | if sd <= sqmd: 146 | minsd = min(sds) 147 | mini = np.argmin(sds) 148 | # if less than max neighbors 149 | if minsd == -1.0: 150 | sds[mini] = sd 151 | inds[i,mini] = j 152 | # if max neighbors 153 | else: 154 | # ff sq distance is less 155 | # than neighbors max sq distance 156 | maxsd = max(sds) 157 | maxi = np.argmax(sds) 158 | if sd < maxsd: 159 | sds[maxi] = sd 160 | inds[i,maxi] = j 161 | # if distance weighted 162 | elif k == 2: 163 | # all stations indexes 164 | inds[i,:] = np.arange(nstat) 165 | # Eq. 8.27: Weight factor 166 | weight = np.exp(-sd/dalpha) 167 | wv[i,j*2] = weight 168 | wv[i,j*2+1] = weight 169 | 170 | # initialize arrays 171 | y = np.zeros(nstat*2) 172 | M = np.zeros((nstat*2,6)) 173 | e = np.zeros((3,3)) 174 | eps = np.zeros((3,3,ncells)) 175 | pstrain = np.zeros((3,3,ncells)) 176 | dilat = np.zeros((ncells,1)) 177 | maxsh = np.zeros((ncells,2)) 178 | 179 | # for each cell 180 | for i in range(ncells): 181 | # if required minimum number of stations 182 | if min(inds[i,:]) >= 0: 183 | # Eq. 8.24: Displacements column vector y 184 | # and design matrix M. X1 = y, X2 = x 185 | for j in range(nstat): 186 | ic = inds[i,j] 187 | y[j*2] = disp[ic,1] 188 | y[j*2+1] = disp[ic,0] 189 | M[j*2,:] = [1.,0.,pos[ic,1],pos[ic,0],0.,0.] 190 | M[j*2+1,:] = [0.,1.,0.,0.,pos[ic,1],pos[ic,0]] 191 | # Eqs. 8.25-8.26: Find x using function lscov 192 | # if Delaunay or nearest neighbor 193 | if k == 0 or k == 1: 194 | x = lscov(M,y) 195 | # if distance weighted 196 | elif k == 2: 197 | x = lscov(M,y,wv[i,:]) 198 | # displacement gradient tensor 199 | for j in range(2): 200 | e[j,0] = x[j*2+2] 201 | e[j,1] = x[j*2+3] 202 | # compute strain 203 | eps[:,:,i],pstrain[:,:,i],dilat[i,:],\ 204 | maxsh[i,:] = fin_strain(e,frame) 205 | 206 | # variable to plot 207 | # if maximum principal strain 208 | if plotpar == 0: 209 | vp = pstrain[0,0,:] 210 | vmin = 0.0 211 | vmax = 2.0 212 | lcb = "emax" 213 | # if minimum principal strain 214 | elif plotpar == 1: 215 | vp = pstrain[2,0,:] 216 | vmin = -2.0 217 | vmax = 0.0 218 | lcb = "emin" 219 | # if dilatation: 220 | elif plotpar == 2: 221 | vp = dilat[:] 222 | vmin = -1.0 223 | vmax = 1.0 224 | lcb = "dilatation" 225 | # if max. shear strain 226 | elif plotpar == 3: 227 | vp = maxsh[:,0] 228 | vmin = 0.0 229 | vmax = 2.0 230 | lcb = "max. shear strain" 231 | 232 | # Patches and colors for cells 233 | patches = [] 234 | colors = [] 235 | 236 | # fill cells patches and colors 237 | # if Delaunay 238 | if k == 0: 239 | for i in range(ncells): 240 | # If minimum number of stations 241 | if min(inds[i,:]) >= 0: 242 | xpyp = [[pos[inds[i,0],0],pos[inds[i,0],1]],\ 243 | [pos[inds[i,1],0],pos[inds[i,1],1]],\ 244 | [pos[inds[i,2],0],pos[inds[i,2],1]]] 245 | polygon = Polygon(xpyp, True) 246 | patches.append(polygon) 247 | colors.append(vp[i]) 248 | # if nearest neighbor or distance weighted 249 | if k == 1 or k == 2: 250 | count = 0 251 | for i in range(cellsy): 252 | for j in range(cellsx): 253 | # if minimum number of stations 254 | if min(inds[count,:]) >= 0: 255 | xpyp = [[XX[i,j],YY[i,j]],[XX[i,j+1],YY[i,j+1]],\ 256 | [XX[i+1,j+1],YY[i+1,j+1]],[XX[i+1,j],YY[i+1,j]]] 257 | polygon = Polygon(xpyp, True) 258 | patches.append(polygon) 259 | colors.append(vp[count]) 260 | count += 1 261 | 262 | # collect cells patches 263 | pcoll = PatchCollection(patches) 264 | # cells colors 265 | pcoll.set_array(np.array(colors)) 266 | # color map is blue to red 267 | pcoll.set_cmap("jet") 268 | norm = mcolors.Normalize(vmin=vmin,vmax=vmax) 269 | pcoll.set_norm(norm) 270 | 271 | # draw cells 272 | ax.add_collection(pcoll) 273 | 274 | # plot stations 275 | if plotst == 1: 276 | ax.plot(pos[:,0],pos[:,1],"k.",markersize=2) 277 | 278 | # axes 279 | ax.axis("equal") 280 | ax.set_xlabel("x") 281 | ax.set_ylabel("y") 282 | 283 | # color bar with nice ticks 284 | intv = (vmax-vmin)*0.25 285 | ticks=[vmin,vmin+intv,vmin+2*intv,vmin+3*intv,vmax] 286 | lticks = ["{:.2}".format(ticks[0]),\ 287 | "{:.2}".format(ticks[1]),"{:.2}".format(ticks[2]),\ 288 | "{:.2}".format(ticks[3]),"{:.2}".format(ticks[4])] 289 | cbar = fig.colorbar(pcoll, label=lcb, ticks=ticks) 290 | cbar.ax.set_yticklabels(lticks) 291 | 292 | return cent, eps, pstrain, dilat, maxsh -------------------------------------------------------------------------------- /source/functions/grid_inf_strain.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | import matplotlib.colors as mcolors 4 | from matplotlib.patches import Polygon 5 | from matplotlib.collections import PatchCollection 6 | from scipy.spatial import Delaunay 7 | 8 | from lscov import lscov as lscov 9 | from inf_strain import inf_strain 10 | 11 | def grid_inf_strain(pos,disp,k,par,plotpar,plotst,fig,ax): 12 | """ 13 | grid__inf_strain computes the infinitesimal strain 14 | of a network of stations with displacements in x 15 | (east) and y (north). Strain in z is assumed to be 16 | zero (plane strain) 17 | 18 | USE: cent,eps,ome,pstrain,rotc = 19 | grid_inf_strain(pos,disp,k,par,plotpar,plotst,fig,ax) 20 | 21 | pos = nstations x 2 matrix with x (east) and y (north) 22 | positions of stations in meters 23 | disp = nstations x 2 matrix with x (east) and y (north) 24 | displacements of stations in meters 25 | k = Type of computation: Delaunay (k = 0), nearest 26 | neighbor (k = 1), or distance weighted (k = 2) 27 | par = Parameters for nearest neighbor or distance 28 | weighted computation. If Delaunay (k = 0), enter 29 | a scalar corresponding to the minimum internal 30 | angle of a triangle valid for computation. 31 | If nearest neighbor (k = 1), input a 1 x 3 vector 32 | with grid spacing, number of nearest neighbors, 33 | and maximum distance to neighbors. If distance 34 | weighted (k = 2), input a 1 x 2 vector with grid 35 | spacing and distance weighting factor alpha 36 | plotpar = Parameter to color the cells: Max elongation 37 | (plotpar = 0), minimum elongation 38 | (plotpar = 1), rotation (plotpar = 2), 39 | or dilatation (plotpar = 3) 40 | plotst = A flag to plot the stations (1) or not (0) 41 | fig = figure handle for the plot 42 | ax = axis handle for the plot 43 | cent = ncells x 2 matrix with x and y positions of 44 | cells centroids 45 | eps = 3 x 3 x ncells array with strain tensors of 46 | the cells 47 | ome = 3 x 3 x ncells array with rotation tensors of 48 | the cells 49 | pstrain = 3 x 3 x ncells array with magnitude and 50 | orientation of principal strains of 51 | the cells 52 | rotc = ncells x 3 matrix with rotation components 53 | of cells 54 | 55 | NOTE: Input/Output angles are in radians. Output 56 | azimuths are given with respect to North 57 | pos, disp, grid spacing, max. distance to 58 | neighbors, and alpha should be in meters 59 | 60 | Python function translated from the Matlab function 61 | GridStrain in Allmendinger et al. (2012) 62 | """ 63 | pi = np.pi 64 | # if Delaunay 65 | if k == 0: 66 | # indexes of triangles vertices 67 | # use function Delaunay 68 | tri = Delaunay(pos) 69 | inds = tri.simplices 70 | # number of cells 71 | ncells = inds.shape[0] 72 | # number of stations per cell = 3 73 | nstat = 3 74 | # centers of cells 75 | cent = np.zeros((ncells,2)) 76 | for i in range(ncells): 77 | # triangle vertices 78 | v1x=pos[inds[i,0],0] 79 | v2x=pos[inds[i,1],0] 80 | v3x=pos[inds[i,2],0] 81 | v1y=pos[inds[i,0],1] 82 | v2y=pos[inds[i,1],1] 83 | v3y=pos[inds[i,2],1] 84 | # center of cell 85 | cent[i,0]=(v1x + v2x + v3x)/3.0 86 | cent[i,1]=(v1y + v2y + v3y)/3.0 87 | # triangle internal angles 88 | s1 = np.sqrt((v3x-v2x)**2 + (v3y-v2y)**2) 89 | s2 = np.sqrt((v1x-v3x)**2 + (v1y-v3y)**2) 90 | s3 = np.sqrt((v2x-v1x)**2 + (v2y-v1y)**2) 91 | a1 = np.arccos((v2x-v1x)*(v3x-v1x)/(s3*s2)+\ 92 | (v2y-v1y)*(v3y-v1y)/(s3*s2)) 93 | a2 = np.arccos((v3x-v2x)*(v1x-v2x)/(s1*s3)+\ 94 | (v3y-v2y)*(v1y-v2y)/(s1*s3)) 95 | a3 = np.arccos((v2x-v3x)*(v1x-v3x)/(s1*s2)+\ 96 | (v2y-v3y)*(v1y-v3y)/(s1*s2)) 97 | # if any of the internal angles is less than 98 | # specified minimum, invalidate triangle 99 | if a1 < par or a2 < par or a3 < par: 100 | inds[i,:] = np.zeros(3) 101 | # if nearest neighbor or distance weighted 102 | else: 103 | # construct grid 104 | xmin, xmax = min(pos[:,0]), max(pos[:,0]) 105 | ymin, ymax = min(pos[:,1]), max(pos[:,1]) 106 | cellsx = int(np.ceil((xmax-xmin)/par[0])) 107 | cellsy = int(np.ceil((ymax-ymin)/par[0])) 108 | xgrid = np.arange(xmin,(xmin+(cellsx+1)*par[0]),par[0]) 109 | ygrid = np.arange(ymin,(ymin+(cellsy+1)*par[0]),par[0]) 110 | XX,YY = np.meshgrid(xgrid,ygrid) 111 | # number of cells 112 | ncells = cellsx * cellsy 113 | # number of stations per cell (nstat) and 114 | # other parameters 115 | # if nearest neighbor 116 | if k == 1: 117 | nstat = par[1] # max neighbors 118 | sqmd = par[2]**2 # max squared distance 119 | # if distance weighted 120 | elif k == 2: 121 | nstat = pos.shape[0] # all stations 122 | dalpha = 2.0*par[1]*par[1] # 2*alpha*alpha 123 | # cells" centers 124 | cent = np.zeros((ncells,2)) 125 | count = 0 126 | for i in range(cellsy): 127 | for j in range(cellsx): 128 | cent[count,0] = (XX[i,j]+XX[i,j+1])/2.0 129 | cent[count,1] = (YY[i,j]+YY[i+1,j])/2.0 130 | count += 1 131 | # initialize stations indexes for cells to -1 132 | inds = np.ones((ncells,nstat), dtype=int)*-1 133 | # initialize weight matrix for distance weighted 134 | wv = np.zeros((ncells,nstat*2)) 135 | # for all cells set stations indexes 136 | for i in range(ncells): 137 | # initialize sq distances to -1.0 138 | sds = np.ones(nstat)*-1.0 139 | # for all stations 140 | for j in range(pos.shape[0]): 141 | # square distance from cell center to station 142 | dx = cent[i,0] - pos[j,0] 143 | dy = cent[i,1] - pos[j,1] 144 | sd = dx**2 + dy**2 145 | # if nearest neighbor 146 | if k == 1: 147 | # if within the max sq distance 148 | if sd <= sqmd: 149 | minsd = min(sds) 150 | mini = np.argmin(sds) 151 | # if less than max neighbors 152 | if minsd == -1.0: 153 | sds[mini] = sd 154 | inds[i,mini] = j 155 | # if max neighbors 156 | else: 157 | # if sq distance is less 158 | # than neighbors max sq distance 159 | maxsd = max(sds) 160 | maxi = np.argmax(sds) 161 | if sd < maxsd: 162 | sds[maxi] = sd 163 | inds[i,maxi] = j 164 | # if distance weighted 165 | elif k == 2: 166 | # all stations indexes 167 | inds[i,:] = np.arange(nstat) 168 | # weight factor 169 | weight = np.exp(-sd/dalpha) 170 | wv[i,j*2] = weight 171 | wv[i,j*2+1] = weight 172 | 173 | # initialize arrays 174 | y = np.zeros(nstat*2) 175 | M = np.zeros((nstat*2,6)) 176 | e = np.zeros((3,3)) 177 | eps = np.zeros((3,3,ncells)) 178 | ome = np.zeros((3,3,ncells)) 179 | pstrain = np.zeros((3,3,ncells)) 180 | rotc = np.zeros((ncells,3)) 181 | 182 | # for each cell 183 | for i in range(ncells): 184 | # if required minimum number of stations 185 | if min(inds[i,:]) >= 0: 186 | # displacements column vector y 187 | # and design matrix M. X1 = North, X2 = East 188 | for j in range(nstat): 189 | ic = inds[i,j] 190 | y[j*2] = disp[ic,1] 191 | y[j*2+1] = disp[ic,0] 192 | M[j*2,:] = [1.,0.,pos[ic,1],pos[ic,0],0.,0.] 193 | M[j*2+1,:] = [0.,1.,0.,0.,pos[ic,1],pos[ic,0]] 194 | # find x using function lscov 195 | # if Delaunay or nearest neighbor 196 | if k == 0 or k == 1: 197 | x = lscov(M,y) 198 | # if distance weighted 199 | elif k == 2: 200 | x = lscov(M,y,wv[i,:]) 201 | # displacement gradient tensor 202 | for j in range(2): 203 | e[j,0] = x[j*2+2] 204 | e[j,1] = x[j*2+3] 205 | # compute strain 206 | eps[:,:,i],ome[:,:,i],pstrain[:,:,i],\ 207 | rotc[i,:],_ = inf_strain(e) 208 | 209 | # variable to plot 210 | # if maximum principal strain 211 | if plotpar == 0: 212 | vp = pstrain[0,0,:] 213 | lcb = "emax" 214 | # if minimum principal strain 215 | elif plotpar == 1: 216 | vp = pstrain[2,0,:] 217 | lcb = "emin" 218 | # if rotation: 219 | # for plane strain, rotation = rotc(3) 220 | elif plotpar == 2: 221 | vp = rotc[:,2]*180/pi 222 | lcb = r"Rotation ($\circ$)" 223 | # if dilatation 224 | elif plotpar == 3: 225 | vp = pstrain[0,0,:]+pstrain[1,0,:]+pstrain[2,0,:] 226 | lcb = "dilatation" 227 | 228 | # patches and colors for cells 229 | patches = [] 230 | colors = [] 231 | 232 | # fill cells patches and colors 233 | # if Delaunay 234 | if k == 0: 235 | for i in range(ncells): 236 | # if minimum number of stations 237 | if min(inds[i,:]) >= 0: 238 | xpyp = [[pos[inds[i,0],0],pos[inds[i,0],1]],\ 239 | [pos[inds[i,1],0],pos[inds[i,1],1]],\ 240 | [pos[inds[i,2],0],pos[inds[i,2],1]]] 241 | # length in km 242 | xpyp = np.divide(xpyp,1e3) 243 | polygon = Polygon(xpyp, True) 244 | patches.append(polygon) 245 | colors.append(vp[i]) 246 | # if nearest neighbor or distance weighted 247 | if k == 1 or k == 2: 248 | count = 0 249 | for i in range(cellsy): 250 | for j in range(cellsx): 251 | # if minimum number of stations 252 | if min(inds[count,:]) >= 0: 253 | xpyp = [[XX[i,j],YY[i,j]],[XX[i,j+1],YY[i,j+1]],\ 254 | [XX[i+1,j+1],YY[i+1,j+1]],[XX[i+1,j],YY[i+1,j]]] 255 | # length in km 256 | xpyp = np.divide(xpyp,1e3) 257 | polygon = Polygon(xpyp, True) 258 | patches.append(polygon) 259 | colors.append(vp[count]) 260 | count += 1 261 | 262 | # collect cells patches 263 | pcoll = PatchCollection(patches) 264 | # cells colors 265 | pcoll.set_array(np.array(colors)) 266 | # color map is blue to red 267 | pcoll.set_cmap("bwr") 268 | # positive values are red, negative are 269 | # blue and zero is white 270 | vmin = min(vp) 271 | vmax = max(vp) 272 | norm=mcolors.TwoSlopeNorm(vmin=vmin,vcenter=0.0,vmax=vmax) 273 | pcoll.set_norm(norm) 274 | 275 | # draw cells 276 | ax.add_collection(pcoll) 277 | 278 | # plot stations 279 | if plotst == 1: 280 | ax.plot(pos[:,0]*1e-3,pos[:,1]*1e-3,"k.",markersize=2) 281 | 282 | # axes 283 | ax.axis("equal") 284 | ax.set_xlabel("x (km)") 285 | ax.set_ylabel("y (km)") 286 | 287 | # color bar with nice ticks 288 | intv = (vmax-vmin)*0.25 289 | ticks=[vmin,vmin+intv,vmin+2*intv,vmin+3*intv,vmax] 290 | lticks = ["{:.2e}".format(ticks[0]),\ 291 | "{:.2e}".format(ticks[1]),"{:.2e}".format(ticks[2]),\ 292 | "{:.2e}".format(ticks[3]),"{:.2e}".format(ticks[4])] 293 | cbar = fig.colorbar(pcoll, label=lcb, ticks=ticks) 294 | cbar.ax.set_yticklabels(lticks) 295 | 296 | return cent, eps, ome, pstrain, rotc -------------------------------------------------------------------------------- /source/functions/hoop.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | def hoop(geom,stress,fig,ax): 5 | """ 6 | hoop computes the hoop and radial stresses 7 | around a circular hole. assuming that 8 | the circle is on a principal plane, 9 | smax (s1) is N-S (theta = 90 or 270), 10 | and smin (s3) is E-W (theta = 0 or 180) 11 | Based on Jaeger et al. (2007) 12 | 13 | USE: shm, srm = hoop(geom,stress,fig,ax) 14 | 15 | geom: A 1 x 2 vector with the number of points 16 | along the radius, and the number of points 17 | around the circle. These values are used 18 | to construct the grid around the circle 19 | stress: A 1 x 3 vector with the value of s1, s3, 20 | and fluid pressure (pf), all in MPa 21 | fig: a figure handle for the plots 22 | ax: a 2 x 2 array of axis handles for the plots 23 | shm, srm: maximum hoop and radial stresses and 24 | their theta orientations 25 | """ 26 | pi = np.pi # pi 27 | 28 | # geometry 29 | M = geom[0] # points along radius 30 | N = geom[1] # points around circle 31 | R1 = 1.0 # radius of hole = 1.0 32 | R2 = R1 * 5 # outer radius = 5.0 33 | nR = np.linspace(R1,R2,M) 34 | nT = np.linspace(0,2*pi,N) 35 | R, T = np.meshgrid(nR,nT) 36 | # convert grid to cartesian coordintes 37 | X = R*np.cos(T) 38 | Y = R*np.sin(T) 39 | m,n = X.shape 40 | 41 | # principal stresses and pore pressure (MPa) 42 | s1 = stress[0] 43 | s3 = stress[1] 44 | pf = stress[2] 45 | 46 | # initialize hoop and radial stresses 47 | sh = np.zeros(X.shape) 48 | sr = np.zeros(X.shape) 49 | 50 | # initialize maximum hoop and radial stresses 51 | shm = np.zeros(2) 52 | srm = np.zeros(2) 53 | 54 | # compute hoop and radial stresses 55 | for i in range(m): 56 | for j in range(n): 57 | # hoop stress 58 | sh[i,j] = (s1+s3)/2*(1+(R1/R[i,j])**2) \ 59 | -(s3-s1)/2*(1+3*(R1/R[i,j])**4) \ 60 | *np.cos(2*T[i,j]) - pf*(R1/R[i,j])**2 61 | # radial stress 62 | sr[i,j] = (s1+s3)/2*(1-(R1/R[i,j])**2) \ 63 | +(s3-s1)/2*(1-4*(R1/R[i,j])**2+3*(R1/R[i,j])**4) \ 64 | *np.cos(2*T[i,j]) + pf*(R1/R[i,j])**2 65 | # maximum hoop stress 66 | if sh[i,j] > shm[0]: 67 | shm[0] = sh[i,j] 68 | shm[1] = T[i,j]*180/pi 69 | # maximum radial stress 70 | if sr[i,j] > srm[0]: 71 | srm[0] = sr[i,j] 72 | srm[1] = T[i,j]*180/pi 73 | 74 | # plot hoop stress 75 | ax[0,0].axis("equal") 76 | ax[0,0].set_frame_on(False) 77 | ax[0,0].get_xaxis().set_visible(False) 78 | ax[0,0].get_yaxis().set_visible(False) 79 | ax[0,0].plot(X[:,0],Y[:,0],"k",linewidth=1.5) 80 | ax[0,0].plot(X[:,n-1],Y[:,n-1],"k",linewidth=1.5) 81 | cbar = ax[0,0].contourf(X, Y, sh, cmap="jet") 82 | cstr = ax[0,0].contour(X,Y,sh, colors = "k") 83 | fig.colorbar(cbar, ax=ax[0,0], label="MPa") 84 | ax[0,0].set_title("Hoop stress",fontweight="bold") 85 | 86 | # plot radial stress 87 | ax[0,1].axis("equal") 88 | ax[0,1].set_frame_on(False) 89 | ax[0,1].get_xaxis().set_visible(False) 90 | ax[0,1].get_yaxis().set_visible(False) 91 | ax[0,1].plot(X[:,0],Y[:,0],"k",linewidth=1.5) 92 | ax[0,1].plot(X[:,n-1],Y[:,n-1],"k",linewidth=1.5) 93 | cbar = ax[0,1].contourf(X, Y, sr, cmap="jet") 94 | cstr = ax[0,1].contour(X,Y,sr, colors = "k") 95 | fig.colorbar(cbar, ax=ax[0,1], label="MPa") 96 | ax[0,1].set_title("Radial stress",fontweight="bold") 97 | 98 | # plot variation of hoop and radial stress along s3 99 | ax[1,0].plot(R[0,:]/R1,sh[0,:],"r.-", label="Hoop") 100 | ax[1,0].plot(R[0,:]/R1,sr[0,:],"b.-", label="Radial") 101 | ax[1,0].grid(True) 102 | ax[1,0].set_xlabel("Normalized radial distance") 103 | ax[1,0].set_ylabel("Stress (MPa)") 104 | ax[1,0].legend(loc="upper right") 105 | ax[1,0].set_title("Stress variation along s3", 106 | fontweight="bold") 107 | 108 | # plot variation of hoop and radial stress around circle 109 | ax[1,1].plot(T[:,0]*180/pi,sh[:,0],"r.-", label="Hoop") 110 | ax[1,1].plot(T[:,0]*180/pi,sr[:,0],"b.-", label="Radial") 111 | ax[1,1].grid(True) 112 | ax[1,1].set_xlim([0, 360]) 113 | ax[1,1].set_xticks([0, 90, 180, 270, 360]) 114 | ax[1,1].set_xlabel("Angle around the hole (deg)") 115 | ax[1,1].set_ylabel("Stress (MPa)") 116 | ax[1,1].legend(loc="upper right") 117 | ax[1,1].set_title("Stress variation around circle", 118 | fontweight="bold") 119 | 120 | return shm, srm -------------------------------------------------------------------------------- /source/functions/inf_strain.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from cart_to_sph import cart_to_sph 4 | from zero_twopi import zero_twopi 5 | 6 | def inf_strain(e): 7 | """ 8 | inf_strain computes infinitesimal strain from an input 9 | displacement gradient tensor 10 | 11 | USE: eps,ome,pstrain,rotc,rot = inf_strain(e) 12 | 13 | e = 3 x 3 displacement gradient tensor 14 | eps = 3 x 3 strain tensor 15 | ome = 3 x 3 rotation tensor 16 | pstrain = 3 x 3 matrix with magnitude (column 1), trend 17 | (column 2) and plunge (column 3) of maximum 18 | (row 1), intermediate (row 2),and minimum 19 | (row 3) principal strains 20 | rotc = 1 x 3 vector with rotation components 21 | rot = 1 x 3 vector with rotation magnitude and trend 22 | and plunge of rotation axis 23 | 24 | NOTE: Output trends and plunges of principal strains 25 | and rotation axes are in radians 26 | 27 | Python function translated from the Matlab function 28 | InfStrain in Allmendinger et al. (2012) 29 | """ 30 | # initialize variables 31 | eps = np.zeros((3,3)) 32 | ome = np.zeros((3,3)) 33 | pstrain = np.zeros((3,3)) 34 | rotc = np.zeros(3) 35 | rot = np.zeros(3) 36 | 37 | # compute strain and rotation tensors 38 | for i in range(3): 39 | for j in range(3): 40 | eps[i,j]=0.5*(e[i,j]+e[j,i]) 41 | ome[i,j]=0.5*(e[i,j]-e[j,i]) 42 | 43 | # compute principal strains and orientations. 44 | # Here we use the function eigh. D is a vector 45 | # of eigenvalues (i.e. principal strains), and V is a 46 | # full matrix whose columns are the corresponding 47 | # eigenvectors (i.e. principal strain directions) 48 | D,V = np.linalg.eigh(eps) 49 | 50 | # maximum principal strain 51 | pstrain[0,0] = D[2] 52 | pstrain[0,1],pstrain[0,2] = cart_to_sph(V[0,2],V[1,2],V[2,2]) 53 | # intermediate principal strain 54 | pstrain[1,0] = D[1] 55 | pstrain[1,1],pstrain[1,2] = cart_to_sph(V[0,1],V[1,1],V[2,1]) 56 | # minimum principal strain 57 | pstrain[2,0] = D[0] 58 | pstrain[2,1],pstrain[2,2] = cart_to_sph(V[0,0],V[1,0],V[2,0]) 59 | 60 | # calculate rotation components 61 | rotc[0]=(ome[1,2]-ome[2,1])*-0.5 62 | rotc[1]=(-ome[0,2]+ome[2,0])*-0.5 63 | rotc[2]=(ome[0,1]-ome[1,0])*-0.5 64 | 65 | # compute rotation magnitude 66 | rot[0] = np.sqrt(rotc[0]**2+rotc[1]**2+rotc[2]**2) 67 | # compute trend and plunge of rotation axis 68 | rot[1],rot[2] = cart_to_sph(rotc[0]/rot[0],rotc[1]/rot[0],rotc[2]/rot[0]) 69 | # if plunge is negative 70 | if rot[2] < 0: 71 | rot[1] = zero_twopi(rot[1]+np.pi) 72 | rot[2] *= -1 73 | rot[0] *= -1 74 | 75 | return eps, ome, pstrain, rotc, rot -------------------------------------------------------------------------------- /source/functions/lscov.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def lscov(A, B, w=None): 4 | """Least-squares solution in presence of known covariance 5 | 6 | :math:`A \\cdot x = B`, that is, :math:`x` minimizes 7 | :math:`(B - A \\cdot x)^T \\cdot \\text{diag}(w) \\cdot (B - A \\cdot x)`. 8 | The matrix :math:`w` typically contains either counts or inverse 9 | variances. 10 | 11 | Parameters 12 | ---------- 13 | A: matrix or 2d ndarray 14 | input matrix 15 | B: vector or 1d ndarray 16 | input vector 17 | 18 | Notes 19 | -------- 20 | https://de.mathworks.com/help/matlab/ref/lscov.html 21 | Code written by Paul Muller in connection with the ggf package 22 | """ 23 | # https://stackoverflow.com/questions/27128688/how-to-use-least-squares-with-weight-matrix-in-python 24 | # https://de.mathworks.com/help/matlab/ref/lscov.html 25 | if w is None: 26 | Aw = A.copy() 27 | Bw = B.T.copy() 28 | else: 29 | W = np.sqrt(np.diag(np.array(w).flatten())) 30 | Aw = np.dot(W, A) 31 | Bw = np.dot(B.T, W) 32 | 33 | # set rcond=1e-10 to prevent diverging odd indices in x 34 | # (problem specific to ggf/stress computation) 35 | x, residuals, rank, s = np.linalg.lstsq(Aw, Bw.T, rcond=1e-10) 36 | return np.array(x).flatten() -------------------------------------------------------------------------------- /source/functions/mohr_circle_stress.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from principal_stress import principal_stress 4 | from sph_to_cart import sph_to_cart 5 | from cart_to_sph import cart_to_sph 6 | from pole import pole_from_plane 7 | 8 | def mohr_circle_stress(stress,tx1,px1,tx3,planes,ax): 9 | """ 10 | Given the stress tensor in a X1X2X3 coordinate system, 11 | and a group of n planes, mohr_circle_stress draws the Mohr 12 | Circle for stress (including the planes). It 13 | also returns the normal and max. shear tractions on the 14 | planes and their orientations 15 | 16 | ns,ons = mohr_circle_stress(stress,tx1,px1,tx3,planes,ax) 17 | 18 | stress = 3 x 3 stress tensor 19 | tx1 = trend of X1 20 | px1 = plunge of X1 21 | tx3 = trend of X3 22 | planes = n x 2 vector with strike and dip of planes 23 | ax = handle to axes where the Mohr Circle will be drawn 24 | ns = n x 2 vector with the normal and max. shear 25 | tractions on the planes 26 | ons = n x 4 vector with the trend and plunge of the 27 | normal traction (columns 1 and 2), and the 28 | max. shear traction (columns 3 and 4) 29 | 30 | NOTE = Planes orientation follows the right hand rule 31 | Input and output angles are in radians 32 | """ 33 | # tolerance for near zero values 34 | tol = 1e-6 35 | 36 | # compute principal stresses and their orientations 37 | pstress, dcp = principal_stress(stress,tx1,px1,tx3) 38 | 39 | # update stress tensor to principal stresses 40 | stress = np.zeros((3,3)) 41 | for i in range(3): 42 | stress[i,i] = pstress[i,0] 43 | 44 | # draw sigma1-sigma3 circle 45 | c = (stress[0,0] + stress[2,2])/2.0 46 | r = (stress[0,0] - stress[2,2])/2.0 47 | th =np.arange(0,2*np.pi,np.pi/50) 48 | costh = np.cos(th) 49 | sinth = np.sin(th) 50 | x = r * costh + c 51 | y = r * sinth 52 | ax.plot(x,y,"k-") 53 | 54 | # draw sigma1-sigma2 circle 55 | c = (stress[0,0] + stress[1,1])/2.0 56 | r = (stress[0,0] - stress[1,1])/2.0 57 | x = r * costh + c 58 | y = r * sinth 59 | ax.plot(x,y,"k-") 60 | 61 | # draw sigma2-sigma3 circle 62 | c = (stress[1,1] + stress[2,2])/2.0 63 | r = (stress[1,1] - stress[2,2])/2.0 64 | x = r * costh + c 65 | y = r * sinth 66 | ax.plot(x,y,"k-") 67 | 68 | 69 | # initialize pole to plane 70 | p = np.zeros(3) 71 | 72 | # initialize vectors with normal and 73 | # max. shear tractions 74 | ns = np.zeros((planes.shape[0],2)) 75 | ons = np.zeros((planes.shape[0],4)) 76 | 77 | # compute normal and max. shear tractions 78 | for i in range(planes.shape[0]): 79 | 80 | # calculate direction cosines of pole to plane 81 | trd, plg = pole_from_plane(planes[i,0],planes[i,1]) 82 | p[0],p[1],p[2] = sph_to_cart(trd, plg) 83 | 84 | # trend and plunge of pole = dir. normal traction 85 | ons[i,0],ons[i,1] = trd, plg 86 | 87 | # transform pole to principal stress coordinates 88 | pt = np.zeros(3) 89 | for j in range(3): 90 | for k in range(3): 91 | pt[j] = dcp[j,k]*p[k] + pt[j] 92 | 93 | # calculate the tractions in principal stress 94 | # coordinates 95 | t = np.zeros(3) 96 | for j in range(3): 97 | for k in range(3): 98 | t[j] = stress[j,k]*pt[k] + t[j] 99 | 100 | # find the b and s axes 101 | b = np.cross(t,pt) 102 | s = np.cross(pt,b) 103 | b = b/np.linalg.norm(b) 104 | s = s/np.linalg.norm(s) 105 | 106 | # transformation matrix from principal 107 | # stress coordinates to plane coordinates 108 | a = np.zeros((3,3)) 109 | a[0,:] = pt 110 | a[1,:] = b 111 | a[2,:] = s 112 | 113 | # normal and max. shear tractions 114 | ns[i,0] = stress[0,0]*a[0,0]*a[0,0] + stress[1,1]\ 115 | *a[0,1]*a[0,1]+ stress[2,2]*a[0,2]*a[0,2] 116 | ns[i,1] = stress[0,0]*a[0,0]*a[2,0] + stress[1,1]\ 117 | *a[0,1]*a[2,1]+ stress[2,2]*a[0,2]*a[2,2] 118 | 119 | # calculate direction cosines of max. 120 | # shear traction with respect to NED 121 | ds = np.zeros(3) 122 | for j in range(3): 123 | for k in range(3): 124 | ds[j] = dcp[k,j]*s[k] + ds[j] 125 | 126 | # trend and plunge of max. shear traction 127 | ons[i,2],ons[i,3] = cart_to_sph(ds[0],ds[1],ds[2]) 128 | 129 | # cross product of pole and max. shear traction 130 | ps = np.cross(p,ds) 131 | 132 | # make clockwise shear traction negative 133 | if np.abs(ps[2]) < tol: # Dip slip 134 | if ds[2] > 0.0: # Normal slip 135 | if pt[0]*pt[2] < 0.0: 136 | ns[i,1] *= -1.0 137 | else: # Reverse slip 138 | if pt[0]*pt[2] >= 0.0: 139 | ns[i,1] *= -1.0 140 | else: # Oblique slip 141 | if ps[2] < 0.0: 142 | ns[i,1] *= -1.0 143 | 144 | # plot planes 145 | ax.plot(ns[:,0],ns[:,1],"ks") 146 | 147 | # make axes equal and plot grid 148 | ax.axis ("equal") 149 | ax.grid() 150 | 151 | # move x-axis to center and y-axis to origin 152 | ax.spines["bottom"].set_position("center") 153 | ax.spines["left"].set_position("zero") 154 | 155 | # eliminate upper and right axes 156 | ax.spines["right"].set_color("none") 157 | ax.spines["top"].set_color("none") 158 | 159 | # show ticks in the left and lower axes only 160 | ax.xaxis.set_ticks_position("bottom") 161 | ax.yaxis.set_ticks_position("left") 162 | 163 | # add labels at end of axes 164 | ax.set_xlabel(r"$\sigma$",x=1) 165 | ax.set_ylabel(r"$\tau$",y=1,rotation=0) 166 | 167 | return ns, ons -------------------------------------------------------------------------------- /source/functions/outcrop_trace.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def outcrop_trace(stk,dip,p1,XG,YG,ZG): 4 | """ 5 | outcrop_trace estimates the outcrop trace of a plane, 6 | given the strike (stk) and dip (dip) of the plane, 7 | the ENU coordinates of a point (p1) where the plane 8 | outcrops, and a DEM of the terrain as a regular grid 9 | of points with E (XG), N (YG) and U (ZG) coordinates. 10 | 11 | After using this function, to draw the outcrop trace 12 | of the plane, you just need to draw the contour 0 of 13 | DG on the grid XG,YG,DG 14 | 15 | NOTE: stk and dip should be input in radians 16 | p1 must be an array 17 | XG and YG arrays should be constructed using 18 | the Numpy function meshgrid 19 | """ 20 | # make the transformation matrix from ENU coordinates to 21 | # SDP coordinates. We just need the third row of this 22 | # matrix 23 | a = np.zeros((3,3)) 24 | a[2,0] = -np.cos(stk)*np.sin(dip) 25 | a[2,1] = np.sin(stk)*np.sin(dip) 26 | a[2,2] = -np.cos(dip) 27 | 28 | # initialize DG 29 | n, m = XG.shape 30 | DG = np.zeros((n,m)) 31 | 32 | # estimate the P coordinate of the outcrop point p1 33 | P1 = a[2,0]*p1[0] + a[2,1]*p1[1] + a[2,2]*p1[2] 34 | 35 | # estimate the P coordinate at each point of the DEM 36 | # grid and subtract P1 37 | for i in range(n): 38 | for j in range(m): 39 | DG[i,j] = P1 - (a[2,0]*XG[i,j] 40 | + a[2,1]*YG[i,j] + a[2,2]*ZG[i,j]) 41 | 42 | return DG -------------------------------------------------------------------------------- /source/functions/pole.py: -------------------------------------------------------------------------------- 1 | import math 2 | from zero_twopi import zero_twopi 3 | 4 | def pole_from_plane(stk,dip): 5 | """ 6 | pole_from_plane returns the trend (trd) and 7 | plunge (plg) of a pole, given the strike and 8 | dip of the plane 9 | 10 | NOTE: Input/Output angles are in radians. 11 | Input stk and dip is in RHR format 12 | """ 13 | # some constants 14 | east = math.pi/2 15 | 16 | # pole from plane 17 | trd = zero_twopi (stk - east) 18 | plg = east - dip 19 | 20 | return trd, plg 21 | 22 | def plane_from_pole(trd,plg): 23 | """ 24 | plane_from_pole returns the strike and dip 25 | of a plane, given the trend (trd) and 26 | plunge (plg) of its pole 27 | 28 | NOTE: Input/Output angles are in radians. 29 | Output stk and dip is in RHR format 30 | """ 31 | # some constants 32 | pi = math.pi 33 | east = pi/2 34 | 35 | # unusual case of pole pointing upwards 36 | if plg < 0.0: 37 | trd += pi 38 | plg *= -1.0 39 | 40 | # calculate plane given its pole 41 | stk = zero_twopi(trd + east) 42 | dip = east - plg 43 | 44 | return stk, dip -------------------------------------------------------------------------------- /source/functions/principal_stress.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from dircos_axes import dircos_axes 4 | from cart_to_sph import cart_to_sph 5 | 6 | def principal_stress(stress,tx1,px1,tx3): 7 | """ 8 | Given the stress tensor in a X1X2X3 coordinate system 9 | principal_stress calculates the principal stresses and 10 | their orientations (trend and plunge) 11 | 12 | USE: pstress,dcp = principal_stress(stress,tx1,px1,tx3) 13 | 14 | stress = Symmetric 3 x 3 stress tensor 15 | tx1 = trend of X1 16 | px1 = plunge of X1 17 | tx3 = trend of X3 18 | pstress = 3 x 3 matrix containing the magnitude 19 | (column 1), trend (column 2), and plunge 20 | (column 3) of the maximum (row 1), 21 | intermediate (row 2), and minimum (row 3) 22 | principal stresses 23 | dcp = 3 x 3 matrix with direction cosines of the 24 | principal stress directions: Max. (row 1), 25 | Int. (row 2), and Min. (row 3) with respect 26 | to North-East-Down 27 | 28 | NOTE: Input/Output angles are in radians 29 | 30 | Python function translated from the Matlab function 31 | PrincipalStress in Allmendinger et al. (2012) 32 | """ 33 | # compute direction cosines of X1X2X3 34 | dc = dircos_axes(tx1,px1,tx3) 35 | 36 | # initialize pstress 37 | pstress = np.zeros((3,3)) 38 | 39 | # calculate the eigenvalues and eigenvectors 40 | # of the stress tensor 41 | D, V = np.linalg.eigh(stress) 42 | 43 | # fill principal stress magnitudes 44 | pstress[0,0] = D[2] # Maximum principal stress 45 | pstress[1,0] = D[1] # Interm. principal stress 46 | pstress[2,0] = D[0] # Minimum principal stress 47 | 48 | # the direction cosines of the principal stresses are 49 | # with respect to the X1X2X3 stress coordinate system, 50 | # so they need to be transformed to the NED coordinate 51 | # system 52 | tv = np.zeros((3,3)) 53 | for i in range(3): 54 | for j in range(3): 55 | for k in range(3): 56 | tv[j,i] = dc[k,j]*V[k,i] + tv[j,i] 57 | 58 | 59 | # initialize dcp 60 | dcp = np.zeros((3,3)) 61 | 62 | # direction cosines of principal stresses 63 | for i in range(3): 64 | for j in range(3): 65 | dcp[i,j] = tv[j,2-i] 66 | # avoid precision issues 67 | # make sure the principal axes are unit vectors 68 | dcp[i,:] = dcp[i,:]/np.linalg.norm(dcp[i,:]) 69 | 70 | # trend and plunge of principal stresses 71 | for i in range(3): 72 | pstress[i,1],pstress[i,2] = cart_to_sph(dcp[i,0], 73 | dcp[i,1],dcp[i,2]) 74 | 75 | return pstress, dcp -------------------------------------------------------------------------------- /source/functions/pt_axes.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | from cart_to_sph import cart_to_sph 5 | from zero_twopi import zero_twopi 6 | from sph_to_cart import sph_to_cart 7 | from stereonet import stereonet 8 | from great_circle import great_circle 9 | from st_coord_line import st_coord_line 10 | from pole import pole_from_plane 11 | 12 | def pt_axes(fault,slip,sense, fpsv,ax): 13 | """ 14 | pt_axes computes the P and T axes from the orientation 15 | of several fault planes and their slip vectors. Results 16 | are plotted in an equal area stereonet 17 | 18 | USE: P,T,senseC = pt_axes(fault,slip,sense,ax) 19 | 20 | fault = nfaults x 2 vector with strikes and dips of 21 | faults 22 | slip = nfaults x 2 vector with trend and plunge of 23 | slip vectors 24 | sense = nfaults x 1 vector with sense of faults 25 | ax = axis handle for the plot 26 | fpsv = A flag to tell wether the fault plane and 27 | slip vector are plotted (1) or not 28 | P = nfaults x 2 vector with trend and plunge of P axes 29 | T = nfaults x 2 vector with trend and plunge of T axes 30 | senseC = nfaults x 1 vector with corrected sense of slip 31 | 32 | NOTE: Input/Output angles are in radians 33 | 34 | Python function based on the Matlab function 35 | PTAxes in Allmendinger et al. (2012) 36 | """ 37 | pi = np.pi 38 | 39 | # initialize some vectors 40 | p = np.zeros(3) 41 | u = np.zeros(3) 42 | eps = np.zeros((3,3)) 43 | P = np.zeros(fault.shape) 44 | T = np.zeros(fault.shape) 45 | senseC = sense 46 | 47 | # for all faults 48 | for i in range(fault.shape[0]): 49 | # Direction cosines of pole to fault and slip vector 50 | trd, plg = pole_from_plane(fault[i,0],fault[i,1]) 51 | p[0],p[1],p[2] = sph_to_cart(trd, plg) 52 | u[0],u[1],u[2] = sph_to_cart(slip[i,0],slip[i,1]) 53 | # compute u(i)*p(j) + u(j)*p(i) 54 | for j in range(3): 55 | for k in range(3): 56 | eps[j,k]=u[j]*p[k]+u[k]*p[j] 57 | # compute orientations of principal axes of strain 58 | # here we use the function eigh 59 | _,V = np.linalg.eigh(eps) 60 | # P orientation 61 | P[i,0],P[i,1] = cart_to_sph(V[0,2],V[1,2],V[2,2]) 62 | if P[i,1] < 0: 63 | P[i,0] = zero_twopi(P[i,0]+pi) 64 | P[i,1] *= -1 65 | # T orientation 66 | T[i,0],T[i,1] = cart_to_sph(V[0,0],V[1,0],V[2,0]) 67 | if T[i,1] < 0.0: 68 | T[i,0] = zero_twopi(T[i,0]+pi) 69 | T[i,1] *= -1 70 | # determine 3rd component of pole cross product slip 71 | cross = p[0] * u[1] - p[1] * u[0] 72 | # use cross and first character in sense to 73 | # determine if kinematic axes should be switched 74 | s2 = "p" 75 | if sense[i][0] == "T" or sense[i][0] == "t": 76 | s2 = "Y" 77 | if (sense[i][0]=="R" or sense[i][0]=="r") and cross>0.0: 78 | s2 = "Y" 79 | if (sense[i][0]=="L" or sense[i][0]=="l") and cross<0.0: 80 | s2 = "Y" 81 | if s2 == "Y": 82 | temp1 = P[i,0] 83 | temp2 = P[i,1] 84 | P[i,0] = T[i,0] 85 | P[i,1] = T[i,1] 86 | T[i,0] = temp1 87 | T[i,1] = temp2 88 | if cross < 0.0: 89 | senseC[i] = "TL" 90 | if cross > 0.0: 91 | senseC[i] = "TR" 92 | else: 93 | if cross < 0.0: 94 | senseC[i] = "NR" 95 | if cross > 0.0: 96 | senseC[i] = "NL" 97 | 98 | # plot in equal area stereonet 99 | stereonet(0,90*pi/180,10*pi/180,1,ax) 100 | # plot P and T axes 101 | for i in range(fault.shape[0]): 102 | if fpsv == 1: 103 | # plot fault 104 | path = great_circle(fault[i,0],fault[i,1],1) 105 | ax.plot(path[:,0],path[:,1],"k") 106 | # plot slip vector (black circle) 107 | xp,yp = st_coord_line(slip[i,0],slip[i,1],1) 108 | ax.plot(xp,yp,"ko","MarkerFaceColor","k") 109 | # plot P axis (blue circle) 110 | xp,yp = st_coord_line(P[i,0],P[i,1],1) 111 | ax.plot(xp,yp,"bo","MarkerFaceColor","b") 112 | # plot T axis (red circle) 113 | xp,yp = st_coord_line(T[i,0],T[i,1],1) 114 | ax.plot(xp,yp,"ro","MarkerFaceColor","r") 115 | 116 | return P, T, senseC -------------------------------------------------------------------------------- /source/functions/pure_shear.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | def pure_shear(pts,st1,ninc,ax): 5 | """ 6 | pure_shear computes and plots displacement paths and 7 | progressive finite strain history for pure shear with 8 | maximum stretching parallel to the X1 axis 9 | 10 | USE: paths,pfs = pure_shear(pts,st1,ninc,ax) 11 | 12 | pts: npoints x 2 matrix with X1 and X3 coord. of points 13 | st1 = Maximum principal stretch 14 | ninc = number of strain increments 15 | ax = an array of two axis handles for the plots 16 | paths = displacement paths of points 17 | pfs = progressive finite strain history. column 1 = 18 | orientation of maximum stretch with respect to X1 19 | in degrees, column 2 = maximum stretch magnitude 20 | 21 | NOTE: Intermediate principal stretch is 1.0 (Plane 22 | strain). Output orientations are in radians 23 | 24 | Python function based on the Matlab function 25 | PureShear in Allmendinger et al. (2012) 26 | """ 27 | # compute minimum principal stretch and incr. stretches 28 | st1inc=st1**(1.0/ninc) 29 | st3=1.0/st1 30 | st3inc=st3**(1.0/ninc) 31 | 32 | # initialize displacement paths 33 | npts = pts.shape[0] # Number of points 34 | paths = np.zeros((ninc+1,npts,2)) 35 | paths[0,:,:] = pts # Initial points of paths 36 | 37 | # calculate incr. deformation gradient tensor 38 | F = np.array([[st1inc, 0.0], [0.0, st3inc]]) 39 | 40 | # compute displacement paths 41 | for i in range(npts): # for all points 42 | for j in range(ninc+1): # for all strain increments 43 | for k in range(2): 44 | for L in range(2): 45 | paths[j,i,k] = F[k,L]*paths[j-1,i,L] + paths[j,i,k] 46 | # plot displacement path of point 47 | ax[0].plot(paths[:,i,0], paths[:,i,1], "k.-") 48 | 49 | # plot initial polygon 50 | inpol = np.zeros((npts+1,2)) 51 | inpol[0:npts,]=paths[0,0:npts,:] 52 | inpol[npts,] = inpol[0,] 53 | ax[0].plot(inpol[:,0],inpol[:,1],"b-") 54 | # plot final polygon 55 | finpol = np.zeros((npts+1,2)) 56 | finpol[0:npts,]=paths[ninc,0:npts,:] 57 | finpol[npts,] = finpol[0,] 58 | ax[0].plot(finpol[:,0],finpol[:,1],"r-") 59 | 60 | # set axes 61 | ax[0].set_xlabel(r"$\mathbf{X_1}$") 62 | ax[0].set_ylabel(r"$\mathbf{X_3}$") 63 | ax[0].grid() 64 | ax[0].axis("equal") 65 | 66 | # initalize progressive finite strain history 67 | pfs = np.zeros((ninc+1,2)) 68 | pfs[0,:] = [0, 1] #Initial state 69 | 70 | # calculate progressive finite strain history 71 | for i in range(1,ninc+1): 72 | # determine the finite deformation gradient tensor 73 | finF = np.linalg.matrix_power(F, i) 74 | # determine Green deformation tensor 75 | G = np.dot(finF,finF.conj().transpose()) 76 | # stretch magnitude and orientation: Maximum 77 | # eigenvalue and their corresponding eigenvectors 78 | # of Green deformation tensor 79 | D, V = np.linalg.eigh(G) 80 | pfs[i,0] = np.arctan(V[1,1]/V[0,1]) 81 | pfs[i,1] = np.sqrt(D[1]) 82 | 83 | # plot progressive finite strain history 84 | ax[1].plot(pfs[:,0]*180/np.pi,pfs[:,1],"k.-") 85 | ax[1].set_xlabel(r"$\Theta\;(\circ)$") 86 | ax[1].set_ylabel("Maximum finite stretch") 87 | ax[1].set_xlim(-90,90) 88 | ax[1].set_ylim(1,max(pfs[:,1])+0.5) 89 | ax[1].grid() 90 | 91 | return paths, pfs -------------------------------------------------------------------------------- /source/functions/rotate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from sph_to_cart import sph_to_cart 3 | from cart_to_sph import cart_to_sph 4 | 5 | def rotate(rtrd,rplg,rot,trd,plg,ans0): 6 | """ 7 | rotate rotates a line by performing a coordinate 8 | transformation 9 | 10 | rtrd = trend of rotation axis 11 | rplg = plunge of rotation axis 12 | rot = magnitude of rotation 13 | trd = trend of the line to be rotated 14 | plg = plunge of the line to be rotated 15 | ans0 = A character indicating whether the line 16 | to be rotated is an axis (ans0 = "a") or a 17 | vector (ans0 = "v") 18 | trdr and plgr are the trend and plunge of the 19 | rotated line 20 | 21 | NOTE: All angles are in radians 22 | 23 | Python function translated from the Matlab function 24 | Rotate in Allmendinger et al. (2012) 25 | """ 26 | # allocate some arrays 27 | a = np.zeros((3,3)) #Transformation matrix 28 | raxis = np.zeros(3) #Dir. cosines of rotation axis 29 | line = np.zeros(3) #Dir. cosines of line to be rotated 30 | liner = np.zeros(3) #Dir. cosines of rotated line 31 | 32 | # convert rotation axis to direction cosines 33 | raxis[0] , raxis[1], raxis[2] = sph_to_cart(rtrd,rplg) 34 | 35 | # calculate the transformation matrix a for the rotation 36 | x = 1.0 - np.cos(rot) 37 | sinrot = np.sin(rot) 38 | cosrot = np.cos(rot) 39 | a[0,0] = cosrot + raxis[0]*raxis[0]*x 40 | a[0,1] = -raxis[2]*sinrot + raxis[0]*raxis[1]*x 41 | a[0,2] = raxis[1]*sinrot + raxis[0]*raxis[2]*x 42 | a[1,0] = raxis[2]*sinrot + raxis[1]*raxis[0]*x 43 | a[1,1] = cosrot + raxis[1]*raxis[1]*x 44 | a[1,2] = -raxis[0]*sinrot + raxis[1]*raxis[2]*x 45 | a[2,0] = -raxis[1]*sinrot + raxis[2]*raxis[0]*x 46 | a[2,1] = raxis[0]*sinrot + raxis[2]*raxis[1]*x 47 | a[2,2] = cosrot + raxis[2]*raxis[2]*x 48 | 49 | # convert trend and plunge of line to be rotated into 50 | # direction cosines 51 | line[0] , line[1], line[2] = sph_to_cart(trd,plg) 52 | 53 | # perform the coordinate transformation 54 | for i in range(3): 55 | for j in range(3): 56 | liner[i] = a[i,j]*line[j] + liner[i] 57 | 58 | # make sure the rotated line is a unit vector 59 | norm = np.linalg.norm(liner) 60 | liner = liner/norm 61 | 62 | # convert to lower hemisphere projection if axis 63 | if liner[2] < 0.0 and ans0 == 'a': 64 | liner *= -1.0 65 | 66 | # convert from direction cosines back to trend and plunge 67 | trdr , plgr = cart_to_sph(liner[0], liner[1], liner[2]) 68 | 69 | return trdr, plgr -------------------------------------------------------------------------------- /source/functions/shear_on_plane.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from principal_stress import principal_stress 3 | from sph_to_cart import sph_to_cart 4 | from cart_to_sph import cart_to_sph 5 | from pole import pole_from_plane 6 | 7 | def shear_on_plane(stress,tx1,px1,tx3,stk,dip): 8 | """ 9 | shear_on_plane calculates the direction and magnitudes of 10 | the normal and maximum shear tractions on an arbitrarily 11 | oriented plane 12 | 13 | tt,dctt,srat=shear_on_plane(stress,tx1,px1,tx3,stk,dip) 14 | 15 | stress = 3 x 3 stress tensor 16 | tx1 = trend of X1 17 | px1 = plunge of X1 18 | tx3 = trend of X3 19 | stk = strike of plane 20 | dip = dip of plane 21 | tt = 3 x 3 matrix with the magnitude (column 1), 22 | trend (column 2) and plunge (column 3) of the: 23 | normal traction on the plane (row 1), zero shear 24 | traction (row 2), and max. shear traction (row 3) 25 | dctt = 3 x 3 matrix with the direction cosines of unit 26 | vectors parallel to: normal traction on the plane 27 | (row 1), zero shear traction (row 2), and maximum 28 | shear traction (row 3) with respect to NED 29 | srat = stress ratio 30 | 31 | NOTE = Input stress tensor does not need to be along 32 | principal stress directions 33 | Plane orientation follows the right hand rule 34 | Input/Output angles are in radians 35 | 36 | Python function translated from the Matlab function 37 | shear_on_plane in Allmendinger et al. (2012) 38 | """ 39 | # compute principal stresses and their orientations 40 | pstress, dcp = principal_stress(stress,tx1,px1,tx3) 41 | 42 | # update stress tensor to principal stress directions 43 | stress = np.zeros((3,3)) 44 | for i in range(3): 45 | stress[i,i] = pstress[i,0] 46 | 47 | # calculate stress ratio 48 | srat = (stress[1,1]-stress[0,0])/(stress[2,2]-stress[0,0]) 49 | 50 | # calculate direction cosines of pole to plane 51 | trd, plg = pole_from_plane(stk,dip) 52 | p = np.zeros(3) 53 | p[0], p[1], p[2] = sph_to_cart(trd,plg) 54 | 55 | # transform pole to plane to principal stress coordinates 56 | pt = np.zeros(3) 57 | for i in range(3): 58 | for j in range(3): 59 | pt[i] = dcp[i,j]*p[j] + pt[i] 60 | 61 | # calculate the tractions in principal stress coordinates 62 | t = np.zeros(3) 63 | # compute tractions using Cauchy's law 64 | for i in range(3): 65 | for j in range(3): 66 | t[i] = stress[i,j]*pt[j] + t[i] 67 | 68 | # find the b axis by the cross product of t and pt 69 | b = np.cross(t,pt) 70 | 71 | # find the max. shear traction orientation 72 | # by the cross product of pt and b 73 | s = np.cross(pt,b) 74 | 75 | # convert b and s to unit vectors 76 | b = b/np.linalg.norm(b) 77 | s = s/np.linalg.norm(s) 78 | 79 | # now we can write the transformation matrix from 80 | # principal stress coordinates to plane coordinates 81 | a = np.zeros((3,3)) 82 | a[0,:] = pt 83 | a[1,:] = b 84 | a[2,:] = s 85 | 86 | # transform stress from principal to plane coordinates 87 | # do it only for the first row since we are just 88 | # interested in the plane: sigma'11 = normal traction, 89 | # sigma'12 = zero, and sigma'13 = max. shear traction 90 | tt = np.zeros((3,3)) 91 | for i in range(3): 92 | tt[i,0] = stress[0,0]*a[0,0]*a[i,0] \ 93 | + stress[1,1]*a[0,1]*a[i,1] + stress[2,2]*a[0,2]*a[i,2] 94 | 95 | # transform normal traction, zero shear 96 | # and max. shear traction to NED coords 97 | dctt = np.zeros((3,3)) 98 | for i in range(3): 99 | for j in range(3): 100 | dctt[0,i] = dcp[j,i]*pt[j] + dctt[0,i] 101 | dctt[1,i] = dcp[j,i]*b[j] + dctt[1,i] 102 | dctt[2,i] = dcp[j,i]*s[j] + dctt[2,i] 103 | 104 | # compute trend and plunge of normal traction, 105 | # zero shear, and max. shear traction on plane 106 | for i in range(3): 107 | tt[i,1],tt[i,2] = cart_to_sph(dctt[i,0], 108 | dctt[i,1],dctt[i,2]) 109 | 110 | return tt, dctt, srat -------------------------------------------------------------------------------- /source/functions/simple_shear.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | def simple_shear(pts,gamma,ninc,ax): 5 | """ 6 | simple_shear computes and plots displacement paths and 7 | progressive finite strain history for simple shear 8 | parallel to the X1 axis 9 | 10 | USE: paths,pfs = simple_shear(pts,gamma,ninc,ax) 11 | 12 | pts: npoints x 2 matrix with X1 and X3 coord. of points 13 | gamma = Engineering shear strain 14 | ninc = number of strain increments 15 | ax = an array of two axis handles for the plots 16 | paths = displacement paths of points 17 | pfs = progressive finite strain history. column 1 = 18 | orientation of maximum stretch with respect to X1 19 | in degrees, column 2 = maximum stretch magnitude 20 | 21 | NOTE: Intermediate principal stretch is 1.0 (Plane 22 | strain). Output orientations are in radians 23 | 24 | Python function based on the Matlab function 25 | SimpleShear in Allmendinger et al. (2012) 26 | """ 27 | # incremental engineering shear strain 28 | gammainc = gamma/ninc 29 | 30 | # initialize displacement paths 31 | npts = pts.shape[0] # Number of points 32 | paths = np.zeros((ninc+1,npts,2)) 33 | paths[0,:,:] = pts # Initial points of paths 34 | 35 | # calculate incr. deformation gradient tensor Eq. 8.44 36 | F = np.array([[1.0, gammainc],[0.0, 1.0]]) 37 | 38 | # compute displacement paths 39 | for i in range(npts): # for all points 40 | for j in range(ninc+1): # for all strain increments 41 | for k in range(2): 42 | for L in range(2): 43 | paths[j,i,k] = F[k,L]*paths[j-1,i,L] + paths[j,i,k] 44 | # plot displacement path of point 45 | ax[0].plot(paths[:,i,0], paths[:,i,1], "k.-") 46 | 47 | # plot initial polygon 48 | inpol = np.zeros((npts+1,2)) 49 | inpol[0:npts,]=paths[0,0:npts,:] 50 | inpol[npts,] = inpol[0,] 51 | ax[0].plot(inpol[:,0],inpol[:,1],"b-") 52 | # plot final polygon 53 | finpol = np.zeros((npts+1,2)) 54 | finpol[0:npts,]=paths[ninc,0:npts,:] 55 | finpol[npts,] = finpol[0,] 56 | ax[0].plot(finpol[:,0],finpol[:,1],"r-") 57 | 58 | # set axes 59 | ax[0].set_xlabel(r"$\mathbf{X_1}$") 60 | ax[0].set_ylabel(r"$\mathbf{X_3}$") 61 | ax[0].grid() 62 | ax[0].axis("equal") 63 | 64 | # initalize progressive finite strain history 65 | pfs = np.zeros((ninc+1,2)) 66 | # in. state: Max. extension is at 45 deg from shear zone 67 | pfs[0,:] = [np.pi/4.0, 1.0] 68 | 69 | # calculate progressive finite strain history 70 | for i in range(1,ninc+1): 71 | # determine the finite deformation gradient tensor 72 | finF = np.linalg.matrix_power(F, i) 73 | # determine Green deformation tensor 74 | G = np.dot(finF,finF.conj().transpose()) 75 | # stretch magnitude and orientation: Maximum 76 | # eigenvalue and their corresponding eigenvectors 77 | # of Green deformation tensor 78 | D, V = np.linalg.eigh(G) 79 | pfs[i,0] = np.arctan(V[1,1]/V[0,1]) 80 | pfs[i,1] = np.sqrt(D[1]) 81 | 82 | # plot progressive finite strain history 83 | ax[1].plot(pfs[:,0]*180/np.pi,pfs[:,1],"k.-") 84 | ax[1].set_xlabel(r"$\Theta\;(\circ)$") 85 | ax[1].set_ylabel("Maximum finite stretch") 86 | ax[1].set_xlim(-90,90) 87 | ax[1].set_ylim(1,max(pfs[:,1])+0.5) 88 | ax[1].grid() 89 | 90 | return paths, pfs -------------------------------------------------------------------------------- /source/functions/small_circle.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from rotate import rotate 3 | from st_coord_line import st_coord_line 4 | from zero_twopi import zero_twopi 5 | 6 | def small_circle(trda,plga,cangle,stype): 7 | """ 8 | small_circle computes the paths of a small circle defined 9 | by its axis and cone angle, for an equal angle or equal 10 | area stereonet of unit radius 11 | 12 | trda = trend of axis 13 | plga = plunge of axis 14 | cangle = cone angle 15 | stype = Stereonet type: 0 = equal angle, 1 = equal area 16 | path1 and path2 = vectors with the x and y coordinates 17 | of the points in the small circle paths 18 | np1 and np2 = Number of points in path1 and path2 19 | 20 | NOTE: All angles should be in radians 21 | 22 | Python function translated from the Matlab function 23 | SmallCircle in Allmendinger et al. (2012) 24 | """ 25 | pi = np.pi 26 | # find where to start the small circle 27 | if (plga - cangle) >= 0.0: 28 | trd = trda 29 | plg = plga - cangle 30 | else: 31 | if plga == pi/2.0: 32 | plga *= 0.9999 33 | angle = np.arccos(np.cos(cangle)/np.cos(plga)) 34 | trd = zero_twopi(trda+angle) 35 | plg = 0.0 36 | 37 | # to make the small circle, rotate the starting line 38 | # 360 degrees in increments of 1 degree 39 | rot = np.radians(np.arange(0,361,1)) 40 | path1 = np.zeros((rot.shape[0],2)) 41 | path2 = np.zeros((rot.shape[0],2)) 42 | np1 = np2 = 0 43 | for i in range(rot.shape[0]): 44 | # rotate line: The line is considered as a vector 45 | rtrd , rplg = rotate(trda,plga,rot[i],trd,plg,"v") 46 | # calculate stereonet coordinates and add to path 47 | # if rotated plunge is positive add to 1st path 48 | if rplg >= 0.0: 49 | path1[np1,0] , path1[np1,1] = st_coord_line(rtrd,rplg, 50 | stype) 51 | np1 += 1 52 | # else add to 2nd path 53 | else: 54 | path2[np2,0] , path2[np2,1] = st_coord_line(rtrd,rplg, 55 | stype) 56 | np2 += 1 57 | 58 | return path1, path2, np1, np2 -------------------------------------------------------------------------------- /source/functions/sph_to_cart.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def sph_to_cart(trd,plg): 4 | """ 5 | sph_to_cart converts line from spherical (trend 6 | and plunge) to Cartesian (direction cosines) 7 | coordinates 8 | 9 | sph_to_cart(trd,plg) returns the north (cn), 10 | east (ce), and down (cd) direction cosines of 11 | a line with trend = trd and plunge = plg 12 | 13 | NOTE: Angles should be entered in radians 14 | 15 | Python function based on the Matlab function 16 | SphToCart in Allmendinger et al. (2012) 17 | """ 18 | # compute direction cosines from trend and plunge 19 | cn = math.cos(trd) * math.cos(plg) 20 | ce = math.sin(trd) * math.cos(plg) 21 | cd = math.sin(plg) 22 | 23 | return cn, ce, cd -------------------------------------------------------------------------------- /source/functions/sph_to_cart_u.py: -------------------------------------------------------------------------------- 1 | from uncertainties import umath # From E. Lebigot 2 | 3 | def sph_to_cart_u(trd,plg): 4 | """ 5 | sph_to_cart_u converts line from spherical (trend 6 | and plunge) to Cartesian (direction cosines) 7 | coordinates 8 | 9 | sph_to_cart_u(trd,plg) returns the north (cn), 10 | east (ce), and down (cd) direction cosines of 11 | a line with trend = trd and plunge = plg 12 | Input and output values have uncertainties 13 | 14 | NOTE: Angles should be entered in radians 15 | 16 | Based on Python function sph_to_cart 17 | """ 18 | # compute direction cosines from trend and plunge 19 | cn = umath.cos(trd) * umath.cos(plg) 20 | ce = umath.sin(trd) * umath.cos(plg) 21 | cd = umath.sin(plg) 22 | 23 | return cn, ce, cd -------------------------------------------------------------------------------- /source/functions/st_coord_line.py: -------------------------------------------------------------------------------- 1 | import math 2 | from zero_twopi import zero_twopi 3 | 4 | def st_coord_line(trd,plg,stype): 5 | """ 6 | st_coord_line computes the coordinates of a line 7 | on an equal angle or equal area stereonet of unit radius 8 | 9 | trd = trend of line 10 | plg = plunge of line 11 | stype = Stereonet type: 0 = equal angle, 1 = equal area 12 | xp and yp: Coordinates of the line in the stereonet 13 | 14 | NOTE: trend and plunge should be entered in radians 15 | 16 | Python function translated from the Matlab function 17 | StCoordLine in Allmendinger et al. (2012) 18 | """ 19 | # Take care of negative plunges 20 | if plg < 0: 21 | trd = zero_twopi(trd+math.pi) 22 | plg *= -1.0 23 | 24 | # Equal angle stereonet 25 | if stype == 0: 26 | x = math.tan(math.pi/4 - plg/2) 27 | # Equal area stereonet 28 | elif stype == 1: 29 | x = math.sqrt(2) * math.sin(math.pi/4 - plg/2) 30 | 31 | # Compute coordinates 32 | xp = x * math.sin(trd) 33 | yp = x * math.cos(trd) 34 | 35 | return xp, yp -------------------------------------------------------------------------------- /source/functions/stereonet.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from pole import plane_from_pole 4 | from geogr_to_view import geogr_to_view 5 | from small_circle import small_circle 6 | from great_circle import great_circle 7 | 8 | def stereonet(trdv,plgv,intrad,stype, ax): 9 | """ 10 | stereonet plots an equal angle or equal area stereonet 11 | of unit radius in any view direction 12 | 13 | USE: stereonet(trdv,plgv,intrad,stype, ax) 14 | 15 | trdv = trend of view direction 16 | plgv = plunge of view direction 17 | intrad = interval in radians between great or small circles 18 | stype = Stereonet type: 0 = equal angle, 1 = equal area 19 | ax = axes handle to plot stereonet 20 | 21 | NOTE: All angles should be entered in radians 22 | 23 | Python function translated from the Matlab function 24 | Stereonet in Allmendinger et al. (2012) 25 | """ 26 | pi = np.pi 27 | # some constants 28 | east = pi/2.0 29 | west = 3.0*east 30 | 31 | # stereonet reference circle 32 | r = 1.0 # radius of stereonet 33 | th = np.radians(np.arange(0,361,1)) 34 | x = r * np.cos(th) 35 | y = r * np.sin(th) 36 | # plot stereonet reference circle 37 | ax.plot(x,y, "k") 38 | ax.axis ([-1, 1, -1, 1]) 39 | ax.axis ("equal") 40 | ax.axis("off") 41 | 42 | # number of small circles 43 | ncircles = int(pi/(intrad*2.0)) 44 | # new interval 45 | intrad = pi/(ncircles*2.0) 46 | 47 | # small circles, start at North 48 | trd = plg = 0.0 49 | 50 | # if view direction is not the default 51 | # transform line to view direction 52 | if trdv != 0.0 or plgv != east: 53 | trd, plg = geogr_to_view(trd,plg,trdv,plgv) 54 | 55 | # plot small circles 56 | for i in range(1,ncircles+1): 57 | cangle = i * intrad 58 | path1, path2, np1, np2 = small_circle(trd,plg,cangle, 59 | stype) 60 | ax.plot(path1[:np1,0], path1[:np1,1], color="gray", 61 | linewidth=0.5) 62 | if np2 > 0: 63 | ax.plot(path2[:np2,0], path2[:np2,1], color="gray", 64 | linewidth=0.5) 65 | 66 | # great circles 67 | for i in range(ncircles*2+1): 68 | # western half 69 | if i <= ncircles: 70 | # pole of great circle 71 | trd = west 72 | plg = i * intrad 73 | # eastern half 74 | else: 75 | # pole of great circle 76 | trd = east 77 | plg = (i-ncircles) * intrad 78 | # if pole is vertical shift it a little bit 79 | if plg == east: 80 | plg *= 0.9999 81 | # if view direction is not the default 82 | # transform line to view direction 83 | if trdv != 0.0 or plgv != east: 84 | trd, plg = geogr_to_view(trd,plg,trdv,plgv) 85 | # compute plane from pole 86 | strike, dip = plane_from_pole(trd,plg) 87 | # plot great circle 88 | path = great_circle(strike,dip,stype) 89 | ax.plot(path[:,0],path[:,1],color="gray",linewidth=0.5) 90 | -------------------------------------------------------------------------------- /source/functions/three_points.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from cart_to_sph import cart_to_sph 3 | from pole import plane_from_pole 4 | 5 | def three_points(p1,p2,p3): 6 | """ 7 | three_points calculates the strike (stk) and dip (dip) 8 | of a plane given the east (E), north (N), and up (U) 9 | coordinates of three non-collinear points on the plane 10 | 11 | p1, p2 and p3 are 1 x 3 arrays defining the location 12 | of the points in an ENU coordinate system. For each one 13 | of these arrays the first, second and third entries are 14 | the E, N and U coordinates, respectively 15 | 16 | NOTE: stk and dip are returned in radians and they 17 | follow the right-hand rule format 18 | """ 19 | # make vectors v (p1 - p3) and u (p2 - p3) 20 | v = p1 - p2 21 | u = p2 - p3 22 | # take the cross product of v and u 23 | vcu = np.cross(v,u) 24 | 25 | # make this vector a unit vector 26 | mvcu = np.linalg.norm(vcu) # magnitude of the vector 27 | if mvcu == 0: # If points collinear 28 | raise ValueError("Error: points are collinear") 29 | 30 | vcu = vcu/mvcu # unit vector 31 | 32 | # make the pole vector in NED coordinates 33 | p = np.array([vcu[1], vcu[0], -vcu[2]]) 34 | 35 | # make pole point downwards 36 | if p[2] < 0: 37 | p *= -1.0 38 | 39 | # find the trend and plunge of the pole 40 | trd, plg = cart_to_sph(p[0],p[1],p[2]) 41 | 42 | # find stk and dip of plane 43 | stk, dip = plane_from_pole(trd, plg) 44 | 45 | return stk, dip -------------------------------------------------------------------------------- /source/functions/three_points_u.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from uncertainties import umath # From E. Lebigot 3 | from cart_to_sph_u import cart_to_sph_u 4 | from pole import plane_from_pole 5 | 6 | def three_points_u(p1,p2,p3): 7 | """ 8 | three_points_u calculates the strike (stk) and dip (dip) 9 | of a plane given the east (E), north (N), and up (U) 10 | coordinates of three non-collinear points on the plane 11 | 12 | p1, p2 and p3 are 1 x 3 arrays defining the location 13 | of the points in an ENU coordinate system. For each one 14 | of these arrays the first, second and third entries are 15 | the E, N and U coordinates, respectively 16 | 17 | Input and output values have uncertainties 18 | 19 | NOTE: stk and dip are returned in radians and they 20 | follow the right-hand rule format 21 | """ 22 | # make vectors v (p1 - p3) and u (p2 - p3) 23 | v = p1 - p2 24 | u = p2 - p3 25 | # take the cross product of v and u 26 | vcu = np.cross(v,u) 27 | 28 | # make this vector a unit vector by dividing it 29 | # by its magnitude 30 | mvcu = umath.sqrt(np.dot(vcu, vcu)) 31 | if mvcu == 0: # If points collinear 32 | raise ValueError("Error: points are collinear") 33 | 34 | vcu = vcu/mvcu # unit vector 35 | 36 | # make the pole vector in NED coordinates 37 | p = np.array([vcu[1], vcu[0], -vcu[2]]) 38 | 39 | # make pole point downwards 40 | if p[2] < 0: 41 | p *= -1.0 42 | 43 | # find the trend and plunge of the pole 44 | trd, plg = cart_to_sph_u(p[0],p[1],p[2]) 45 | 46 | # find stk and dip of plane 47 | stk, dip = plane_from_pole(trd, plg) 48 | 49 | return stk, dip -------------------------------------------------------------------------------- /source/functions/transform_stress.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from dircos_axes import dircos_axes 3 | 4 | def transform_stress(stress,tx1,px1,tx3,ntx1,npx1,ntx3): 5 | """ 6 | transform_stress transforms a stress tensor from 7 | old X1X2X3 to new X1'X2'X3' coordinates 8 | 9 | nstress=transform_stress(stress,tx1,px1,tx3,ntx1,npx1,ntx3) 10 | 11 | stress = 3 x 3 stress tensor 12 | tx1 = trend of X1 13 | px1 = plunge of X1 14 | tx3 = trend of X3 15 | ntx1 = trend of X1' 16 | npx1 = plunge of X1' 17 | ntx3 = trend of X3' 18 | nstress = 3 x 3 stress tensor in new coordinate system 19 | 20 | NOTE: All input angles should be in radians 21 | 22 | Python function translated from the Matlab function 23 | TransformStress in Allmendinger et al. (2012) 24 | """ 25 | # direction cosines of axes of old coordinate system 26 | odc = dircos_axes(tx1,px1,tx3) 27 | 28 | # direction cosines of axes of new coordinate system 29 | ndc = dircos_axes(ntx1,npx1,ntx3) 30 | 31 | # transformation matrix between old and new 32 | # coordinate systems 33 | a = np.zeros((3,3)) 34 | for i in range(3): 35 | for j in range(3): 36 | # Use dot product 37 | a[i,j] = np.dot(ndc[i,:],odc[j,:]) 38 | 39 | # transform stress 40 | nstress = np.zeros((3,3)) 41 | for i in range(3): 42 | for j in range(3): 43 | for k in range(3): 44 | for l in range(3): 45 | nstress[i,j] = a[i,k] * a[j,l] * stress[k,l] \ 46 | + nstress[i,j] 47 | 48 | return nstress -------------------------------------------------------------------------------- /source/functions/true_thickness.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def true_thickness(stk,dip,top,base): 4 | """ 5 | true_thickness calculates the thickness (t) of a unit 6 | given the strike (stk) and dip (dip) of the unit, 7 | and points at its top (top) and base (base) 8 | 9 | top and base are 1 x 3 arrays defining the location 10 | of top and base points in an ENU coordinate system. 11 | For each one of these arrays, the first, second 12 | and third entries are the E, N and U coordinates 13 | 14 | NOTE: stk and dip should be input in radians 15 | """ 16 | # make the transformation matrix a 17 | # from ENU coordinates to SDP coordinates 18 | sin_str = np.sin(stk) 19 | cos_str = np.cos(stk) 20 | sin_dip = np.sin(dip) 21 | cos_dip = np.cos(dip) 22 | a = np.array([[sin_str, cos_str, 0], 23 | [cos_str*cos_dip, -sin_str*cos_dip, -sin_dip], 24 | [-cos_str*sin_dip, sin_str*sin_dip, -cos_dip]]) 25 | 26 | # transform the top and base points 27 | # from ENU to SDP coordinates 28 | topn = np.zeros(3) 29 | basen = np.zeros(3) 30 | for i in range(3): 31 | for j in range(3): 32 | topn[i] = a[i,j]*top[j] + topn[i] 33 | basen[i] = a[i,j]*base[j] + basen[i] 34 | 35 | # compute the thickness of the unit 36 | t = np.abs(basen[2] - topn[2]) 37 | 38 | return t -------------------------------------------------------------------------------- /source/functions/true_thickness_u.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from uncertainties import ufloat # From E. Lebigot 3 | from uncertainties import umath # From E. Lebigot 4 | 5 | def true_thickness_u(stk,dip,top,base): 6 | """ 7 | true_thickness_u calculates the thickness (t) of a unit 8 | given the strike (stk) and dip (dip) of the unit, 9 | and points at its top (top) and base (base). 10 | stk and dip, as well as the points have 11 | uncertainties. 12 | 13 | top and base are 1 x 3 arrays defining the location 14 | of top and base points in an ENU coordinate system. 15 | For each one of these arrays, the first, second 16 | and third entries are the E, N and U coordinates. 17 | These coordinates have uncertainties 18 | 19 | NOTE: stk and dip should be input in radians and 20 | they have uncertainties in radians. The 21 | returned thickness has also uncertainties 22 | """ 23 | # make the transformation matrix from ENU coordinates 24 | # to SDP coordinates 25 | sin_str = umath.sin(stk) 26 | cos_str = umath.cos(stk) 27 | sin_dip = umath.sin(dip) 28 | cos_dip = umath.cos(dip) 29 | a = np.array([[sin_str, cos_str, ufloat(0,0)], 30 | [cos_str*cos_dip, -sin_str*cos_dip, -sin_dip], 31 | [-cos_str*sin_dip, sin_str*sin_dip, -cos_dip]]) 32 | 33 | # transform the top and base points 34 | # from ENU to SDP coordinates 35 | topn = np.array([ufloat(0,0), ufloat(0,0), ufloat(0,0)]) 36 | basen = np.array([ufloat(0,0), ufloat(0,0), ufloat(0,0)]) 37 | for i in range(3): 38 | for j in range(3): 39 | topn[i] = a[i,j]*top[j] + topn[i] 40 | basen[i] = a[i,j]*base[j] + basen[i] 41 | 42 | # compute the thickness of the unit 43 | t = np.abs(basen[2] - topn[2]) 44 | 45 | return t -------------------------------------------------------------------------------- /source/functions/zero_twopi.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def zero_twopi(a): 4 | """ 5 | This function makes sure input azimuth (a) 6 | is within 0 and 2*pi 7 | 8 | NOTE: Azimuth a is input/output in radians 9 | 10 | Python function translated from the Matlab function 11 | ZeroTwoPi in Allmendinger et al. (2012) 12 | """ 13 | twopi = 2*math.pi 14 | if a < 0: 15 | a += twopi 16 | elif a >= twopi: 17 | a -= twopi 18 | 19 | return a -------------------------------------------------------------------------------- /source/notebooks/ch2-3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "We have a coordinate pair defined in decimal degrees of latitude and longitude. The longitude is -120.108° and latitude is 34.36116666°. We want to make a coordinate conversion from latitude and longitude to Universal Transverse Mercator, where the point is defined by east and north coordinates in meters. To learn more about Universal Transverse Mercator (UTM), refer to (Snyder, 1987). In the code, we use the pyproj `Proj` function. We can only use `Proj` when making a coordinate conversion (i.e. the same datum). Run the code below." 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "x=765975.641, y=3805993.134\n" 20 | ] 21 | } 22 | ], 23 | "source": [ 24 | "# Import pyproj\n", 25 | "from pyproj import Proj\n", 26 | "\n", 27 | "# Construct the projection matrix\n", 28 | "p = Proj(proj=\"utm\",zone=10,ellps=\"WGS84\", \n", 29 | " preserve_units=False)\n", 30 | "\n", 31 | "# Apply the projection to the lat-long point\n", 32 | "x,y = p(-120.108, 34.36116666)\n", 33 | "\n", 34 | "print(f\"x={x:9.3f}, y={y:11.3f}\")" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "This is the same location but only expressed in meters (east and north) using the UTM coordinate reference system. The datum used is WGS84. We can convert the UTM coordinates back to latitute and longitude by adding two lines of code:" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 2, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "name": "stdout", 51 | "output_type": "stream", 52 | "text": [ 53 | "lon=-120.10800000, lat=34.36116666\n" 54 | ] 55 | } 56 | ], 57 | "source": [ 58 | "# Apply the inverse of the projection matrix\n", 59 | "# to the point in UTM\n", 60 | "lon,lat = p(x,y,inverse=True)\n", 61 | "print(f\"lon={lon:8.8f}, lat={lat:5.8f}\")" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "We can confirm that the inverse conversion arrives at the original pair. Let's now try converting several points of different latitude and longitude using a collection of objects in Python, or tuples. Add the following code:" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 3, 74 | "metadata": {}, 75 | "outputs": [ 76 | { 77 | "name": "stdout", 78 | "output_type": "stream", 79 | "text": [ 80 | "(792763.8631257229, 925321.5373562573, 554714.3009414743) (4074377.6167697194, 3763936.9410883673, 4163835.3033114495)\n" 81 | ] 82 | } 83 | ], 84 | "source": [ 85 | "# three points in lat-long\n", 86 | "lons = (-119.72,-118.40,-122.38)\n", 87 | "lats = (36.77, 33.93, 37.62 )\n", 88 | "# Apply the projection to the points\n", 89 | "x1,y1 = p(lons, lats)\n", 90 | "print(x1,y1)" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "Now, let's do a more advanced exercise: In the cartographic community, an easy way to communicate the coordinate reference system is to use the EPSG Geodetic Parameter Data set. Every coordinate reference system is given a code. This ensures that if someone uses UTM zone 10 North with datum WGS-84 and tells you UTM zone 10, that you do not accidentally use UTM zone 10 North with datum GRS80, for example.\n", 98 | "\n", 99 | "Earlier in this exercise, we defined the UTM zone in the Proj function. Here, we will refer to the EPSG code. First, we will take a coordinate pair in longitude and latitude with datum WGS84 and convert it to EPSG:32667. Before proceeding, conduct a quick internet search on what EPSG:32667 means. This is important to understand what we will do next. The first part of the code is:" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 4, 105 | "metadata": { 106 | "scrolled": true 107 | }, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "x=-5851386.754, y=20320914.191 (feet)\n" 114 | ] 115 | } 116 | ], 117 | "source": [ 118 | "# silence warnings\n", 119 | "import warnings \n", 120 | "warnings.simplefilter(\"ignore\")\n", 121 | "\n", 122 | "# initial coordinate conversion\n", 123 | "p = Proj(init=\"EPSG:32667\", preserve_units=True, \n", 124 | " always_xy=True)\n", 125 | "# Apply the conversion to the lat-long point\n", 126 | "x,y = p(-114.057222, 51.045)\n", 127 | "print(f\"x={x:9.3f}, y={y:11.3f} (feet)\")" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "Let’s dissect this as the pyproj code looks quite a bit different. The first part of the function `Proj` calls EPSG:32667. If you looked up EPSG:32667 online, you found that it is for UTM zone 17 North, but the units are in feet. The default mode for`Proj` is `preserve_units=False`, which forces any unit to meters. However, we want to see the units in US Survey Feet as the projection defines; therefore, we change the argument to `True`.\n", 135 | "\n", 136 | "Now, suppose we want to see the output in meters. How will you amend the code? Here is what you should add:" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 5, 142 | "metadata": {}, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "x=-1783506.250, y=6193827.033 (meters)\n" 149 | ] 150 | } 151 | ], 152 | "source": [ 153 | "# Print the coordinate pair in meters\n", 154 | "p1 = Proj(init=\"EPSG:32667\", preserve_units=False)\n", 155 | "x1,y1 = p1(-114.057222, 51.045)\n", 156 | "print(f\"x={x1:9.3f}, y={y1:11.3f} (meters)\")" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "As discussed, you should change to `preserve_units=False` and change the unit to be printed from `feet` to `meters`. Congratulations! You now have a good understanding of coordinate conversions." 164 | ] 165 | } 166 | ], 167 | "metadata": { 168 | "kernelspec": { 169 | "display_name": "base", 170 | "language": "python", 171 | "name": "python3" 172 | }, 173 | "language_info": { 174 | "codemirror_mode": { 175 | "name": "ipython", 176 | "version": 3 177 | }, 178 | "file_extension": ".py", 179 | "mimetype": "text/x-python", 180 | "name": "python", 181 | "nbconvert_exporter": "python", 182 | "pygments_lexer": "ipython3", 183 | "version": "3.12.7" 184 | } 185 | }, 186 | "nbformat": 4, 187 | "nbformat_minor": 4 188 | } 189 | -------------------------------------------------------------------------------- /source/notebooks/ch2-4.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "We will use the pyproj `CRS` and `transform` functions. The `CRS` function defines the coordinate reference system while`transform` specifies which coordinate reference system is the original and which is the output.`CRS` has the same ability to refer directly to an EPSG code.\n", 8 | "\n", 9 | "The input coordinates are in EPSG:4326, which is a commonly used code. It is the geographic coordinate system with datum WGS84. The output coordinates are EPSG:31984, which is for UTM zone 24 S with datum SIRGAS2000." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "x=2930179.850, y=5185231.716\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "# Import transform and CRS functions\n", 27 | "from pyproj import transform\n", 28 | "from pyproj import CRS\n", 29 | "\n", 30 | "# Function transform is deprecated\n", 31 | "# Silence warnings\n", 32 | "import warnings \n", 33 | "warnings.simplefilter(\"ignore\")\n", 34 | "\n", 35 | "# input coordinates\n", 36 | "c1 = CRS(\"EPSG:4326\")\n", 37 | "# coordinate pair\n", 38 | "y1=-10.754283\n", 39 | "x1=-39.866132\n", 40 | "# output coordinates\n", 41 | "c2 = CRS(\"EPSG:31984\")\n", 42 | "# Coordinate transformation\n", 43 | "x2, y2 = transform(c1, c2, x1, y1)\n", 44 | "print(f\"x={x2:9.3f}, y={y2:11.3f}\")" 45 | ] 46 | } 47 | ], 48 | "metadata": { 49 | "kernelspec": { 50 | "display_name": "base", 51 | "language": "python", 52 | "name": "python3" 53 | }, 54 | "language_info": { 55 | "codemirror_mode": { 56 | "name": "ipython", 57 | "version": 3 58 | }, 59 | "file_extension": ".py", 60 | "mimetype": "text/x-python", 61 | "name": "python", 62 | "nbconvert_exporter": "python", 63 | "pygments_lexer": "ipython3", 64 | "version": "3.12.7" 65 | } 66 | }, 67 | "nbformat": 4, 68 | "nbformat_minor": 4 69 | } 70 | -------------------------------------------------------------------------------- /source/notebooks/ch2-5.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Transform locations from lat-long to World Mercator\n", 8 | "\n", 9 | "We have a csv file with two columns: longitude and latitude. Each coordinate pair is the center of a volcano around the world. There are 1,509 volcanoes in our dataset. The original coordinate reference system is geographic coordinates with datum WGS84. We want to make a coordinate transformation of these data points to World Mercator. It will take much too long to manually transform these coordinates as we have done in the notebooks before. Therefore, our new code will read the csv file and create a new csv file.\n", 10 | "\n", 11 | "Check that the input (`src_dir`) and output (`dst_dir`) directories match the directory where the csv file is. In this example, the volcanoes file `volc_longlat.csv`) is in the directory `data/ch2-5`. Run the code, you will know the process is finished when the message \"process completed\" and the time of execution are returned:" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "metadata": {}, 18 | "outputs": [ 19 | { 20 | "name": "stdout", 21 | "output_type": "stream", 22 | "text": [ 23 | "process completed in 0.012643098831176758 seconds\n" 24 | ] 25 | } 26 | ], 27 | "source": [ 28 | "# Thanks to Rustam Zaitov for implementing \n", 29 | "# this new version of the code\n", 30 | "\n", 31 | "# Import libraries\n", 32 | "import csv, time\n", 33 | "from os import path\n", 34 | "from pyproj import Transformer, CRS\n", 35 | "\n", 36 | "src_file = \"volc_longlat.csv\" # input file\n", 37 | "dst_file = \"volc_projected.csv\" # output file\n", 38 | "\n", 39 | "src_dir = path.abspath(\"../data/ch2-5\") # input directory\n", 40 | "dst_dir = path.abspath(\"../data/ch2-5\") # output directory\n", 41 | "\n", 42 | "src_path = path.join(src_dir, src_file)\n", 43 | "dst_path = path.join(dst_dir, dst_file)\n", 44 | "\n", 45 | "src_crs = CRS(\"EPSG:4326\") #WGS84\n", 46 | "dst_crs = CRS(\"EPSG:3395\") #World Mercator\n", 47 | "\n", 48 | "# create coordinate transformer\n", 49 | "# always_xy=True makes projector.transform() accept \n", 50 | "# lon, lat (GIS order) instead of lat, lon\n", 51 | "projector = Transformer.from_crs(src_crs, dst_crs, \n", 52 | " always_xy=True)\n", 53 | "\n", 54 | "# source csv file has lon, lat columns\n", 55 | "src_header = [\"LONGITUDE\", \"LATITUDE\"]\n", 56 | "\n", 57 | "# destinatin csv file will have x, y columns\n", 58 | "dst_header = [\"x\", \"y\"]\n", 59 | "\n", 60 | "# start benchmark timer\n", 61 | "start_time = time.time()\n", 62 | "\n", 63 | "# open destination file in write mode\n", 64 | "with open(dst_path, \"w\") as w:\n", 65 | " # open source file in read mode\n", 66 | " with open(src_path, \"r\") as r:\n", 67 | " reader = csv.reader(r, dialect=\"excel\")\n", 68 | " # read and skip first header row \n", 69 | " input_headers = next(reader) \n", 70 | "\n", 71 | " writer = csv.writer(w, delimiter=\",\", quotechar='\"',\n", 72 | " quoting=csv.QUOTE_MINIMAL)\n", 73 | " # Write the output header\n", 74 | " writer.writerow(dst_header) \n", 75 | " for row in reader:\n", 76 | " try:\n", 77 | " # convert string values inside row \n", 78 | " # into float values\n", 79 | " lon, lat = [float(val) for val in row]\n", 80 | " x, y = projector.transform(lon, lat)\n", 81 | " writer.writerow([ x, y ])\n", 82 | " except Exception as e:\n", 83 | " # If coordinates are out of bounds, \n", 84 | " # skip row and print the error\n", 85 | " print (e)\n", 86 | "\n", 87 | "# stop benchmarking\n", 88 | "end_time = time.time()\n", 89 | "\n", 90 | "print(\"process completed in {} seconds\"\n", 91 | " .format(end_time-start_time))" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "It takes less than one second to run this code! Check the newly created csv file and notice that you now have a listing of coordinates in meters. The EPSG definition of the output coordinate reference system is listed under `dst_crs`. You can easily change this variable to another EPSG and rerun the script. If you wish to run the script on another file, change the`src_file` and `dst_file`, and the `scr_dir` and `dst_dir` if the file is in another directory." 99 | ] 100 | } 101 | ], 102 | "metadata": { 103 | "kernelspec": { 104 | "display_name": "base", 105 | "language": "python", 106 | "name": "python3" 107 | }, 108 | "language_info": { 109 | "codemirror_mode": { 110 | "name": "ipython", 111 | "version": 3 112 | }, 113 | "file_extension": ".py", 114 | "mimetype": "text/x-python", 115 | "name": "python", 116 | "nbconvert_exporter": "python", 117 | "pygments_lexer": "ipython3", 118 | "version": "3.12.7" 119 | } 120 | }, 121 | "nbformat": 4, 122 | "nbformat_minor": 4 123 | } 124 | -------------------------------------------------------------------------------- /source/notebooks/ch4-1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Vector: [1 2 3]\n", 13 | "Magnitude of the vector: 3.7417\n", 14 | "Unit Vector: [0.2673 0.5345 0.8018]\n", 15 | "Magnitude of the unit vector: 1.0000\n" 16 | ] 17 | } 18 | ], 19 | "source": [ 20 | "# Import numpy\n", 21 | "import numpy as np\n", 22 | "# Make vector\n", 23 | "v = np.array([1,2,3])\n", 24 | "print(f\"Vector: {v}\")\n", 25 | "# Magnitude of the vector\n", 26 | "length = np.linalg.norm(v) \n", 27 | "print(f\"Magnitude of the vector: {length:.4f}\")\n", 28 | "# Unit vector\n", 29 | "v_hat = v / length\n", 30 | "print(f\"Unit Vector: {np.round(v_hat,4)}\")\n", 31 | "# Magnitude of unit vector\n", 32 | "length = np.linalg.norm(v_hat) \n", 33 | "print(f\"Magnitude of the unit vector: {length:.4f}\")" 34 | ] 35 | } 36 | ], 37 | "metadata": { 38 | "kernelspec": { 39 | "display_name": "base", 40 | "language": "python", 41 | "name": "python3" 42 | }, 43 | "language_info": { 44 | "codemirror_mode": { 45 | "name": "ipython", 46 | "version": 3 47 | }, 48 | "file_extension": ".py", 49 | "mimetype": "text/x-python", 50 | "name": "python", 51 | "nbconvert_exporter": "python", 52 | "pygments_lexer": "ipython3", 53 | "version": "3.12.7" 54 | } 55 | }, 56 | "nbformat": 4, 57 | "nbformat_minor": 4 58 | } 59 | -------------------------------------------------------------------------------- /source/notebooks/ch4-2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "u = [1 2 3]\n", 13 | "v = [3 2 1]\n", 14 | "3 * u = [3 6 9]\n", 15 | "u + v = [4 4 4]\n", 16 | "u . v = 10\n", 17 | "u x v = [-4 8 -4]\n" 18 | ] 19 | } 20 | ], 21 | "source": [ 22 | "# Import numpy\n", 23 | "import numpy as np\n", 24 | "# Make vectors\n", 25 | "u = np.array([1,2,3])\n", 26 | "v = np.array([3,2,1])\n", 27 | "print(f\"u = {u}\")\n", 28 | "print(f\"v = {v}\")\n", 29 | "# Scalar multiplication of vector\n", 30 | "sv = 3 * u\n", 31 | "print(f\"3 * u = {sv}\")\n", 32 | "# Sum of vectors\n", 33 | "vsum = u + v\n", 34 | "print(f\"u + v = {vsum}\")\n", 35 | "# Dot product of vectors\n", 36 | "dotp = np.dot(u,v)\n", 37 | "print(f\"u . v = {dotp}\")\n", 38 | "# Cross product of vectors\n", 39 | "crossp = np.cross(u,v) \n", 40 | "print(f\"u x v = {crossp}\")" 41 | ] 42 | } 43 | ], 44 | "metadata": { 45 | "kernelspec": { 46 | "display_name": "base", 47 | "language": "python", 48 | "name": "python3" 49 | }, 50 | "language_info": { 51 | "codemirror_mode": { 52 | "name": "ipython", 53 | "version": 3 54 | }, 55 | "file_extension": ".py", 56 | "mimetype": "text/x-python", 57 | "name": "python", 58 | "nbconvert_exporter": "python", 59 | "pygments_lexer": "ipython3", 60 | "version": "3.12.7" 61 | } 62 | }, 63 | "nbformat": 4, 64 | "nbformat_minor": 4 65 | } 66 | -------------------------------------------------------------------------------- /source/notebooks/ch4-4.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "This notebook illustrates the use of the functions in the module [angles](../functions/angles.py) to solve several interesting problems. Let's start with the following problem: Two limbs of a chevron fold (A and B) have orientations (strike/dip, RHR) as follows:\n", 8 | "\n", 9 | "Limb A = 120/40\n", 10 | "\n", 11 | "Limb B = 250/60\n", 12 | "\n", 13 | "Determine: (a) the trend and plunge of the hinge line of the fold, (b) the rake of the hinge line in limb A, (c) the rake of the hinge line in limb B. " 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 1, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "name": "stdout", 23 | "output_type": "stream", 24 | "text": [ 25 | "Hinge trend = 265.8, plunge 25.3\n", 26 | "Rake of hinge in SW dipping limb = 138.4 E\n", 27 | "Rake of hinge in NW dipping limb = 29.5 W\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "import numpy as np\n", 33 | "rad = 180/np.pi\n", 34 | "\n", 35 | "# Import functions \n", 36 | "import sys, os\n", 37 | "sys.path.append(os.path.abspath(\"../functions\"))\n", 38 | "from angles import angle_bw_lines, int_bw_planes\n", 39 | "\n", 40 | "# Strike and dip of the limbs in radians\n", 41 | "stk1, dip1 = np.radians([120, 40])\n", 42 | "stk2, dip2 = np.radians([250, 60])\n", 43 | "\n", 44 | "# (a) Chevron folds have planar limbs. The hinge\n", 45 | "# of the fold is the intersection of the limbs\n", 46 | "htrd, hplg = int_bw_planes(stk1,dip1,stk2,dip2)\n", 47 | "print(f\"Hinge trend = {htrd*rad:.1f}, plunge {hplg*rad:.1f}\")\n", 48 | "\n", 49 | "# The rake of the hinge on either limb is the angle \n", 50 | "# between the hinge and the strike line on the limb. \n", 51 | "# This line is horizontal and has plunge = 0\n", 52 | "plg = 0\n", 53 | "\n", 54 | "# (b) For the SW dipping limb\n", 55 | "ang = angle_bw_lines(stk1,plg,htrd,hplg)\n", 56 | "print(f\"Rake of hinge in SW dipping limb = {ang*rad:.1f} E\")\n", 57 | "\n", 58 | "# (c) And for the NW dipping limb\n", 59 | "ang = angle_bw_lines(stk2,plg,htrd,hplg)\n", 60 | "print(f\"Rake of hinge in NW dipping limb = {ang*rad:.1f} W\")" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "Let's do another problem: A quarry has two walls, one trending 002 and the other 135. The apparent dip of bedding on the faces are 40N and 30 SE respectively. Calculate the strike and dip of bedding." 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 2, 73 | "metadata": {}, 74 | "outputs": [ 75 | { 76 | "name": "stdout", 77 | "output_type": "stream", 78 | "text": [ 79 | "Bedding strike = 333.9, dip 60.7\n" 80 | ] 81 | } 82 | ], 83 | "source": [ 84 | "# Import function\n", 85 | "from angles import plane_from_app_dips\n", 86 | "\n", 87 | "# The apparent dips are just two lines on bedding\n", 88 | "# These lines have orientations:\n", 89 | "trd1, plg1 = np.radians([2, 40])\n", 90 | "trd2, plg2 = np.radians([135, 30])\n", 91 | "\n", 92 | "# Calculate bedding from these two apparent dips\n", 93 | "stk, dip = plane_from_app_dips(trd1,plg1,trd2,plg2)\n", 94 | "print(f\"Bedding strike = {stk*rad:.1f}, dip {dip*rad:.1f}\")" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "And the final problem: Slickenside lineations trending 074 occur on a fault with orientation 300/50 (RHR). Determine the plunge of these lineations and their rake in the plane of the fault." 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 3, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "Slickensides trend = 74.0, plunge 40.6\n", 114 | "Rake of slickensides = 121.8 W\n" 115 | ] 116 | } 117 | ], 118 | "source": [ 119 | "# The lineation on the fault is just the intersection\n", 120 | "# of a vertical plane with a strike equal to\n", 121 | "# the trend of the lineation, and the fault\n", 122 | "stk1, dip1 = np.radians([74, 90])\n", 123 | "stk2, dip2 = np.radians([300, 50])\n", 124 | "\n", 125 | "# Find the intersection of these two planes which is\n", 126 | "# the lineation on the fault\n", 127 | "ltrd, lplg = int_bw_planes(stk1,dip1,stk2,dip2)\n", 128 | "print(f\"Slickensides trend = {ltrd*rad:.1f}, plunge {lplg*rad:.1f}\")\n", 129 | "\n", 130 | "# And the rake of this lineation is the angle\n", 131 | "# between the lineation and the strike line on the fault\n", 132 | "plg = 0\n", 133 | "ang = angle_bw_lines(stk2,plg,ltrd,lplg)\n", 134 | "print(f\"Rake of slickensides = {ang*rad:.1f} W\")" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "There are many interesting problems you can solve using the functions in the module [angles](../functions/angles.py). You will find more problems in the Exercises section. " 142 | ] 143 | } 144 | ], 145 | "metadata": { 146 | "kernelspec": { 147 | "display_name": "base", 148 | "language": "python", 149 | "name": "python3" 150 | }, 151 | "language_info": { 152 | "codemirror_mode": { 153 | "name": "ipython", 154 | "version": 3 155 | }, 156 | "file_extension": ".py", 157 | "mimetype": "text/x-python", 158 | "name": "python", 159 | "nbconvert_exporter": "python", 160 | "pygments_lexer": "ipython3", 161 | "version": "3.12.7" 162 | } 163 | }, 164 | "nbformat": 4, 165 | "nbformat_minor": 4 166 | } 167 | -------------------------------------------------------------------------------- /source/notebooks/ch4-5.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Plane strike = 84.5, dip = 22.5\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "import numpy as np\n", 18 | "rad = 180/np.pi\n", 19 | "\n", 20 | "# Import function three_points\n", 21 | "import sys, os\n", 22 | "sys.path.append(os.path.abspath(\"../functions\"))\n", 23 | "from three_points import three_points\n", 24 | "\n", 25 | "# ENU coordinates of the three points\n", 26 | "p1 = np.array([509, 2041, 400])\n", 27 | "p2 = np.array([1323, 2362, 500])\n", 28 | "p3 = np.array([2003, 2913, 700])\n", 29 | "\n", 30 | "# Compute the orientation of the plane\n", 31 | "stk, dip = three_points(p1,p2,p3)\n", 32 | "print(f\"Plane strike = {stk*rad:.1f}, dip = {dip*rad:.1f}\")" 33 | ] 34 | } 35 | ], 36 | "metadata": { 37 | "kernelspec": { 38 | "display_name": "base", 39 | "language": "python", 40 | "name": "python3" 41 | }, 42 | "language_info": { 43 | "codemirror_mode": { 44 | "name": "ipython", 45 | "version": 3 46 | }, 47 | "file_extension": ".py", 48 | "mimetype": "text/x-python", 49 | "name": "python", 50 | "nbconvert_exporter": "python", 51 | "pygments_lexer": "ipython3", 52 | "version": "3.12.7" 53 | } 54 | }, 55 | "nbformat": 4, 56 | "nbformat_minor": 4 57 | } 58 | -------------------------------------------------------------------------------- /source/notebooks/ch4-6.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Suppose that in the first problem on page 64, the uncertainty in strike is 4° and in dip is 2°. This problem can be solved as follows:" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "Hinge trend = 265.8+/-3.3, plunge 25.3+/-2.6\n", 20 | "Rake of hinge in SW dipping limb = 138.4+/-4.6 E\n", 21 | "Rake of hinge in NW dipping limb = 29.5+/-3.5 W\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "# Import libraries\n", 27 | "import numpy as np\n", 28 | "rad = 180/np.pi\n", 29 | "from uncertainties import ufloat\n", 30 | "\n", 31 | "# Import functions\n", 32 | "import sys, os\n", 33 | "sys.path.append(os.path.abspath(\"../functions\"))\n", 34 | "from angles_u import angle_bw_lines_u, int_bw_planes_u\n", 35 | "\n", 36 | "# Strike and dip of the limbs in radians\n", 37 | "stk1, dip1 = np.radians([120, 40]) # SW dipping limb\n", 38 | "stk2, dip2 = np.radians([250, 60]) # NW dipping limb\n", 39 | "\n", 40 | "# Errors in strike and dip in radians\n", 41 | "ustr, udip = np.radians([4, 2])\n", 42 | "\n", 43 | "# Create the input values with uncertainties\n", 44 | "stk1 = ufloat(stk1, ustr) # stk1 = stk1 +/-ustr\n", 45 | "dip1 = ufloat(dip1, udip) # dip1 = dip1 +/-udip\n", 46 | "stk2 = ufloat(stk2, ustr) # stk2 = stk2 +/-ustr\n", 47 | "dip2 = ufloat(dip2, udip) # dip2 = dip2 +/-udip\n", 48 | "\n", 49 | "# (a) Chevron folds have planar limbs. The hinge\n", 50 | "# of the fold is the intersection of the limbs\n", 51 | "htrd, hplg = int_bw_planes_u(stk1,dip1,stk2,dip2)\n", 52 | "print(f\"Hinge trend = {htrd*rad:.1f}, plunge {hplg*rad:.1f}\")\n", 53 | "\n", 54 | "# The rake of the hinge on either limb is the angle \n", 55 | "# between the hinge and the strike line on the limb. \n", 56 | "# This line is horizontal and has plunge = 0\n", 57 | "plg = ufloat(0, udip) # plg = 0 +/-udip\n", 58 | "\n", 59 | "# (b) For the SW dipping limb\n", 60 | "ang = angle_bw_lines_u(stk1,plg,htrd,hplg)\n", 61 | "print(f\"Rake of hinge in SW dipping limb = {ang*rad:.1f} E\")\n", 62 | "\n", 63 | "# (c) And for the NW dipping limb\n", 64 | "ang = angle_bw_lines_u(stk2,plg,htrd,hplg)\n", 65 | "print(f\"Rake of hinge in NW dipping limb = {ang*rad:.1f} W\")" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "In the map of Fig. 4.6, the error in East and North coordinates is 10 m, and in elevation is 5 m. What is the strike and dip of the T-S contact? " 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 2, 78 | "metadata": {}, 79 | "outputs": [ 80 | { 81 | "name": "stdout", 82 | "output_type": "stream", 83 | "text": [ 84 | "Plane strike = 84.5+/-3.5, dip = 22.5+/-2.7\n" 85 | ] 86 | } 87 | ], 88 | "source": [ 89 | "# Import function three_points_u\n", 90 | "from three_points_u import three_points_u\n", 91 | "\n", 92 | "# ENU coordinates of the three points\n", 93 | "# with uncertainties in E-N = 10, and U = 5\n", 94 | "p1 = np.array([ufloat(509, 10), ufloat(2041, 10), \n", 95 | " ufloat(400, 5)])\n", 96 | "p2 = np.array([ufloat(1323, 10), ufloat(2362, 10), \n", 97 | " ufloat(500, 5)])\n", 98 | "p3 = np.array([ufloat(2003, 10), ufloat(2913, 10), \n", 99 | " ufloat(700, 5)])\n", 100 | "\n", 101 | "# Compute the orientation of the plane\n", 102 | "stk, dip = three_points_u(p1,p2,p3)\n", 103 | "print(f\"Plane strike = {stk*rad:.1f}, dip = {dip*rad:.1f}\")" 104 | ] 105 | } 106 | ], 107 | "metadata": { 108 | "kernelspec": { 109 | "display_name": "base", 110 | "language": "python", 111 | "name": "python3" 112 | }, 113 | "language_info": { 114 | "codemirror_mode": { 115 | "name": "ipython", 116 | "version": 3 117 | }, 118 | "file_extension": ".py", 119 | "mimetype": "text/x-python", 120 | "name": "python", 121 | "nbconvert_exporter": "python", 122 | "pygments_lexer": "ipython3", 123 | "version": "3.12.7" 124 | } 125 | }, 126 | "nbformat": 4, 127 | "nbformat_minor": 4 128 | } 129 | -------------------------------------------------------------------------------- /source/notebooks/ch5-1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Thickness of unit T = 467.2 m\n", 13 | "Thickness of unit S = 94.6 m\n", 14 | "Thickness of unit R = 278.6 m\n", 15 | "Thickness of unit Q = 195.6 m\n" 16 | ] 17 | } 18 | ], 19 | "source": [ 20 | "import numpy as np\n", 21 | "\n", 22 | "# Import function true_thickness\n", 23 | "import sys, os\n", 24 | "sys.path.append(os.path.abspath(\"../functions\"))\n", 25 | "from true_thickness import true_thickness\n", 26 | "\n", 27 | "# strike and dip of the unit in radians\n", 28 | "stk, dip = np.radians([84.5, 22.5]) \n", 29 | "\n", 30 | "# ENU coordinates of the points\n", 31 | "p1 = np.array([1147, 3329, 400]) \n", 32 | "p2 = np.array([1323, 2362, 500]) \n", 33 | "p3 = np.array([1105, 1850, 400]) \n", 34 | "p4 = np.array([1768, 940, 300]) \n", 35 | "p5 = np.array([1842, 191, 200])\n", 36 | "\n", 37 | "# Compute the thickness of the units\n", 38 | "thickT = true_thickness(stk,dip,p2,p1)\n", 39 | "thickS = true_thickness(stk,dip,p3,p2)\n", 40 | "thickR = true_thickness(stk,dip,p4,p3)\n", 41 | "thickQ = true_thickness(stk,dip,p5,p4)\n", 42 | "print(\"Thickness of unit T = {:.1f} m\".format(thickT))\n", 43 | "print(\"Thickness of unit S = {:.1f} m\".format(thickS))\n", 44 | "print(\"Thickness of unit R = {:.1f} m\".format(thickR))\n", 45 | "print(\"Thickness of unit Q = {:.1f} m\".format(thickQ))" 46 | ] 47 | } 48 | ], 49 | "metadata": { 50 | "kernelspec": { 51 | "display_name": "base", 52 | "language": "python", 53 | "name": "python3" 54 | }, 55 | "language_info": { 56 | "codemirror_mode": { 57 | "name": "ipython", 58 | "version": 3 59 | }, 60 | "file_extension": ".py", 61 | "mimetype": "text/x-python", 62 | "name": "python", 63 | "nbconvert_exporter": "python", 64 | "pygments_lexer": "ipython3", 65 | "version": "3.12.7" 66 | } 67 | }, 68 | "nbformat": 4, 69 | "nbformat_minor": 4 70 | } 71 | -------------------------------------------------------------------------------- /source/notebooks/ch5-2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Thickness of unit T = 467.2+/-31.5 m\n", 13 | "Thickness of unit S = 94.6+/-20.4 m\n", 14 | "Thickness of unit R = 278.6+/-37.0 m\n", 15 | "Thickness of unit Q = 195.6+/-27.0 m\n" 16 | ] 17 | } 18 | ], 19 | "source": [ 20 | "# Import libraries\n", 21 | "import numpy as np\n", 22 | "from uncertainties import ufloat\n", 23 | "\n", 24 | "# Import function true_thickness_u\n", 25 | "import sys, os\n", 26 | "sys.path.append(os.path.abspath(\"../functions\"))\n", 27 | "from true_thickness_u import true_thickness_u\n", 28 | "\n", 29 | "# strike and dip of the unit in radians\n", 30 | "stk, dip = np.radians([84.5, 22.5]) \n", 31 | "\n", 32 | "# strike and dip errors in radians\n", 33 | "ustk, udip = np.radians([4, 2]) \n", 34 | "\n", 35 | "# Create the strike and dip with uncertainties\n", 36 | "stk = ufloat(stk, ustk) \n", 37 | "dip = ufloat(dip, udip)\n", 38 | "\n", 39 | "# ENU coordinates of the points\n", 40 | "# with uncertainties in E-N = 10, and U = 5\n", 41 | "p1 = np.array([ufloat(1147, 10), ufloat(3329, 10), \n", 42 | " ufloat(400, 5)]) \n", 43 | "p2 = np.array([ufloat(1323, 10), ufloat(2362, 10), \n", 44 | " ufloat(500, 5)]) \n", 45 | "p3 = np.array([ufloat(1105, 10), ufloat(1850, 10), \n", 46 | " ufloat(400, 5)]) \n", 47 | "p4 = np.array([ufloat(1768, 10), ufloat(940, 10), \n", 48 | " ufloat(300, 5)]) \n", 49 | "p5 = np.array([ufloat(1842, 10), ufloat(191, 10), \n", 50 | " ufloat(200, 5)])\n", 51 | "\n", 52 | "# Compute the thickness of the units\n", 53 | "thickT = true_thickness_u(stk, dip, p2, p1)\n", 54 | "thickS = true_thickness_u(stk, dip, p3, p2)\n", 55 | "thickR = true_thickness_u(stk, dip, p4, p3)\n", 56 | "thickQ = true_thickness_u(stk, dip, p5, p4) \n", 57 | "print(\"Thickness of unit T = {:.1f} m\".format(thickT))\n", 58 | "print(\"Thickness of unit S = {:.1f} m\".format(thickS))\n", 59 | "print(\"Thickness of unit R = {:.1f} m\".format(thickR))\n", 60 | "print(\"Thickness of unit Q = {:.1f} m\".format(thickQ))" 61 | ] 62 | } 63 | ], 64 | "metadata": { 65 | "kernelspec": { 66 | "display_name": "base", 67 | "language": "python", 68 | "name": "python3" 69 | }, 70 | "language_info": { 71 | "codemirror_mode": { 72 | "name": "ipython", 73 | "version": 3 74 | }, 75 | "file_extension": ".py", 76 | "mimetype": "text/x-python", 77 | "name": "python", 78 | "nbconvert_exporter": "python", 79 | "pygments_lexer": "ipython3", 80 | "version": "3.12.7" 81 | } 82 | }, 83 | "nbformat": 4, 84 | "nbformat_minor": 4 85 | } 86 | -------------------------------------------------------------------------------- /source/notebooks/ch5-5.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "An overturned bed oriented 305/60 (RHR) has sedimentary lineations which indicate the palaeocurrent direction. These pitch at 60NW, with the current flowing up the plunge. Calculate the original trend of the paleocurrents.\n", 8 | "\n", 9 | "Besides rotating the lineations back to their pre-tilted orientation, there is an additional challenge in this problem. We need to figure out the orientation of the current lineations from their pitch on the bed. We will do this as well using a rotation." 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 1, 15 | "metadata": {}, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "Original trend of the paleocurrents = 65.0\n" 22 | ] 23 | } 24 | ], 25 | "source": [ 26 | "import numpy as np\n", 27 | "rad = 180/np.pi\n", 28 | "\n", 29 | "# Import functions \n", 30 | "import sys, os\n", 31 | "sys.path.append(os.path.abspath(\"../functions\"))\n", 32 | "from zero_twopi import zero_twopi\n", 33 | "from pole import pole_from_plane\n", 34 | "from rotate import rotate\n", 35 | "\n", 36 | "# Strike and dip of bed in radians\n", 37 | "strike, dip = np.radians([305, 60]) \n", 38 | "\n", 39 | "# Pole of bed\n", 40 | "rtrd, rplg = pole_from_plane(strike, dip)\n", 41 | "\n", 42 | "# To find the orientation of the lineations\n", 43 | "# rotate the strike line clockwise about the \n", 44 | "# pole an amount equal to the pitch\n", 45 | "\n", 46 | "# strike line\n", 47 | "trd, plg = strike, 0 \n", 48 | "\n", 49 | "# rotation = pitch\n", 50 | "rot = 60/rad # in radians\n", 51 | "\n", 52 | "# orientation of lineations\n", 53 | "trdr, plgr = rotate(rtrd,rplg,rot,trd,plg,\"a\")\n", 54 | "\n", 55 | "# Now we need to rotate the lineations about\n", 56 | "# the strike line to their pre-tilted orientation\n", 57 | "\n", 58 | "# The bed is overturned, so it has been rotated \n", 59 | "# pass the vertical. The amount of rotation\n", 60 | "# required to restore the bed to its pre-tilted\n", 61 | "# orientation is 180- 60 = 120 deg, and it\n", 62 | "# should be clockwise\n", 63 | "rot = 120/rad # in radians\n", 64 | "\n", 65 | "# rotate lineations to their pre-tilted orientation\n", 66 | "trdl, plgl = rotate(trd,plg,rot,trdr,plgr,\"a\")\n", 67 | "\n", 68 | "# The current flows up the plunge, \n", 69 | "# so the trend of the paleocurrents is:\n", 70 | "trdl = zero_twopi(trdl + np.pi)\n", 71 | "print(f\"Original trend of the paleocurrents = {trdl*rad:.1f}\")" 72 | ] 73 | } 74 | ], 75 | "metadata": { 76 | "kernelspec": { 77 | "display_name": "base", 78 | "language": "python", 79 | "name": "python3" 80 | }, 81 | "language_info": { 82 | "codemirror_mode": { 83 | "name": "ipython", 84 | "version": 3 85 | }, 86 | "file_extension": ".py", 87 | "mimetype": "text/x-python", 88 | "name": "python", 89 | "nbconvert_exporter": "python", 90 | "pygments_lexer": "ipython3", 91 | "version": "3.12.7" 92 | } 93 | }, 94 | "nbformat": 4, 95 | "nbformat_minor": 4 96 | } 97 | -------------------------------------------------------------------------------- /source/notebooks/ch6-2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Strike = 153.3, Dip = 29.9\n", 13 | "Standard deviation = 246.8 m\n" 14 | ] 15 | } 16 | ], 17 | "source": [ 18 | "# Import libraries\n", 19 | "import numpy as np\n", 20 | "rad = 180/np.pi\n", 21 | "\n", 22 | "# Import function fit_plane\n", 23 | "import sys, os\n", 24 | "sys.path.append(os.path.abspath(\"../functions\"))\n", 25 | "from fit_plane import fit_plane\n", 26 | "\n", 27 | "# Read the points on the contact\n", 28 | "# Coordinates are UTM (ENU) in meters\n", 29 | "jske = np.loadtxt(os.path.abspath(\"../data/ch6-2/jske.txt\"))\n", 30 | "\n", 31 | "# Compute best-fit plane\n", 32 | "stk, dip, stdev = fit_plane(jske)\n", 33 | "\n", 34 | "# Print strike and dip of plane\n", 35 | "print(f\"Strike = {stk*rad:.1f}, Dip = {dip*rad:.1f}\")\n", 36 | "\n", 37 | "# Print standard deviation of the distance of each point\n", 38 | "# from the best-fit plane\n", 39 | "print(f\"Standard deviation = {stdev:.1f} m\")" 40 | ] 41 | } 42 | ], 43 | "metadata": { 44 | "kernelspec": { 45 | "display_name": "base", 46 | "language": "python", 47 | "name": "python3" 48 | }, 49 | "language_info": { 50 | "codemirror_mode": { 51 | "name": "ipython", 52 | "version": 3 53 | }, 54 | "file_extension": ".py", 55 | "mimetype": "text/x-python", 56 | "name": "python", 57 | "nbconvert_exporter": "python", 58 | "pygments_lexer": "ipython3", 59 | "version": "3.12.7" 60 | } 61 | }, 62 | "nbformat": 4, 63 | "nbformat_minor": 4 64 | } 65 | -------------------------------------------------------------------------------- /source/notebooks/ch7-1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "X1, X2 and X3 tractions = [16.905 20.828 11.651] \n", 13 | "\n", 14 | "Sigma = 28.392, Tau = 7.015\n", 15 | "\n", 16 | "Stress in new coord. system = \n", 17 | " [[31.25 3.062 8.75 ]\n", 18 | " [ 3.062 27.5 -3.062]\n", 19 | " [ 8.75 -3.062 31.25 ]] \n", 20 | "\n", 21 | "Sigma1 = 40.000, T = 126.9, P = -90.0\n", 22 | "Sigma2 = 30.000, T = 270.0, P = -0.0\n", 23 | "Sigma3 = 20.000, T = 180.0, P = 0.0\n" 24 | ] 25 | } 26 | ], 27 | "source": [ 28 | "# Import libraries\n", 29 | "import numpy as np\n", 30 | "\n", 31 | "# Import Cauchy, TransformStress and PrincipalStress\n", 32 | "import sys, os\n", 33 | "sys.path.append(os.path.abspath(\"../functions\"))\n", 34 | "from cauchy import cauchy \n", 35 | "from transform_stress import transform_stress\n", 36 | "from principal_stress import principal_stress\n", 37 | "\n", 38 | "# Stress tensor in principal stress coordinate system\n", 39 | "stress = np.array([[40, 0, 0],[ 0, 30, 0],[ 0, 0, 20]])\n", 40 | "\n", 41 | "# trend and plunge of X1, and trend of X3\n", 42 | "tx1, px1, tx3 = np.radians([0, 90, 0])\n", 43 | "\n", 44 | "# plane orientation\n", 45 | "stk, dip = np.radians([40, 65])\n", 46 | "\n", 47 | "# X1, X2 and X3 tractions on the plane\n", 48 | "t,pt = cauchy(stress,tx1,px1,tx3,stk,dip)\n", 49 | "print(\"X1, X2 and X3 tractions = \", t.round(3),\"\\n\")\n", 50 | "\n", 51 | "# Compute the normal and maximum shear tractions \n", 52 | "# on the plane: Eq. 7.6\n", 53 | "l2 = pt[0]**2\n", 54 | "m2 = pt[1]**2\n", 55 | "n2 = pt[2]**2\n", 56 | "s1 = stress[0,0]\n", 57 | "s2 = stress[1,1]\n", 58 | "s3 = stress[2,2]\n", 59 | "s12 = s1 - s2\n", 60 | "s23 = s2 - s3\n", 61 | "s31 = s3 - s1\n", 62 | "sigma = s1*l2 + s2*m2 + s3*n2\n", 63 | "tau = np.sqrt(s12*s12*l2*m2 + s23*s23*m2*n2 + s31*s31*n2*l2)\n", 64 | "print(f\"Sigma = {sigma:.3f}, Tau = {tau:.3f}\\n\")\n", 65 | "\n", 66 | "# New coordinate system\n", 67 | "# trend and plunge of X\"1,and trend of X\"3\n", 68 | "ntx1, npx1, ntx3 = np.radians([30, 45, 210])\n", 69 | "\n", 70 | "# Transform stress to new coordinate system\n", 71 | "nstress = transform_stress(stress,tx1,px1,tx3,ntx1,npx1,ntx3)\n", 72 | "print(\"Stress in new coord. system = \\n\", \n", 73 | " nstress.round(3),\"\\n\")\n", 74 | "\n", 75 | "# Principal stresses from new components\n", 76 | "ps, dcp = principal_stress(nstress,ntx1,npx1,ntx3)\n", 77 | "ps[:,1:3] = ps[:,1:3]*180/np.pi\n", 78 | "print(f\"Sigma1 = {ps[0,0]:.3f}, T = {ps[0,1]:.1f}, P = {ps[0,2]:.1f}\")\n", 79 | "print(f\"Sigma2 = {ps[1,0]:.3f}, T = {ps[1,1]:.1f}, P = {ps[1,2]:.1f}\")\n", 80 | "print(f\"Sigma3 = {ps[2,0]:.3f}, T = {ps[2,1]:.1f}, P = {ps[2,2]:.1f}\")" 81 | ] 82 | } 83 | ], 84 | "metadata": { 85 | "kernelspec": { 86 | "display_name": "base", 87 | "language": "python", 88 | "name": "python3" 89 | }, 90 | "language_info": { 91 | "codemirror_mode": { 92 | "name": "ipython", 93 | "version": 3 94 | }, 95 | "file_extension": ".py", 96 | "mimetype": "text/x-python", 97 | "name": "python", 98 | "nbconvert_exporter": "python", 99 | "pygments_lexer": "ipython3", 100 | "version": "3.12.7" 101 | } 102 | }, 103 | "nbformat": 4, 104 | "nbformat_minor": 4 105 | } 106 | -------------------------------------------------------------------------------- /source/notebooks/mylib.py: -------------------------------------------------------------------------------- 1 | def deg_to_rad(angle_degrees): 2 | pi = 3.141592 3 | return pi*angle_degrees/180 4 | 5 | def rad_to_deg(angle_radians): 6 | pi = 3.141592 7 | return angle_radians*180/pi --------------------------------------------------------------------------------