├── .circleci
└── config.yml
├── .github
├── dependabot.yml
├── release.yml
└── workflows
│ └── build-and-publish-to-pypi.yml
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── docs
├── images
│ ├── set_grid.png
│ ├── set_point_psd.png
│ └── set_point_ts.png
├── plot_grid_SET.ipynb
├── plot_point_SET.ipynb
└── requirements.txt
├── pyproject.toml
├── requirements.txt
├── src
└── pysolid
│ ├── __init__.py
│ ├── grid.py
│ ├── point.py
│ └── solid.for
└── tests
├── grid.py
├── point.py
└── requirements.txt
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | # The python orb contains a set of prepackaged CircleCI configuration you can use repeatedly in your configuration files
5 | # Orb commands and jobs help you with common scripting around a language/tool
6 | # so you dont have to copy and paste it everywhere.
7 | # See the orb documentation here: https://circleci.com/developer/orbs/orb/circleci/python
8 | python: circleci/python@1.2
9 |
10 | workflows:
11 | build-and-test: # This is the name of the workflow, feel free to change it to better match your workflow.
12 | # Inside the workflow, you define the jobs you want to run.
13 | # For more details on extending your workflow, see the configuration docs: https://circleci.com/docs/2.0/configuration-reference/#workflows
14 | jobs:
15 | - build-and-test
16 |
17 |
18 | jobs:
19 | build-and-test: # This is the name of the job, feel free to change it to better match what you're trying to do!
20 | # These next lines defines a Docker executors: https://circleci.com/docs/2.0/executor-types/
21 | # You can specify an image from Dockerhub or use one of the convenience images from CircleCI's Developer Hub
22 | # A list of available CircleCI Docker convenience images are available here: https://circleci.com/developer/images/image/cimg/python
23 | # The executor is the environment in which the steps below will be executed - below will use a python 3.9 container
24 | # Change the version below to your required version of python
25 | docker:
26 | - image: cimg/base:current
27 | environment:
28 | CONDA_PREFIX: /root/tools/miniforge
29 | PYSOLID_HOME: /root/tools/PySolid
30 | user: root
31 | working_directory: /root/tools/PySolid
32 | # Checkout the code as the first step. This is a dedicated CircleCI step.
33 | steps:
34 | - checkout
35 | - run:
36 | name: Setting Environment with Miniforge
37 | command: |
38 | apt update
39 | apt-get update --yes && apt-get upgrade --yes
40 | apt-get install --yes wget git
41 | # download and install miniforge
42 | mkdir -p ${HOME}/tools
43 | cd ${HOME}/tools
44 | wget https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh
45 | bash Miniforge3-Linux-x86_64.sh -b -p ${HOME}/tools/miniforge
46 | ${HOME}/tools/miniforge/bin/mamba init bash
47 | # modify/export env var PATH to BASH_ENV to be shared across run steps
48 | echo 'export PATH=${CONDA_PREFIX}/bin:${PATH}' >> ${BASH_ENV}
49 |
50 | - run:
51 | name: Installing PySolid
52 | no_output_timeout: 10m
53 | command: |
54 | export PYTHONUNBUFFERED=1
55 | # install dependencies and source code
56 | mamba install --verbose --yes fortran-compiler --file ${PYSOLID_HOME}/requirements.txt --file ${PYSOLID_HOME}/tests/requirements.txt
57 | python -m pip install ${PYSOLID_HOME}
58 |
59 | - run:
60 | name: Unit Test
61 | command: |
62 | # run tests
63 | python ${PYSOLID_HOME}/tests/point.py
64 | python ${PYSOLID_HOME}/tests/grid.py
65 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "github-actions" # see doc for possible values
9 | directory: "/" # location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | # .github/release.yml
2 |
3 | changelog:
4 | exclude:
5 | authors:
6 | - dependabot
7 | - pre-commit-ci
8 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-publish-to-pypi.yml:
--------------------------------------------------------------------------------
1 | name: Publish 📦 to PyPI
2 |
3 | # Build on every branch push, tag push, and pull request change:
4 | on:
5 | push:
6 | branches:
7 | - main
8 | tags:
9 | - v*
10 | pull_request:
11 |
12 | jobs:
13 | build_wheels:
14 | name: Build 🐍 wheels 📦 on ${{ matrix.os }}
15 | runs-on: ${{ matrix.os }}
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | # skip building wheel for windows as it's not working yet
20 | os: [ubuntu-latest, macos-13] #windows-2019
21 |
22 | steps:
23 | - uses: actions/checkout@v4
24 | with:
25 | fetch-depth: 0
26 |
27 | - name: Provide gfortran (macOS-13)
28 | if: runner.os == 'macOS'
29 | run: |
30 | # https://github.com/actions/virtual-environments/issues/2524
31 | # https://github.com/cbg-ethz/dce/blob/master/.github/workflows/pkgdown.yaml
32 | sudo ln -s /usr/local/bin/gfortran-13 /usr/local/bin/gfortran
33 | sudo mkdir /usr/local/gfortran
34 | sudo ln -s /usr/local/Cellar/gcc@13/*/lib/gcc/13 /usr/local/gfortran/lib
35 | gfortran --version
36 |
37 | - name: Provide gfortran (Windows)
38 | if: runner.os == 'Windows'
39 | uses: msys2/setup-msys2@v2
40 |
41 | - name: Tell distutils to use mingw (Windows)
42 | if: runner.os == 'Windows'
43 | run: |
44 | echo "[build]`ncompiler=mingw32" | Out-File -Encoding ASCII ~/pydistutils.cfg
45 |
46 | - name: Build wheels
47 | uses: pypa/cibuildwheel@v2.21.3
48 | env:
49 | # Disable building for PyPy and 32bit.
50 | CIBW_SKIP: pp* *-win32 *-manylinux_i686
51 | CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET="13.0"
52 | CIBW_BEFORE_BUILD_MACOS: python -m pip install --upgrade pip
53 | # Package the DLL dependencies in the wheel for windows (done by default for the other platforms).
54 | # delvewheel cannot mangle the libraries, stripping does not work.
55 | CIBW_BEFORE_BUILD_WINDOWS: pip install delvewheel
56 | CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel show {wheel} && delvewheel repair -w {dest_dir} {wheel} --no-mangle-all"
57 |
58 | - uses: actions/upload-artifact@v4
59 | with:
60 | name: artifact-wheels-${{ matrix.os }}
61 | path: ./wheelhouse/*.whl
62 |
63 | build_sdist:
64 | name: Build 🐍 source distribution 📦
65 | runs-on: ubuntu-latest
66 | steps:
67 | - uses: actions/checkout@v4
68 | with:
69 | fetch-depth: 0
70 |
71 | - name: Build sdist
72 | run: pipx run build --sdist
73 |
74 | - uses: actions/upload-artifact@v4
75 | with:
76 | name: artifact-source
77 | path: dist/*.tar.gz
78 |
79 | upload_pypi:
80 | name: Upload 📦 to PyPI
81 | needs: [build_wheels, build_sdist]
82 | runs-on: ubuntu-latest
83 | if: github.repository_owner == 'insarlab' && github.event_name == 'push'
84 | steps:
85 | - uses: actions/download-artifact@v4
86 | with:
87 | # unpacks default artifact into dist/
88 | # if `name: artifact` is omitted, the action will create extra parent dir
89 | path: dist
90 | pattern: artifact-*
91 | merge-multiple: true
92 | - name: Display structure of downloaded files
93 | run: ls -R dist
94 |
95 | - name: Publish developed version 📦 to Test PyPI
96 | uses: pypa/gh-action-pypi-publish@release/v1
97 | with:
98 | user: __token__
99 | password: ${{ secrets.TEST_PYPI_API_TOKEN }}
100 | repository-url: https://test.pypi.org/legacy/
101 | skip-existing: false
102 | verbose: true
103 |
104 | - name: Publish released version 📦 to PyPI
105 | uses: pypa/gh-action-pypi-publish@release/v1
106 | if: startsWith(github.ref, 'refs/tags/v')
107 | with:
108 | user: __token__
109 | password: ${{ secrets.PYPI_API_TOKEN }}
110 | verbose: true
111 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | tests/*/*.png
3 | solid.txt
4 |
5 | # Byte-compiled / optimized / DLL files
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 |
10 | ## C extensions
11 | *.so
12 |
13 | # Distribution / packaging
14 | .Python
15 | build/
16 | develop-eggs/
17 | dist/
18 | downloads/
19 | eggs/
20 | .eggs/
21 | lib/
22 | lib64/
23 | parts/
24 | sdist/
25 | var/
26 | wheels/
27 | pip-wheel-metadata/
28 | share/python-wheels/
29 | *.egg-info/
30 | .installed.cfg
31 | *.egg
32 | MANIFEST
33 |
34 | # PyInstaller
35 | # Usually these files are written by a python script from a template
36 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
37 | *.manifest
38 | *.spec
39 |
40 | # Installer logs
41 | pip-log.txt
42 | pip-delete-this-directory.txt
43 |
44 | # Unit test / coverage reports
45 | htmlcov/
46 | .tox/
47 | .nox/
48 | .coverage
49 | .coverage.*
50 | .cache
51 | nosetests.xml
52 | coverage.xml
53 | *.cover
54 | *.py,cover
55 | .hypothesis/
56 | .pytest_cache/
57 |
58 | # Translations
59 | *.mo
60 | *.pot
61 |
62 | # Django stuff:
63 | *.log
64 | local_settings.py
65 | db.sqlite3
66 | db.sqlite3-journal
67 |
68 | # Flask stuff:
69 | instance/
70 | .webassets-cache
71 |
72 | # Scrapy stuff:
73 | .scrapy
74 |
75 | # Sphinx documentation
76 | docs/_build/
77 |
78 | # PyBuilder
79 | target/
80 |
81 | # Jupyter Notebook
82 | .ipynb_checkpoints
83 |
84 | # IPython
85 | profile_default/
86 | ipython_config.py
87 |
88 | # pyenv
89 | .python-version
90 |
91 | # pipenv
92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
95 | # install all needed dependencies.
96 | #Pipfile.lock
97 |
98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
99 | __pypackages__/
100 |
101 | # Celery stuff
102 | celerybeat-schedule
103 | celerybeat.pid
104 |
105 | # SageMath parsed files
106 | *.sage.py
107 |
108 | # Environments
109 | .env
110 | .venv
111 | env/
112 | venv/
113 | ENV/
114 | env.bak/
115 | venv.bak/
116 |
117 | # Spyder project settings
118 | .spyderproject
119 | .spyproject
120 |
121 | # Rope project settings
122 | .ropeproject
123 |
124 | # mkdocs documentation
125 | /site
126 |
127 | # mypy
128 | .mypy_cache/
129 | .dmypy.json
130 | dmypy.json
131 |
132 | # Pyre type checker
133 | .pyre/
134 |
135 | # Scikit-build temp directory
136 | /_skbuild/
137 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Based on:
2 | # https://github.com/scikit-build/scikit-build-core/tree/main/docs/examples/getting_started/fortran
3 |
4 | cmake_minimum_required(VERSION 3.17.2...3.29)
5 | project(${SKBUILD_PROJECT_NAME} LANGUAGES C Fortran)
6 |
7 | find_package(
8 | Python
9 | COMPONENTS Interpreter Development.Module NumPy
10 | REQUIRED)
11 |
12 | # F2PY headers
13 | execute_process(
14 | COMMAND "${PYTHON_EXECUTABLE}" -c
15 | "import numpy.f2py; print(numpy.f2py.get_include())"
16 | OUTPUT_VARIABLE F2PY_INCLUDE_DIR
17 | OUTPUT_STRIP_TRAILING_WHITESPACE)
18 |
19 | add_library(fortranobject OBJECT "${F2PY_INCLUDE_DIR}/fortranobject.c")
20 | target_link_libraries(fortranobject PUBLIC Python::NumPy)
21 | target_include_directories(fortranobject PUBLIC "${F2PY_INCLUDE_DIR}")
22 | set_property(TARGET fortranobject PROPERTY POSITION_INDEPENDENT_CODE ON)
23 |
24 | add_custom_command(
25 | OUTPUT solidmodule.c solid-f2pywrappers.f
26 | DEPENDS src/pysolid/solid.for
27 | VERBATIM
28 | COMMAND "${Python_EXECUTABLE}" -m numpy.f2py
29 | "${CMAKE_CURRENT_SOURCE_DIR}/src/pysolid/solid.for" -m solid --lower)
30 |
31 | python_add_library(
32 | solid MODULE "${CMAKE_CURRENT_BINARY_DIR}/solidmodule.c"
33 | "${CMAKE_CURRENT_BINARY_DIR}/solid-f2pywrappers.f"
34 | "${CMAKE_CURRENT_SOURCE_DIR}/src/pysolid/solid.for" WITH_SOABI)
35 | target_link_libraries(solid PRIVATE fortranobject)
36 |
37 | install(TARGETS solid DESTINATION pysolid)
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.python.org/)
2 | [](https://circleci.com/gh/insarlab/PySolid)
3 | [](https://anaconda.org/conda-forge/pysolid)
4 | [](https://github.com/insarlab/PySolid/releases)
5 | [](https://github.com/insarlab/PySolid/blob/main/LICENSE)
6 | [](https://doi.org/10.1109/TGRS.2022.3168509)
7 |
8 | ## PySolid
9 |
10 | The Python based solid Earth tides (PySolid) is a thin Python wrapper of the [`solid.for`](http://geodesyworld.github.io/SOFTS/solid.htm) program (by Dennis Milbert based on [_dehanttideinelMJD.f_](https://iers-conventions.obspm.fr/content/chapter7/software/dehanttideinel/) from V. Dehant, S. Mathews, J. Gipson and C. Bruyninx) to calculate [solid Earth tides](https://en.wikipedia.org/wiki/Earth_tide) in east, north and up directions (section 7.1.1 in the [2010 IERS Conventions](https://www.iers.org/IERS/EN/Publications/TechnicalNotes/tn36.html)). Solid Earth tides introduce large offsets in SAR observations and long spatial wavelength ramps in InSAR observations, as shown in the Sentinel-1 data with regular acquisitions and large swaths ([Yunjun et al., 2022](https://doi.org/10.1109/TGRS.2022.3168509)).
11 |
12 | This is research code provided to you "as is" with NO WARRANTIES OF CORRECTNESS. Use at your own risk.
13 |
14 | ### 1. Install
15 |
16 | PySolid is available on the [conda-forge](https://anaconda.org/conda-forge/pysolid) channel and the main archive of the [Debian](https://tracker.debian.org/pkg/pysolid) GNU/Linux OS. The released version can be installed via `conda` as:
17 |
18 | ```shell
19 | # run "conda update pysolid" to update the installed version
20 | conda install -c conda-forge pysolid
21 | ```
22 |
23 | or via `apt` (or other package managers) for [Debian-derivative OS](https://wiki.debian.org/Derivatives/Census) users, including [Ubuntu](https://ubuntu.com), as:
24 |
25 | ```shell
26 | apt install python3-pysolid
27 | ```
28 |
29 |
30 | Or build from source:
31 |
32 | PySolid relies on a few Python modules as described in [requirements.txt](./requirements.txt) and [NumPy's f2py](https://numpy.org/doc/stable/f2py/) to build the Fortran source code. You could use `conda` to install all the dependencies, including the Fortran compiler, or use your own installed Fortran compiler and `pip` to install the rest.
33 |
34 | ##### a. Download source code
35 |
36 | ```bash
37 | # run "cd PySolid; git pull" to update to the latest development version
38 | git clone https://github.com/insarlab/PySolid.git
39 | ```
40 |
41 | ##### b. Install dependencies
42 |
43 | ```bash
44 | # option 1: use conda to install dependencies into an existing, activated environment
45 | conda install -c conda-forge fortran-compiler --file PySolid/requirements.txt --file PySolid/tests/requirements.txt
46 |
47 | # option 2: use conda to install dependencies into a new environment, e.g. named "pysolid"
48 | conda create --name pysolid fortran-compiler --file PySolid/requirements.txt --file PySolid/tests/requirements.txt
49 | conda activate pysolid
50 |
51 | # option 3: have a Fortran compiler already installed and use pip to install the dependencies
52 | python -m pip install -r PySolid/requirements.txt -r PySolid/tests/requirements.txt
53 | ```
54 |
55 | ##### c. Install PySolid
56 |
57 | ```bash
58 | # option 1: use pip to install pysolid into the current environment
59 | python -m pip install ./PySolid
60 |
61 | # option 2: use pip to install pysolid in develop mode (editable) into the current environment
62 | python -m pip install -e ./PySolid
63 |
64 | # option 3: manually compile the Fortran code and setup environment variable
65 | cd PySolid/src/pysolid
66 | f2py -c -m solid solid.for
67 | # Replace with proper path to PySolid main folder
68 | export PYTHONPATH=${PYTHONPATH}:/PySolid/src
69 | ```
70 |
71 | ##### d. Test the installation
72 |
73 | To test the installation, run the following:
74 |
75 | ```bash
76 | python -c "import pysolid; print(pysolid.__version__)"
77 | python PySolid/tests/grid.py
78 | python PySolid/tests/point.py
79 | ```
80 |
81 |
82 | ### 2. Usage
83 |
84 | PySolid could compute solid Earth tides in two modes: **point** and **grid**. Both modes produce displacement in east, north and up directions.
85 |
86 | + **Point mode:** compute 1D tides time-series at a specific point for a given time period
87 | + **Grid mode:** compute 2D tides grid at a specific time for a given spatial grid
88 |
89 | #### 2.1 Point Mode [[notebook](./docs/plot_point_SET.ipynb)]
90 |
91 | ```python
92 | import datetime as dt
93 | import pysolid
94 |
95 | # prepare inputs
96 | lat, lon = 34.0, -118.0 # point of interest in degree, Los Angles, CA
97 | step_sec = 60 * 5 # sample spacing in time domain in seconds
98 | dt0 = dt.datetime(2020, 1, 1, 4, 0, 0) # start date and time
99 | dt1 = dt.datetime(2021, 1, 1, 2, 0, 0) # end date and time
100 |
101 | # compute SET via pysolid
102 | dt_out, tide_e, tide_n, tide_u = pysolid.calc_solid_earth_tides_point(
103 | lat, lon, dt0, dt1,
104 | step_sec=step_sec,
105 | display=False,
106 | verbose=False,
107 | )
108 |
109 | # plot the power spectral density of SET up component
110 | pysolid.plot_power_spectral_density4tides(tide_u, sample_spacing=step_sec)
111 | ```
112 |
113 |
114 |
115 |
116 |
117 |
118 | #### 2.2 Grid Mode [[notebook](./docs/plot_grid_SET.ipynb)]
119 |
120 | ```python
121 | import datetime as dt
122 | import numpy as np
123 | import pysolid
124 |
125 | # prepare inputs
126 | dt_obj = dt.datetime(2020, 12, 25, 14, 7, 44)
127 | meta = {
128 | 'LENGTH' : 500, # number of rows
129 | 'WIDTH' : 450, # number of columns
130 | 'X_FIRST': -126, # min longitude in degree (upper left corner of the upper left pixel)
131 | 'Y_FIRST': 43, # max laitude in degree (upper left corner of the upper left pixel)
132 | 'X_STEP' : 0.000925926 * 30, # output resolution in degree
133 | 'Y_STEP' : -0.000925926 * 30, # output resolution in degree
134 | }
135 |
136 | # compute SET via pysolid
137 | tide_e, tide_n, tide_u = pysolid.calc_solid_earth_tides_grid(
138 | dt_obj, meta,
139 | display=False,
140 | verbose=True,
141 | )
142 |
143 | # project SET from ENU to satellite line-of-sight (LOS) direction with positive for motion towards the satellite
144 | # inc_angle : incidence angle of the LOS vector (from ground to radar platform) measured from vertical.
145 | # az_angle : azimuth angle of the LOS vector (from ground to radar platform) measured from the north, with anti-clockwirse as positive.
146 | inc_angle = np.deg2rad(34) # radian, typical value for Sentinel-1
147 | az_angle = np.deg2rad(-102) # radian, typical value for Sentinel-1 descending track
148 | tide_los = ( tide_e * np.sin(inc_angle) * np.sin(az_angle) * -1
149 | + tide_n * np.sin(inc_angle) * np.cos(az_angle)
150 | + tide_u * np.cos(inc_angle))
151 | ```
152 |
153 |
154 |
155 |
156 |
157 | ### 3. Citing this work
158 |
159 | + Yunjun, Z., Fattahi, H., Pi, X., Rosen, P., Simons, M., Agram, P., & Aoki, Y. (2022). Range Geolocation Accuracy of C-/L-band SAR and its Implications for Operational Stack Coregistration. _IEEE Trans. Geosci. Remote Sens., 60_, 5227219. [ [doi](https://doi.org/10.1109/TGRS.2022.3168509) \| [arxiv](https://doi.org/10.31223/X5F641) \| [data](https://doi.org/10.5281/zenodo.6360749) \| [notebook](https://github.com/yunjunz/2022-Geolocation) ]
160 | + Milbert, D. (2018), "solid: Solid Earth Tide", [Online]. Available: http://geodesyworld.github.io/SOFTS/solid.htm. Accessd on: 2020-09-06.
161 |
--------------------------------------------------------------------------------
/docs/images/set_grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insarlab/PySolid/9ac869b05a44057de193c4a71fa2a35891db8b1e/docs/images/set_grid.png
--------------------------------------------------------------------------------
/docs/images/set_point_psd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insarlab/PySolid/9ac869b05a44057de193c4a71fa2a35891db8b1e/docs/images/set_point_psd.png
--------------------------------------------------------------------------------
/docs/images/set_point_ts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/insarlab/PySolid/9ac869b05a44057de193c4a71fa2a35891db8b1e/docs/images/set_point_ts.png
--------------------------------------------------------------------------------
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | # extra dependencies required for running notebooks
2 | cartopy
3 | matplotlib
4 | mintpy
5 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["cmake", "scikit-build-core", "setuptools", "setuptools_scm[toml]>=6.2", "numpy", "wheel"]
3 | build-backend = "scikit_build_core.build"
4 |
5 | [project]
6 | name = "pysolid"
7 | description = "A Python wrapper for solid to compute solid Earth tides"
8 | authors = [
9 | {name="Zhang Yunjun", email="yunjunz@outlook.com"},
10 | {name="Dennis Milbert"},
11 | ]
12 | readme = "README.md"
13 | requires-python = ">=3.8"
14 | dependencies = [
15 | "numpy",
16 | "scipy",
17 | ]
18 |
19 | keywords = ["solid Earth tides", "deformation", "geodesy", "geophysics"]
20 | license = {text = "GPL-3.0-or-later"}
21 | classifiers = [
22 | "Development Status :: 4 - Beta",
23 | "Intended Audience :: Science/Research",
24 | "Topic :: Scientific/Engineering",
25 | "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
26 | "Operating System :: OS Independent",
27 | "Programming Language :: Python :: 3",
28 | ]
29 |
30 | # see section: setuptools_scm
31 | # https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata
32 | # dependencies will be read from text files
33 | dynamic = ["version"]
34 |
35 | [project.urls]
36 | "Homepage" = "https://github.com/insarlab/PySolid"
37 | "Bug Tracker" = "https://github.com/insarlab/PySolid/issues"
38 |
39 | [tool.setuptools]
40 | include-package-data = true
41 | zip-safe = false
42 |
43 | [tool.setuptools.packages.find]
44 | where = ["src"]
45 |
46 | [tool.setuptools.package-data]
47 | pysolid = ["*.for"]
48 |
49 | [tool.setuptools_scm]
50 | version_scheme = "post-release"
51 | local_scheme = "no-local-version"
52 |
53 | [tool.scikit-build]
54 | metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
55 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # for running
2 | numpy
3 | scipy
4 |
--------------------------------------------------------------------------------
/src/pysolid/__init__.py:
--------------------------------------------------------------------------------
1 | from importlib.metadata import PackageNotFoundError, version
2 |
3 | # get version info
4 | try:
5 | __version__ = version(__name__)
6 | except PackageNotFoundError:
7 | print('package is not installed!\n'
8 | 'Please follow the installation instructions in the README.md.\n'
9 | 'Or, to just get the version number, use:\n'
10 | ' python -m setuptools_scm')
11 |
12 |
13 | # top-level functions
14 | from pysolid.grid import (
15 | calc_solid_earth_tides_grid,
16 | plot_solid_earth_tides_grid,
17 | )
18 | from pysolid.point import (
19 | TIDES,
20 | calc_solid_earth_tides_point,
21 | plot_solid_earth_tides_point,
22 | plot_power_spectral_density4tides,
23 | )
24 |
25 | __all__ = [
26 | '__version__',
27 | 'calc_solid_earth_tides_grid',
28 | 'plot_solid_earth_tides_grid',
29 | 'TIDES',
30 | 'calc_solid_earth_tides_point',
31 | 'plot_solid_earth_tides_point',
32 | 'plot_power_spectral_density4tides',
33 | ]
34 |
--------------------------------------------------------------------------------
/src/pysolid/grid.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #######################################################################
3 | # A Python wrapper for solid Earth tides calculation using solid.for.
4 | # Fortran code is originally written by Dennis Milbert, 2018-06-01.
5 | # Available at: http://geodesyworld.github.io/SOFTS/solid.htm.
6 | # Author: Zhang Yunjun, Simran Sangha, Sep 2020
7 | # Copyright 2020, by the California Institute of Technology.
8 | #######################################################################
9 | # Recommend usage:
10 | # import pysolid
11 | # pysolid.calc_solid_earth_tides_grid()
12 |
13 |
14 | import os
15 |
16 | import numpy as np
17 | from scipy import ndimage
18 |
19 |
20 | ################################## Earth tides - grid mode ###################################
21 | def calc_solid_earth_tides_grid(dt_obj, atr, step_size=1e3, display=False, verbose=True):
22 | """Calculate SET in east/north/up direction for a spatial grid at a given date/time.
23 |
24 | Note that we use step_size to speedup >30 times, by feeding the Fortran code (SET calc and
25 | text file writing) the coarse grid, then resize the output to the same shape as the original
26 | input size. This uses the fact that SET varies slowly in space. Comparison w and w/o step_size
27 | shows a difference in tide_u with max of 5e-8 m, thus negligible.
28 |
29 | Parameters: dt_obj - datetime.datetime object (with precision up to the second)
30 | atr - dict, metadata including the following keys:
31 | LENGTH/WIDTTH
32 | X/Y_FIRST
33 | X/Y_STEP
34 | step_size - float, grid step feeded into the fortran code in meters
35 | to speedup the calculation
36 | display - bool, plot the calculated SET
37 | verbose - bool, print verbose message
38 | Returns: tide_e - 2D np.ndarray, SET in east direction in meters
39 | tide_n - 2D np.ndarray, SET in north direction in meters
40 | tide_u - 2D np.ndarray, SET in up direction in meters
41 | Examples: atr = readfile.read_attribute('geo_velocity.h5')
42 | tide_e, tide_n, tide_u = calc_solid_earth_tides_grid('20180219', atr)
43 | """
44 | try:
45 | from pysolid.solid import solid_grid
46 | except ImportError:
47 | msg = "Cannot import name 'solid' from 'pysolid'!"
48 | msg += '\n Maybe solid.for is NOT compiled yet.'
49 | msg += '\n Check instruction at: https://github.com/insarlab/PySolid.'
50 | raise ImportError(msg)
51 |
52 | vprint = print if verbose else lambda *args, **kwargs: None
53 |
54 | # location
55 | lat0 = float(atr['Y_FIRST'])
56 | lon0 = float(atr['X_FIRST'])
57 | lat1 = lat0 + float(atr['Y_STEP']) * int(atr['LENGTH'])
58 | lon1 = lon0 + float(atr['X_STEP']) * int(atr['WIDTH'])
59 |
60 | vprint('PYSOLID: ----------------------------------------')
61 | vprint('PYSOLID: datetime: {}'.format(dt_obj.isoformat()))
62 | vprint('PYSOLID: SNWE: {}'.format((lat1, lat0, lon0, lon1)))
63 |
64 | # step size
65 | num_step = int(step_size / 108e3 / abs(float(atr['Y_STEP'])))
66 | num_step = max(1, num_step)
67 | length = np.rint(int(atr['LENGTH']) / num_step - 1e-4).astype(int)
68 | width = np.rint(int(atr['WIDTH']) / num_step - 1e-4).astype(int)
69 | lat_step = float(atr['Y_STEP']) * num_step
70 | lon_step = float(atr['X_STEP']) * num_step
71 | vprint('SOLID : calculate solid Earth tides in east/north/up direction')
72 | vprint('SOLID : shape: {s}, step size: {la:.4f} by {lo:.4f} deg'.format(
73 | s=(length, width), la=lat_step, lo=lon_step))
74 |
75 | ## calc solid Earth tides
76 | tide_e, tide_n, tide_u = solid_grid(dt_obj.year, dt_obj.month, dt_obj.day,
77 | dt_obj.hour, dt_obj.minute, dt_obj.second,
78 | lat0, lat_step, length,
79 | lon0, lon_step, width)
80 |
81 | # resample to the input size
82 | # via scipy.ndimage.zoom or skimage.transform.resize
83 | if num_step > 1:
84 | in_shape = tide_e.shape
85 | out_shape = (int(atr['LENGTH']), int(atr['WIDTH']))
86 | vprint('PYSOLID: resize data to the shape of {} using order-1 spline interpolation'.format(out_shape))
87 |
88 | enu = np.stack([tide_e, tide_n, tide_u])
89 | zoom_factors = [1, *np.divide(out_shape, in_shape)]
90 | kwargs = dict(order=1, mode="nearest", grid_mode=True)
91 | tide_e, tide_n, tide_u = ndimage.zoom(enu, zoom_factors, **kwargs)
92 |
93 | # plot
94 | if display:
95 | plot_solid_earth_tides_grid(tide_e, tide_n, tide_u, dt_obj)
96 |
97 | return tide_e, tide_n, tide_u
98 |
99 |
100 | ######################################### Plot ###############################################
101 | def plot_solid_earth_tides_grid(tide_e, tide_n, tide_u, dt_obj=None,
102 | out_fig=None, save=False, display=True):
103 | """Plot the solid Earth tides in ENU direction."""
104 | from matplotlib import pyplot as plt, ticker
105 |
106 | # plot
107 | fig, axs = plt.subplots(nrows=1, ncols=3, figsize=[6, 3], sharex=True, sharey=True)
108 | for ax, data, label in zip(axs.flatten(),
109 | [tide_e, tide_n, tide_u],
110 | ['East', 'North', 'Up']):
111 | im = ax.imshow(data*100, cmap='RdBu')
112 | ax.tick_params(which='both', direction='in', bottom=True, top=True, left=True, right=True)
113 | fig.colorbar(im, ax=ax, orientation='horizontal', label=label+' [cm]', pad=0.1, ticks=ticker.MaxNLocator(3))
114 | fig.tight_layout()
115 |
116 | # super title
117 | if dt_obj is not None:
118 | axs[1].set_title('solid Earth tides at {}'.format(dt_obj.isoformat()), fontsize=12)
119 |
120 | # output
121 | if out_fig:
122 | save = True
123 |
124 | if save:
125 | if not out_fig:
126 | out_fig = os.path.abspath('SET_grid.png')
127 | print('save figure to {}'.format(out_fig))
128 | fig.savefig(out_fig, bbox_inches='tight', transparent=True, dpi=300)
129 |
130 | if display:
131 | print('showing...')
132 | plt.show()
133 | else:
134 | plt.close()
135 |
136 | return
137 |
--------------------------------------------------------------------------------
/src/pysolid/point.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #######################################################################
3 | # A Python wrapper for solid Earth tides calculation using solid.for.
4 | # Fortran code is originally written by Dennis Milbert, 2018-06-01.
5 | # Available at: http://geodesyworld.github.io/SOFTS/solid.htm.
6 | # Author: Zhang Yunjun, Jan 2021
7 | # Copyright 2020, by the California Institute of Technology.
8 | #######################################################################
9 | # Recommend usage:
10 | # import pysolid
11 | # pysolid.calc_solid_earth_tides_point()
12 |
13 |
14 | import collections
15 | import datetime as dt
16 | import os
17 |
18 | import numpy as np
19 |
20 |
21 | ## Tidal constituents
22 | # https://en.wikipedia.org/wiki/Theory_of_tides#Tidal_constituents. Accessed on: 2022-03-07.
23 | # unit: period (hour), speed (deg per hour)
24 | Tag = collections.namedtuple('Tag', 'species symbol period speed doodson_num noaa_order')
25 | TIDES = (
26 | # Semi-diurnal
27 | Tag('Principal lunar semidiurnal' , r'$M_2$' , 12.4206012 , 28.9841042, 255.555, 1 ),
28 | Tag('Principal solar semidiurnal' , r'$S_2$' , 12.0 , 30.0 , 273.555, 2 ),
29 | Tag('Larger lunar elliptic semidiurnal' , r'$N_2$' , 12.65834751, 28.4397295, 245.655, 3 ),
30 | Tag('Larger lunar evectional' , r'$v_2$' , 12.62600509, 28.5125831, 247.455, 11),
31 | Tag('Variational' , r'$\mu_2$' , 12.8717576 , 27.9682084, 237.555, 13),
32 | Tag('Lunar elliptical semidiurnal second-order', '2"N'+r'$_2$' , 12.90537297, 27.8953548, 235.755, 14),
33 | Tag('Smaller lunar evectional' , r'$\lambda_2$', 12.22177348, 29.4556253, 263.655, 16),
34 | Tag('Larger solar elliptic' , r'$T_2$' , 12.01644934, 29.9589333, 272.555, 27),
35 | Tag('Smaller solar elliptic' , r'$R_2$' , 11.98359564, 30.0410667, 274.555, 28),
36 | Tag('Shallow water semidiurnal' , r'$2SM_2$' , 11.60695157, 31.0158958, 291.555, 31),
37 | Tag('Smaller lunar elliptic semidiurnal' , r'$L_2$' , 12.19162085, 29.5284789, 265.455, 33),
38 | Tag('Lunisolar semidiurnal' , r'$K_2$' , 11.96723606, 30.0821373, 275.555, 35),
39 |
40 | # Diurnal
41 | Tag('Lunar diurnal' , r'$K_1$' , 23.93447213, 15.0410686, 165.555, 4 ),
42 | Tag('Lunar diurnal' , r'$O_1$' , 25.81933871, 13.9430356, 145.555, 6 ),
43 | Tag('Lunar diurnal' , r'$OO_1$', 22.30608083, 16.1391017, 185.555, 15),
44 | Tag('Solar diurnal' , r'$S_1$' , 24.0 , 15.0 , 164.555, 17),
45 | Tag('Smaller lunar elliptic diurnal' , r'$M_1$' , 24.84120241, 14.4920521, 155.555, 18),
46 | Tag('Smaller lunar elliptic diurnal' , r'$J_1$' , 23.09848146, 15.5854433, 175.455, 19),
47 | Tag('Larger lunar evectional diurnal', r'$\rho$', 26.72305326, 13.4715145, 137.455, 25),
48 | Tag('Larger lunar elliptic diurnal' , r'$Q_1$' , 26.868350 , 13.3986609, 135.655, 26),
49 | Tag('Larger elliptic diurnal' , r'$2Q_1$', 28.00621204, 12.8542862, 125.755, 29),
50 | Tag('Solar diurnal' , r'$P_1$' , 24.06588766, 14.9589314, 163.555, 30),
51 |
52 | # Long period
53 | Tag('Lunar monthly' , r'$M_m$' , 661.3111655, 0.5443747, 65.455, 20), # period 27.554631896 days
54 | Tag('Solar semiannual' , r'$S_{sa}$', 4383.076325 , 0.0821373, 57.555, 21), # period 182.628180208 days
55 | Tag('Solar annual' , r'$S_a$' , 8766.15265 , 0.0410686, 56.555, 22), # period 365.256360417 days
56 | Tag('Lunisolar synodic fortnightly' , r'$MS_f$' , 354.3670666, 1.0158958, 73.555, 23), # period 14.765294442 days
57 | Tag('Lunisolar fortnightly' , r'$M_f$' , 327.8599387, 1.0980331, 75.555, 24), # period 13.660830779 days
58 |
59 | # Short period
60 | Tag('Shallow water overtides of principal lunar', r'$M_4$' , 6.210300601, 57.9682084, 455.555, 5 ),
61 | Tag('Shallow water overtides of principal lunar', r'$M_6$' , 4.140200401, 86.9523127, 655.555, 7 ),
62 | Tag('Shallow water terdiurnal' , r'$MK_3$' , 8.177140247, 44.0251729, 365.555, 8 ),
63 | Tag('Shallow water overtides of principal solar', r'$S_4$' , 6.0 , 60.0 , 491.555, 9 ),
64 | Tag('Shallow water quarter diurnal' , r'$MN_4$' , 6.269173724, 57.4238337, 445.655, 10),
65 | Tag('Shallow water overtides of principal solar', r'$S_6$' , 4.0 , 90.0 , np.nan , 12),
66 | Tag('Lunar terdiurnal' , r'$M_3$' , 8.280400802, 43.4761563, 355.555, 32),
67 | Tag('Shallow water terdiurnal' , '2"MK'+r'$_3$', 8.38630265 , 42.9271398, 345.555, 34),
68 | Tag('Shallow water eighth diurnal' , r'$M_8$' , 3.105150301, 115.9364166, 855.555, 36),
69 | Tag('Shallow water quarter diurnal' , r'$MS_4$' , 6.103339275, 58.9841042, 473.555, 37),
70 | )
71 |
72 |
73 | ################################## Earth tides - point mode ##################################
74 | def calc_solid_earth_tides_point(lat, lon, dt0, dt1, step_sec=60, display=False, verbose=True):
75 | """Calculate SET in east/north/up direction for the given time period at the given point (lat/lon).
76 |
77 | Parameters: lat/lon - float32, latitude/longitude of the point of interest
78 | dt0/1 - datetime.datetime object, start/end date and time
79 | step_sec - int16, time step in seconds
80 | display - bool, plot the calculated SET
81 | verbose - bool, print verbose message
82 | Returns: dt_out - 1D np.ndarray in dt.datetime objects
83 | tide_e - 1D np.ndarray in float32, SET in east direction in meters
84 | tide_n - 1D np.ndarray in float32, SET in north direction in meters
85 | tide_u - 1D np.ndarray in float32, SET in up direction in meters
86 | Examples: dt0 = dt.datetime(2020,11,1,4,0,0)
87 | dt1 = dt.datetime(2020,12,31,2,0,0)
88 | (dt_out,
89 | tide_e,
90 | tide_n,
91 | tide_u) = calc_solid_earth_tides_point(34.0, -118.0, dt0, dt1)
92 | """
93 |
94 | print('PYSOLID: calculate solid Earth tides in east/north/up direction')
95 | print(f'PYSOLID: lot/lon: {lat}/{lon} degree')
96 | print(f'PYSOLID: start UTC: {dt0.isoformat()}')
97 | print(f'PYSOLID: end UTC: {dt1.isoformat()}')
98 | print(f'PYSOLID: time step: {step_sec} seconds')
99 |
100 | dt_out = []
101 | tide_e = []
102 | tide_n = []
103 | tide_u = []
104 |
105 | ndays = (dt1.date() - dt0.date()).days + 1
106 | for i in range(ndays):
107 | di = dt0.date() + dt.timedelta(days=i)
108 | if verbose:
109 | print(f'SOLID : {di.isoformat()} {i+1}/{ndays} ...')
110 |
111 | # calc tide_u/n/u for the whole day
112 | (dt_outi,
113 | tide_ei,
114 | tide_ni,
115 | tide_ui) = calc_solid_earth_tides_point_per_day(lat, lon,
116 | date_str=di.strftime('%Y%m%d'),
117 | step_sec=int(step_sec))
118 |
119 | # flag to mark the first/last datetime
120 | if i == 0:
121 | flag = dt_outi >= dt0
122 | elif i == ndays - 1:
123 | flag = dt_outi <= dt1
124 | else:
125 | flag = np.ones(dt_outi.size, dtype=np.bool_)
126 |
127 | # update
128 | dt_out += dt_outi[flag].tolist()
129 | tide_e += tide_ei[flag].tolist()
130 | tide_n += tide_ni[flag].tolist()
131 | tide_u += tide_ui[flag].tolist()
132 |
133 | # list --> np.ndarray for output
134 | dt_out = np.array(dt_out)
135 | tide_e = np.array(tide_e)
136 | tide_n = np.array(tide_n)
137 | tide_u = np.array(tide_u)
138 |
139 | # plot
140 | if display:
141 | plot_solid_earth_tides_point(dt_out, tide_e, tide_n, tide_u, lalo=[lat, lon])
142 |
143 | return dt_out, tide_e, tide_n, tide_u
144 |
145 |
146 | def calc_solid_earth_tides_point_per_day(lat, lon, date_str, step_sec=60):
147 | """Calculate solid Earth tides (SET) in east/north/up direction
148 | for one day at the given point (lat/lon).
149 |
150 | Parameters: lat/lon - float32, latitude/longitude of the point of interest
151 | date_str - str, date in YYYYMMDD
152 | step_sec - int16, time step in seconds
153 | Returns: dt_out - 1D np.ndarray in dt.datetime objects
154 | tide_e - 1D np.ndarray in float32, SET in east direction in meters
155 | tide_n - 1D np.ndarray in float32, SET in north direction in meters
156 | tide_u - 1D np.ndarray in float32, SET in up direction in meters
157 | Examples: (dt_out,
158 | tide_e,
159 | tide_n,
160 | tide_u) = calc_solid_earth_tides_point_per_day(34.0, -118.0, '20180219')
161 | """
162 | try:
163 | from pysolid.solid import solid_point
164 | except ImportError:
165 | msg = "Cannot import name 'solid' from 'pysolid'!"
166 | msg += '\n Maybe solid.for is NOT compiled yet.'
167 | msg += '\n Check instruction at: https://github.com/insarlab/PySolid.'
168 | raise ImportError(msg)
169 |
170 | # calc solid Earth tides
171 | t = dt.datetime.strptime(date_str, '%Y%m%d')
172 | secs, tide_e, tide_n, tide_u = solid_point(
173 | lat, lon, t.year, t.month, t.day, step_sec
174 | )
175 |
176 | dt_out = [t + dt.timedelta(seconds=sec) for sec in secs]
177 | dt_out = np.array(dt_out)
178 |
179 | return dt_out, tide_e, tide_n, tide_u
180 |
181 |
182 | ######################################### Plot ###############################################
183 | def plot_solid_earth_tides_point(dt_out, tide_e, tide_n, tide_u, lalo=None,
184 | out_fig=None, save=False, display=True):
185 | """Plot the solid Earth tides at one point."""
186 | from matplotlib import pyplot as plt, dates as mdates
187 |
188 | # plot
189 | fig, axs = plt.subplots(nrows=3, ncols=1, figsize=[6, 4], sharex=True)
190 | for ax, data, label in zip(axs.flatten(),
191 | [tide_e, tide_n, tide_u],
192 | ['East [cm]', 'North [cm]', 'Up [cm]']):
193 | ax.plot(dt_out, data*100)
194 | ax.set_ylabel(label, fontsize=12)
195 |
196 | # axis format
197 | for ax in axs:
198 | ax.tick_params(which='both', direction='out', bottom=True, top=False, left=True, right=True)
199 | ax.xaxis.set_major_locator(mdates.MonthLocator())
200 | ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
201 | ax.xaxis.set_minor_locator(mdates.DayLocator())
202 |
203 | if lalo:
204 | axs[0].set_title('solid Earth tides at (N{}, E{})'.format(lalo[0], lalo[1]), fontsize=12)
205 | fig.tight_layout()
206 | fig.align_ylabels()
207 |
208 | # output
209 | if out_fig:
210 | save = True
211 |
212 | if save:
213 | if not out_fig:
214 | out_fig = os.path.abspath('SET_point.png')
215 | print('save figure to {}'.format(out_fig))
216 | fig.savefig(out_fig, bbox_inches='tight', transparent=True, dpi=300)
217 |
218 | if display:
219 | print('showing...')
220 | plt.show()
221 | else:
222 | plt.close()
223 |
224 | return
225 |
226 |
227 | def plot_power_spectral_density4tides(tide_ts, sample_spacing, out_fig=None, fig_dpi=300, min_psd=1500):
228 | """Plot the power spectral density (PSD) of tides time-series.
229 | Note: for accurate PSD analysis, a long time-series, e.g. one year, is recommended.
230 | """
231 | from matplotlib import pyplot as plt, ticker
232 | from scipy import signal
233 |
234 | ## calc PSD
235 | freq, psd = signal.periodogram(tide_ts, fs=1/sample_spacing, scaling='density')
236 | # get rid of zero in the first element
237 | psd = psd[1:]
238 | freq = freq[1:]
239 | period = 1./3600./freq # period (hour)
240 |
241 | ## plot
242 | fig, axs = plt.subplots(nrows=1, ncols=2, figsize=[8,3], sharey=True)
243 | for ax in axs:
244 | ax.plot(period, psd, '.', ms=16, mfc='none', mew=2)
245 |
246 | # axis format
247 | for ax in axs:
248 | ax.tick_params(which='both', direction='out', bottom=True, top=True, left=True, right=True)
249 | ax.xaxis.set_minor_locator(ticker.AutoMinorLocator())
250 | ax.set_xlabel('Period [hour]')
251 | axs[0].set_xlim(11.3, 13.2)
252 | axs[1].set_xlim(22.0, 28.0)
253 | ax = axs[0]
254 | ax.set_ylabel('Power Spectral Density\n'+r'[$m^2/Hz$]')
255 | ax.set_ylim(0, ymax=axs[0].get_ylim()[1] * 1.1)
256 | #ax.set_ylim(1e-1,5e6); plt.yscale('log')
257 | #ax.yaxis.set_major_locator(ticker.FixedLocator([0,50e3,100e3,150e3]))
258 | #ax.set_yticklabels(['0','50k','100k','150k'])
259 | fig.tight_layout()
260 |
261 | # Tidal constituents
262 | for ax in axs:
263 | add_tidal_constituents(ax, period, psd, min_psd=min_psd)
264 | axs[0].annotate('semi-diurnal', xy=(0.05,0.85), xycoords='axes fraction')
265 | axs[1].annotate('diurnal', xy=(0.05,0.85), xycoords='axes fraction')
266 |
267 | # output
268 | if out_fig:
269 | print('save figure to file:', os.path.abspath(out_fig))
270 | fig.savefig(out_fig, bbox_inches='tight', transparent=True, dpi=fig_dpi)
271 | plt.show()
272 |
273 | return
274 |
275 |
276 | def add_tidal_constituents(ax, period, psd, min_psd=1500, verbose=False):
277 | """Mark tidal constituents covered in the axes."""
278 | pmin, pmax = ax.get_xlim()
279 | for tide in TIDES:
280 | if pmin <= tide.period <= pmax:
281 | tide_psd = psd[np.argmin(np.abs(period - tide.period))]
282 | if tide_psd >= min_psd:
283 | ymax = tide_psd / ax.get_ylim()[1]
284 | ax.axvline(x=tide.period, ymax=ymax, color='k', ls='--', lw=1)
285 | ax.annotate(tide.symbol, xy=(tide.period, tide_psd), xytext=(6, 4), textcoords='offset points')
286 | if verbose:
287 | print('tide: speices={}, symbol={}, period={} hours, psd={} m^2/Hz'.format(
288 | tide.species, tide.symbol, tide.period, tide_psd))
289 | return
290 |
--------------------------------------------------------------------------------
/src/pysolid/solid.for:
--------------------------------------------------------------------------------
1 | *** Solid Earth tides (SET) prediction (section 7.1.2 in the 2003 IERS Conventions).
2 | *** The used syntax is mainly f77 with MIL-STD-1753 extension.
3 | *** Author: Dennis Milbert, 2018-06-01. The code is available at:
4 | *** http://geodesyworld.github.io/SOFTS/solid.htm and can be downloaded as:
5 | *** wget http://geodesyworld.github.io/SOFTS/solid.for.txt -O solid.for
6 | *** The code is based on dehanttideinel.f provided by V. Dehant, S. Mathews,
7 | *** J. Gipson and C. Bruyninx. The latest version of dehanttideinel.f and its
8 | *** dependent subroutines can be download from IERS conventions website as:
9 | *** wget -r -l1 --no-parent -R "index.html*" -nH --cut-dirs=3 https://iers-conventions.obspm.fr/content/chapter7/software/dehanttideinel
10 | *** Sep 2020: modify solid() to solid_point/grid() as subroutines, Z. Yunjun and S. Sangha.
11 | *** Apr 2023: return numpy arrays instead of writing txt file, S. Staniewicz.
12 |
13 | subroutine solid_grid(iyr,imo,idy,ihh,imm,iss,
14 | * glad0,steplat,nlat,glod0,steplon,nlon,tide_e,tide_n,tide_u)
15 |
16 | *** calculate solid earth tides (SET) for one spatial grid given the date/time
17 | *** Arguments: iyr/imo/idy/ihh/imm/iss - int, date/time for YYYY/MM/DD/HH/MM/SS
18 | *** glad0/glad1/steplat - float, north(Y_FIRST)/south/step(negative) in deg
19 | *** glod0/glod1/steplon - float, west (X_FIRST)/east /step(positive) in deg
20 | *** Returns: tide_e/tide_n/tide_u - 2D np.ndarray, east/north/up component of SET in m
21 |
22 | implicit double precision(a-h,o-z)
23 | dimension rsun(3),rmoon(3),etide(3),xsta(3)
24 | integer iyr,imo,idy,ihh,imm,iss
25 | integer nlat,nlon
26 | double precision glad0,steplat
27 | double precision glod0,steplon
28 | real(8), intent(out), dimension(nlat,nlon) :: tide_e
29 | real(8), intent(out), dimension(nlat,nlon) :: tide_n
30 | real(8), intent(out), dimension(nlat,nlon) :: tide_u
31 | !***^ leap second table limit flag
32 | logical lflag
33 | common/stuff/rad,pi,pi2
34 | common/comgrs/a,e2
35 | !f2py intent(in) iyr,imo,idy,ihh,imm,iss,glad0,steplat,nlat,glod0,steplon,nlon
36 | !f2py intent(out) tide_e,tide_n,tide_u
37 |
38 | *** constants
39 |
40 | pi=4.d0*datan(1.d0)
41 | pi2=pi+pi
42 | rad=180.d0/pi
43 |
44 | *** grs80
45 |
46 | a=6378137.d0
47 | e2=6.69438002290341574957d-03
48 |
49 | *** input section
50 |
51 | if(iyr.lt.1901.or.iyr.gt.2099) then
52 | print *, 'ERROR: year NOT in [1901-2099]:',iyr
53 | return
54 | endif
55 |
56 | if(imo.lt.1.or.imo.gt.12) then
57 | print *, 'ERROR: month NOT in [1-12]:',imo
58 | return
59 | endif
60 |
61 | if(idy.lt.1.or.idy.gt.31) then
62 | print *, 'ERROR: day NOT in [1-31]:',idy
63 | return
64 | endif
65 |
66 | if(ihh.lt.0.or.ihh.gt.23) then
67 | print *, 'ERROR: hour NOT in [0-23]:',ihh
68 | return
69 | endif
70 |
71 | if(imm.lt.0.or.imm.gt.59) then
72 | print *, 'ERROR: minute NOT in [0-59]:',imm
73 | return
74 | endif
75 |
76 | if(iss.lt.0.or.iss.gt.59) then
77 | print *, 'ERROR: second NOT in [0-59]:',iss
78 | return
79 | endif
80 |
81 | if(glad0.lt.-90.d0.or.glad0.gt.90.d0) then
82 | print *, 'ERROR: lat0 NOT in [-90,+90]:',glad0
83 | return
84 | endif
85 |
86 | if(glod0.lt.-360.d0.or.glod0.gt.360.d0) then
87 | print *, 'ERROR: lon0 NOT in [-360,+360]',glod0
88 | return
89 | endif
90 |
91 | glad1=glad0+nlat*steplat
92 | glod1=glod0+nlon*steplon
93 |
94 | *** loop over the grid
95 |
96 | do ilat=1,nlat
97 | do ilon=1,nlon
98 |
99 | glad = glad0 + (ilat-1)*steplat
100 | glod = glod0 + (ilon-1)*steplon
101 |
102 | *** position of observing point (positive East)
103 |
104 | if(glod.lt. 0.d0) glod=glod+360.d0
105 | if(glod.ge.360.d0) glod=glod-360.d0
106 |
107 | gla0=glad/rad
108 | glo0=glod/rad
109 | eht0=0.d0
110 | call geoxyz(gla0,glo0,eht0,x0,y0,z0)
111 | xsta(1)=x0
112 | xsta(2)=y0
113 | xsta(3)=z0
114 |
115 |
116 | *** here comes the sun (and the moon) (go, tide!)
117 |
118 | !***^ UTC time system
119 | ihr= ihh
120 | imn= imm
121 | sec= iss
122 | call civmjd(iyr,imo,idy,ihr,imn,sec,mjd,fmjd)
123 | !***^ normalize civil time
124 | call mjdciv(mjd,fmjd,iyr,imo,idy,ihr,imn,sec)
125 | call setjd0(iyr,imo,idy)
126 |
127 | !***^ false means flag not raised
128 | !***^ mjd/fmjd in UTC
129 | !***^ mjd/fmjd in UTC
130 | !***^ mjd/fmjd in UTC
131 | lflag=.false.
132 | call sunxyz (mjd,fmjd,rsun,lflag)
133 | call moonxyz(mjd,fmjd,rmoon,lflag)
134 | call detide (xsta,mjd,fmjd,rsun,rmoon,etide,lflag)
135 | xt = etide(1)
136 | yt = etide(2)
137 | zt = etide(3)
138 |
139 | *** determine local geodetic horizon components (topocentric)
140 |
141 | !***^ tide vector
142 | call rge(gla0,glo0,ut,vt,wt,xt, yt, zt)
143 |
144 | call mjdciv(mjd,fmjd +0.001d0/86400.d0,
145 | * iyr,imo,idy,ihr,imn,sec-0.001d0)
146 |
147 | !*** write output respective arrays
148 | tide_e(ilat, ilon) = vt
149 | tide_n(ilat, ilon) = ut
150 | tide_u(ilat, ilon) = wt
151 |
152 | enddo
153 | enddo
154 |
155 | *** end of processing and flag for leap second
156 |
157 | if(lflag) then
158 | print *, 'Mild Warning -- time crossed leap second table'
159 | print *, ' boundaries. Boundary edge value used instead'
160 | endif
161 |
162 |
163 | return
164 | end
165 |
166 | *-----------------------------------------------------------------------
167 | subroutine solid_point(glad,glod,iyr,imo,idy,step_sec,
168 | * secs,tide_e,tide_n,tide_u)
169 |
170 | *** calculate SET at given location for one day with step_sec seconds resolution
171 | *** Arguments: glad/glod - float, latitude/longitude in deg
172 | *** iyr/imo/idy - int, start date/time in UTC
173 | *** step_sec - int, time step in seconds
174 | *** Returns: secs - 1D np.ndarray, seconds since start
175 | *** tide_e/tide_n/tide_u - 1D np.ndarray, east/north/up component of SET in m
176 |
177 | implicit double precision(a-h,o-z)
178 | dimension rsun(3),rmoon(3),etide(3),xsta(3)
179 | double precision glad,glod
180 | integer iyr,imo,idy
181 | integer nloop, step_sec
182 | double precision tdel2
183 | real(8), intent(out), dimension(60*60*24/step_sec) :: secs
184 | real(8), intent(out), dimension(60*60*24/step_sec) :: tide_e
185 | real(8), intent(out), dimension(60*60*24/step_sec) :: tide_n
186 | real(8), intent(out), dimension(60*60*24/step_sec) :: tide_u
187 | !*** leap second table limit flag
188 | logical lflag
189 | common/stuff/rad,pi,pi2
190 | common/comgrs/a,e2
191 | !f2py intent(in) glad,glod,iyr,imo,idy,step_sec
192 | !f2py intent(out) secs,tide_e,tide_n,tide_u
193 |
194 | *** constants
195 |
196 | pi=4.d0*datan(1.d0)
197 | pi2=pi+pi
198 | rad=180.d0/pi
199 |
200 | *** grs80
201 |
202 | a=6378137.d0
203 | e2=6.69438002290341574957d-03
204 |
205 | *** check inputs section
206 |
207 | if(glad.lt.-90.d0.or.glad.gt.90.d0) then
208 | print *, 'ERROR: lat NOT in [-90,+90]:',glad
209 | return
210 | endif
211 |
212 | if(glod.lt.-360.d0.or.glod.gt.360.d0) then
213 | print *, 'ERROR: lon NOT in [-360,+360]:',glod
214 | return
215 | endif
216 |
217 | if(iyr.lt.1901.or.iyr.gt.2099) then
218 | print *, 'ERROR: year NOT in [1901-2099]:',iyr
219 | return
220 | endif
221 |
222 | if(imo.lt.1.or.imo.gt.12) then
223 | print *, 'ERROR: month NOT in [1-12]:',imo
224 | return
225 | endif
226 |
227 | if(idy.lt.1.or.idy.gt.31) then
228 | print *, 'ERROR: day NOT in [1-31]:',idy
229 | return
230 | endif
231 |
232 |
233 | *** position of observing point (positive East)
234 |
235 | if(glod.lt. 0.d0) glod=glod+360.d0
236 | if(glod.ge.360.d0) glod=glod-360.d0
237 |
238 | gla0=glad/rad
239 | glo0=glod/rad
240 | eht0=0.d0
241 | call geoxyz(gla0,glo0,eht0,x0,y0,z0)
242 | xsta(1)=x0
243 | xsta(2)=y0
244 | xsta(3)=z0
245 |
246 | *** here comes the sun (and the moon) (go, tide!)
247 |
248 | !*** UTC time system
249 | ihr= 0
250 | imn= 0
251 | sec=0.d0
252 | call civmjd(iyr,imo,idy,ihr,imn,sec,mjd,fmjd)
253 | !*** normalize civil time
254 | call mjdciv(mjd,fmjd,iyr,imo,idy,ihr,imn,sec)
255 | call setjd0(iyr,imo,idy)
256 |
257 | *** loop over time
258 |
259 | nloop=60*60*24/step_sec
260 | tdel2=1.d0/DFLOAT(nloop)
261 | do iloop=1,nloop
262 | !*** false means flag not raised
263 | !*** mjd/fmjd in UTC
264 | !*** mjd/fmjd in UTC
265 | !*** mjd/fmjd in UTC
266 | lflag=.false.
267 | call sunxyz (mjd,fmjd,rsun,lflag)
268 | call moonxyz(mjd,fmjd,rmoon,lflag)
269 | call detide (xsta,mjd,fmjd,rsun,rmoon,etide,lflag)
270 | xt = etide(1)
271 | yt = etide(2)
272 | zt = etide(3)
273 |
274 | *** determine local geodetic horizon components (topocentric)
275 |
276 | !*** tide vector
277 | call rge(gla0,glo0,ut,vt,wt,xt, yt, zt)
278 |
279 | call mjdciv(mjd,fmjd,iyr,imo,idy,ihr,imn,sec)
280 |
281 | tsec=ihr*3600.d0+imn*60.d0+sec
282 |
283 | secs(iloop) = tsec
284 | tide_e(iloop) = vt
285 | tide_n(iloop) = ut
286 | tide_u(iloop) = wt
287 |
288 | !*** update fmjd for the next round
289 | fmjd=fmjd+tdel2
290 | !*** force 1 sec. granularity
291 | fmjd=(idnint(fmjd*86400.d0))/86400.d0
292 | enddo
293 |
294 | *** end of processing and flag of leap second
295 |
296 | if(lflag) then
297 | print *, 'Mild Warning -- time crossed leap second table'
298 | print *, ' boundaries. Boundary edge value used instead'
299 | endif
300 |
301 | return
302 | end
303 |
304 | *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
305 | subroutine detide(xsta,mjd,fmjd,xsun,xmon,dxtide,lflag)
306 |
307 | *** computation of tidal corrections of station displacements caused
308 | *** by lunar and solar gravitational attraction
309 | *** UTC version
310 |
311 | *** step 1 (here general degree 2 and 3 corrections +
312 | *** call st1idiu + call st1isem + call st1l1)
313 | *** + step 2 (call step2diu + call step2lon + call step2idiu)
314 | *** it has been decided that the step 3 un-correction for permanent tide
315 | *** would *not* be applied in order to avoid jump in the reference frame
316 | *** (this step 3 must added in order to get the mean tide station position
317 | *** and to be conformed with the iag resolution.)
318 |
319 | *** inputs
320 | *** xsta(i),i=1,2,3 -- geocentric position of the station (ITRF/ECEF)
321 | *** xsun(i),i=1,2,3 -- geoc. position of the sun (ECEF)
322 | *** xmon(i),i=1,2,3 -- geoc. position of the moon (ECEF)
323 | *** mjd,fmjd -- modified julian day (and fraction) (in UTC time)
324 |
325 | ****old calling sequence*****************************************************
326 | *** dmjd -- time in mean julian date (including day fraction)
327 | *** fhr=hr+zmin/60.+sec/3600. -- hr in the day
328 |
329 | *** outputs
330 | *** dxtide(i),i=1,2,3 -- displacement vector (ITRF)
331 | *** lflag -- leap second table limit flag, false:flag not raised
332 |
333 | *** author iers 1996 : v. dehant, s. mathews and j. gipson
334 | *** (test between two subroutines)
335 | *** author iers 2000 : v. dehant, c. bruyninx and s. mathews
336 | *** (test in the bernese program by c. bruyninx)
337 |
338 | *** created: 96/03/23 (see above)
339 | *** modified from dehanttideinelMJD.f by Dennis Milbert 2006sep10
340 | *** bug fix regarding fhr (changed calling sequence, too)
341 | *** modified to reflect table 7.5a and b IERS Conventions 2003
342 | *** modified to use TT time system to call step 2 functions
343 | *** sign correction by V.Dehant to match eq.16b, p.81, Conventions
344 | *** applied by Dennis Milbert 2007may05
345 | *** UTC version by Dennis Milbert 2018june01
346 |
347 | implicit double precision(a-h,o-z)
348 | double precision xsta(3),xsun(3),xmon(3),dxtide(3),xcorsta(3)
349 | double precision h20,l20,h3,l3,h2,l2,fmjd
350 | integer mjd
351 | double precision mass_ratio_sun,mass_ratio_moon
352 | logical lflag,leapflag
353 | !*** leap second table limit flag
354 | !*** leap second table limit flag
355 | save /limitflag/
356 | common/limitflag/leapflag
357 |
358 | *** nominal second degree and third degree love numbers and shida numbers
359 |
360 | data h20/0.6078d0/,l20/0.0847d0/,h3/0.292d0/,l3/0.015d0/
361 |
362 | *** internal support for new calling sequence
363 | *** first, convert UTC time into TT time (and, bring leapflag into variable)
364 |
365 | leapflag=lflag
366 | !*** UTC time (sec of day)
367 | !*** TT time (sec of day)
368 | !*** TT time (fract. day)
369 | tsecutc =fmjd*86400.d0
370 | tsectt =utc2ttt(tsecutc)
371 | fmjdtt =tsectt/86400.d0
372 | lflag = leapflag
373 |
374 | !*** float MJD in TT
375 | dmjdtt=mjd+fmjdtt
376 | *** commented line was live code in dehanttideinelMJD.f
377 | *** changed on the suggestion of Dr. Don Kim, UNB -- 09mar21
378 | *** Julian date for 2000 January 1 00:00:00.0 UT is JD 2451544.5
379 | *** MJD for 2000 January 1 00:00:00.0 UT is MJD 51544.0
380 | ***** t=(dmjdtt-51545.d0)/36525.d0
381 | !*** days to centuries, TT
382 | !*** days to centuries, TT
383 | t=(dmjdtt-51544.d0)/36525.d0
384 | !*** hours in the day, TT
385 | fhr=(dmjdtt-int(dmjdtt))*24.d0
386 |
387 | *** scalar product of station vector with sun/moon vector
388 |
389 | call sprod(xsta,xsun,scs,rsta,rsun)
390 | call sprod(xsta,xmon,scm,rsta,rmon)
391 | scsun=scs/rsta/rsun
392 | scmon=scm/rsta/rmon
393 |
394 | *** computation of new h2 and l2
395 |
396 | cosphi=dsqrt(xsta(1)*xsta(1) + xsta(2)*xsta(2))/rsta
397 | h2=h20-0.0006d0*(1.d0-3.d0/2.d0*cosphi*cosphi)
398 | l2=l20+0.0002d0*(1.d0-3.d0/2.d0*cosphi*cosphi)
399 |
400 | *** p2-term
401 |
402 | p2sun=3.d0*(h2/2.d0-l2)*scsun*scsun-h2/2.d0
403 | p2mon=3.d0*(h2/2.d0-l2)*scmon*scmon-h2/2.d0
404 |
405 | *** p3-term
406 |
407 | p3sun=5.d0/2.d0*(h3-3.d0*l3)*scsun**3+3.d0/2.d0*(l3-h3)*scsun
408 | p3mon=5.d0/2.d0*(h3-3.d0*l3)*scmon**3+3.d0/2.d0*(l3-h3)*scmon
409 |
410 | *** term in direction of sun/moon vector
411 |
412 | x2sun=3.d0*l2*scsun
413 | x2mon=3.d0*l2*scmon
414 | x3sun=3.d0*l3/2.d0*(5.d0*scsun*scsun-1.d0)
415 | x3mon=3.d0*l3/2.d0*(5.d0*scmon*scmon-1.d0)
416 |
417 | *** factors for sun/moon
418 |
419 | mass_ratio_sun=332945.943062d0
420 | mass_ratio_moon=0.012300034d0
421 | re =6378136.55d0
422 | fac2sun=mass_ratio_sun*re*(re/rsun)**3
423 | fac2mon=mass_ratio_moon*re*(re/rmon)**3
424 | fac3sun=fac2sun*(re/rsun)
425 | fac3mon=fac2mon*(re/rmon)
426 |
427 | *** total displacement
428 |
429 | do i=1,3
430 | dxtide(i)=fac2sun*( x2sun*xsun(i)/rsun + p2sun*xsta(i)/rsta ) +
431 | * fac2mon*( x2mon*xmon(i)/rmon + p2mon*xsta(i)/rsta ) +
432 | * fac3sun*( x3sun*xsun(i)/rsun + p3sun*xsta(i)/rsta ) +
433 | * fac3mon*( x3mon*xmon(i)/rmon + p3mon*xsta(i)/rsta )
434 | enddo
435 | call zero_vec8(xcorsta)
436 |
437 | *** corrections for the out-of-phase part of love numbers
438 | *** (part h_2^(0)i and l_2^(0)i )
439 |
440 | *** first, for the diurnal band
441 |
442 | call st1idiu(xsta,xsun,xmon,fac2sun,fac2mon,xcorsta)
443 | dxtide(1)=dxtide(1)+xcorsta(1)
444 | dxtide(2)=dxtide(2)+xcorsta(2)
445 | dxtide(3)=dxtide(3)+xcorsta(3)
446 |
447 | *** second, for the semi-diurnal band
448 |
449 | call st1isem(xsta,xsun,xmon,fac2sun,fac2mon,xcorsta)
450 | dxtide(1)=dxtide(1)+xcorsta(1)
451 | dxtide(2)=dxtide(2)+xcorsta(2)
452 | dxtide(3)=dxtide(3)+xcorsta(3)
453 |
454 | *** corrections for the latitude dependence of love numbers (part l^(1) )
455 |
456 | call st1l1(xsta,xsun,xmon,fac2sun,fac2mon,xcorsta)
457 | dxtide(1)=dxtide(1)+xcorsta(1)
458 | dxtide(2)=dxtide(2)+xcorsta(2)
459 | dxtide(3)=dxtide(3)+xcorsta(3)
460 |
461 | *** consider corrections for step 2
462 | *** corrections for the diurnal band:
463 |
464 | *** first, we need to know the date converted in julian centuries
465 |
466 | *** this is now handled at top of code (also convert to TT time system)
467 | ***** t=(dmjd-51545.)/36525.
468 | ***** fhr=dmjd-int(dmjd) !*** this is/was a buggy line (day vs. hr)
469 |
470 | *** second, the diurnal band corrections,
471 | *** (in-phase and out-of-phase frequency dependence):
472 |
473 | call step2diu(xsta,fhr,t,xcorsta)
474 | dxtide(1)=dxtide(1)+xcorsta(1)
475 | dxtide(2)=dxtide(2)+xcorsta(2)
476 | dxtide(3)=dxtide(3)+xcorsta(3)
477 |
478 | *** corrections for the long-period band,
479 | *** (in-phase and out-of-phase frequency dependence):
480 |
481 | call step2lon(xsta,fhr,t,xcorsta)
482 | dxtide(1)=dxtide(1)+xcorsta(1)
483 | dxtide(2)=dxtide(2)+xcorsta(2)
484 | dxtide(3)=dxtide(3)+xcorsta(3)
485 |
486 | *** consider corrections for step 3
487 | *-----------------------------------------------------------------------
488 | * The code below is commented to prevent restoring deformation
489 | * due to permanent tide. All the code above removes
490 | * total tidal deformation with conventional Love numbers.
491 | * The code above realizes a conventional tide free crust (i.e. ITRF).
492 | * This does NOT conform to Resolution 16 of the 18th General Assembly
493 | * of the IAG (1983). This resolution has not been implemented by
494 | * the space geodesy community in general (c.f. IERS Conventions 2003).
495 | *-----------------------------------------------------------------------
496 |
497 | *** uncorrect for the permanent tide (only if you want mean tide system)
498 |
499 | *** pi=3.141592654
500 | *** sinphi=xsta(3)/rsta
501 | *** cosphi=dsqrt(xsta(1)**2+xsta(2)**2)/rsta
502 | *** cosla=xsta(1)/cosphi/rsta
503 | *** sinla=xsta(2)/cosphi/rsta
504 | *** dr=-dsqrt(5./4./pi)*h2*0.31460*(3./2.*sinphi**2-0.5)
505 | *** dn=-dsqrt(5./4./pi)*l2*0.31460*3.*cosphi*sinphi
506 | *** dxtide(1)=dxtide(1)-dr*cosla*cosphi+dn*cosla*sinphi
507 | *** dxtide(2)=dxtide(2)-dr*sinla*cosphi+dn*sinla*sinphi
508 | *** dxtide(3)=dxtide(3)-dr*sinphi -dn*cosphi
509 |
510 | return
511 | end
512 | *-----------------------------------------------------------------------
513 | subroutine st1l1(xsta,xsun,xmon,fac2sun,fac2mon,xcorsta)
514 |
515 | *** this subroutine gives the corrections induced by the latitude dependence
516 | *** given by l^(1) in mahtews et al (1991)
517 |
518 | *** input: xsta,xsun,xmon,fac3sun,fac3mon
519 | *** output: xcorsta
520 |
521 | implicit double precision (a-h,o-z)
522 | double precision xsta(3),xsun(3),xmon(3),xcorsta(3)
523 | double precision l1,l1d,l1sd,fac2sun,fac2mon
524 | data l1d/0.0012d0/,l1sd/0.0024d0/
525 |
526 | rsta=enorm8(xsta)
527 | sinphi=xsta(3)/rsta
528 | cosphi=dsqrt(xsta(1)**2+xsta(2)**2)/rsta
529 | sinla=xsta(2)/cosphi/rsta
530 | cosla=xsta(1)/cosphi/rsta
531 | rmon=enorm8(xmon)
532 | rsun=enorm8(xsun)
533 |
534 | *** for the diurnal band
535 |
536 | l1=l1d
537 | dnsun=-l1*sinphi**2*fac2sun*xsun(3)*(xsun(1)*cosla+xsun(2)*sinla)
538 | * /rsun**2
539 | dnmon=-l1*sinphi**2*fac2mon*xmon(3)*(xmon(1)*cosla+xmon(2)*sinla)
540 | * /rmon**2
541 | desun=l1*sinphi*(cosphi**2-sinphi**2)*fac2sun*xsun(3)*
542 | * (xsun(1)*sinla-xsun(2)*cosla)/rsun**2
543 | demon=l1*sinphi*(cosphi**2-sinphi**2)*fac2mon*xmon(3)*
544 | * (xmon(1)*sinla-xmon(2)*cosla)/rmon**2
545 | de=3.d0*(desun+demon)
546 | dn=3.d0*(dnsun+dnmon)
547 | xcorsta(1)=-de*sinla-dn*sinphi*cosla
548 | xcorsta(2)= de*cosla-dn*sinphi*sinla
549 | xcorsta(3)= dn*cosphi
550 |
551 | *** for the semi-diurnal band
552 |
553 | l1=l1sd
554 | costwola=cosla**2-sinla**2
555 | sintwola=2.d0*cosla*sinla
556 | dnsun=-l1/2.d0*sinphi*cosphi*fac2sun*((xsun(1)**2-xsun(2)**2)*
557 | * costwola+2.d0*xsun(1)*xsun(2)*sintwola)/rsun**2
558 | dnmon=-l1/2.d0*sinphi*cosphi*fac2mon*((xmon(1)**2-xmon(2)**2)*
559 | * costwola+2.d0*xmon(1)*xmon(2)*sintwola)/rmon**2
560 | desun=-l1/2.d0*sinphi**2*cosphi*fac2sun*((xsun(1)**2-xsun(2)**2)*
561 | * sintwola-2.d0*xsun(1)*xsun(2)*costwola)/rsun**2
562 | demon=-l1/2.d0*sinphi**2*cosphi*fac2mon*((xmon(1)**2-xmon(2)**2)*
563 | * sintwola-2.d0*xmon(1)*xmon(2)*costwola)/rmon**2
564 | de=3.d0*(desun+demon)
565 | dn=3.d0*(dnsun+dnmon)
566 | xcorsta(1)=xcorsta(1)-de*sinla-dn*sinphi*cosla
567 | xcorsta(2)=xcorsta(2)+de*cosla-dn*sinphi*sinla
568 | xcorsta(3)=xcorsta(3) +dn*cosphi
569 |
570 | return
571 | end
572 | *-----------------------------------------------------------------------
573 | subroutine step2diu(xsta,fhr,t,xcorsta)
574 |
575 | *** last change: vd 17 may 00 1:20 pm
576 | *** these are the subroutines for the step2 of the tidal corrections.
577 | *** they are called to account for the frequency dependence
578 | *** of the love numbers.
579 |
580 | implicit double precision (a-h,o-z)
581 | double precision xsta(3),xcorsta(3),datdi(9,31)
582 | double precision deg2rad, fhr, t
583 | data deg2rad/0.017453292519943295769d0/
584 |
585 | *** note, following table is derived from dehanttideinelMJD.f (2000oct30 16:10)
586 | *** has minor differences from that of dehanttideinel.f (2000apr17 14:10)
587 | *** D.M. edited to strictly follow published table 7.5a (2006aug08 13:46)
588 |
589 | *** cf. table 7.5a of IERS conventions 2003 (TN.32, pg.82)
590 | *** columns are s,h,p,N',ps, dR(ip),dR(op),dT(ip),dT(op)
591 | *** units of mm
592 |
593 | data ((datdi(i,j),i=1,9),j=1,31)/
594 | * -3., 0., 2., 0., 0.,-0.01,-0.01, 0.0 , 0.0,
595 | * -3., 2., 0., 0., 0.,-0.01,-0.01, 0.0 , 0.0,
596 | * -2., 0., 1.,-1., 0.,-0.02,-0.01, 0.0 , 0.0,
597 | *****-----------------------------------------------------------------------
598 | ****** -2., 0., 1., 0., 0.,-0.08,-0.05, 0.01,-0.02,
599 | !*** original entry
600 | !*** table 7.5a
601 | * -2., 0., 1., 0., 0.,-0.08, 0.00, 0.01, 0.01,
602 | *****-----------------------------------------------------------------------
603 | * -2., 2.,-1., 0., 0.,-0.02,-0.01, 0.0 , 0.0,
604 | *****-----------------------------------------------------------------------
605 | ****** -1., 0., 0.,-1., 0.,-0.10,-0.05, 0.0 ,-0.02,
606 | !*** original entry
607 | !*** table 7.5a
608 | * -1., 0., 0.,-1., 0.,-0.10, 0.00, 0.00, 0.00,
609 | *****-----------------------------------------------------------------------
610 | ****** -1., 0., 0., 0., 0.,-0.51,-0.26,-0.02,-0.12,
611 | !*** original entry
612 | !*** table 7.5a
613 | * -1., 0., 0., 0., 0.,-0.51, 0.00,-0.02, 0.03,
614 | *****-----------------------------------------------------------------------
615 | * -1., 2., 0., 0., 0., 0.01, 0.0 , 0.0 , 0.0,
616 | * 0.,-2., 1., 0., 0., 0.01, 0.0 , 0.0 , 0.0,
617 | * 0., 0.,-1., 0., 0., 0.02, 0.01, 0.0 , 0.0,
618 | *****-----------------------------------------------------------------------
619 | ****** 0., 0., 1., 0., 0., 0.06, 0.02, 0.0 , 0.01,
620 | !*** original entry
621 | !*** table 7.5a
622 | * 0., 0., 1., 0., 0., 0.06, 0.00, 0.00, 0.00,
623 | *****-----------------------------------------------------------------------
624 | * 0., 0., 1., 1., 0., 0.01, 0.0 , 0.0 , 0.0,
625 | * 0., 2.,-1., 0., 0., 0.01, 0.0 , 0.0 , 0.0,
626 | !*** table 7.5a
627 | * 1.,-3., 0., 0., 1.,-0.06, 0.00, 0.00, 0.00,
628 | * 1.,-2., 0., 1., 0., 0.01, 0.0 , 0.0 , 0.0,
629 | *****-----------------------------------------------------------------------
630 | ****** 1.,-2., 0., 0., 0.,-1.23,-0.05, 0.06,-0.06,
631 | !*** original entry
632 | !*** table 7.5a
633 | * 1.,-2., 0., 0., 0.,-1.23,-0.07, 0.06, 0.01,
634 | *****-----------------------------------------------------------------------
635 | * 1.,-1., 0., 0.,-1., 0.02, 0.0 , 0.0 , 0.0,
636 | * 1.,-1., 0., 0., 1., 0.04, 0.0 , 0.0 , 0.0,
637 | !*** table 7.5a
638 | * 1., 0., 0.,-1., 0.,-0.22, 0.01, 0.01, 0.00,
639 | *****-----------------------------------------------------------------------
640 | ****** 1., 0., 0., 0., 0.,12.02,-0.45,-0.66, 0.17,
641 | !*** original entry
642 | !*** table 7.5a
643 | * 1., 0., 0., 0., 0.,12.00,-0.78,-0.67,-0.03,
644 | *****-----------------------------------------------------------------------
645 | ****** 1., 0., 0., 1., 0., 1.73,-0.07,-0.10, 0.02,
646 | !*** original entry
647 | !*** table 7.5a
648 | * 1., 0., 0., 1., 0., 1.73,-0.12,-0.10, 0.00,
649 | *****-----------------------------------------------------------------------
650 | * 1., 0., 0., 2., 0.,-0.04, 0.0 , 0.0 , 0.0,
651 | *****-----------------------------------------------------------------------
652 | ****** 1., 1., 0., 0.,-1.,-0.50, 0.0 , 0.03, 0.0,
653 | !*** original entry
654 | !*** table 7.5a
655 | * 1., 1., 0., 0.,-1.,-0.50,-0.01, 0.03, 0.00,
656 | *****-----------------------------------------------------------------------
657 | * 1., 1., 0., 0., 1., 0.01, 0.0 , 0.0 , 0.0,
658 | *****-----------------------------------------------------------------------
659 | ****** 0., 1., 0., 1.,-1.,-0.01, 0.0 , 0.0 , 0.0,
660 | !*** original entry
661 | !*** v.dehant 2007
662 | * 1., 1., 0., 1.,-1.,-0.01, 0.0 , 0.0 , 0.0,
663 | *****-----------------------------------------------------------------------
664 | * 1., 2.,-2., 0., 0.,-0.01, 0.0 , 0.0 , 0.0,
665 | *****-----------------------------------------------------------------------
666 | ****** 1., 2., 0., 0., 0.,-0.12, 0.01, 0.01, 0.0,
667 | !*** original entry
668 | !*** table 7.5a
669 | * 1., 2., 0., 0., 0.,-0.11, 0.01, 0.01, 0.00,
670 | *****-----------------------------------------------------------------------
671 | * 2.,-2., 1., 0., 0.,-0.01, 0.0 , 0.0 , 0.0,
672 | * 2., 0.,-1., 0., 0.,-0.02, 0.02, 0.0 , 0.01,
673 | * 3., 0., 0., 0., 0., 0.0 , 0.01, 0.0 , 0.01,
674 | * 3., 0., 0., 1., 0., 0.0 , 0.01, 0.0 , 0.0/
675 |
676 | s=218.31664563d0+481267.88194d0*t-0.0014663889d0*t*t
677 | * +0.00000185139d0*t**3
678 | tau=fhr*15.d0+280.4606184d0+36000.7700536d0*t+0.00038793d0*t*t
679 | * -0.0000000258d0*t**3-s
680 | pr=1.396971278*t+0.000308889*t*t+0.000000021*t**3
681 | * +0.000000007*t**4
682 | s=s+pr
683 | h=280.46645d0+36000.7697489d0*t+0.00030322222d0*t*t
684 | * +0.000000020*t**3-0.00000000654*t**4
685 | p=83.35324312d0+4069.01363525d0*t-0.01032172222d0*t*t
686 | * -0.0000124991d0*t**3+0.00000005263d0*t**4
687 | zns=234.95544499d0 +1934.13626197d0*t-0.00207561111d0*t*t
688 | * -0.00000213944d0*t**3+0.00000001650d0*t**4
689 | ps=282.93734098d0+1.71945766667d0*t+0.00045688889d0*t*t
690 | * -0.00000001778d0*t**3-0.00000000334d0*t**4
691 |
692 | *** reduce angles to between 0 and 360
693 |
694 | s= dmod( s,360.d0)
695 | tau=dmod(tau,360.d0)
696 | h= dmod( h,360.d0)
697 | p= dmod( p,360.d0)
698 | zns=dmod(zns,360.d0)
699 | ps= dmod( ps,360.d0)
700 |
701 | rsta=dsqrt(xsta(1)**2+xsta(2)**2+xsta(3)**2)
702 | sinphi=xsta(3)/rsta
703 | cosphi=dsqrt(xsta(1)**2+xsta(2)**2)/rsta
704 |
705 | cosla=xsta(1)/cosphi/rsta
706 | sinla=xsta(2)/cosphi/rsta
707 | zla = datan2(xsta(2),xsta(1))
708 | do i=1,3
709 | xcorsta(i)=0.d0
710 | enddo
711 | do j=1,31
712 | thetaf=(tau+datdi(1,j)*s+datdi(2,j)*h+datdi(3,j)*p+
713 | * datdi(4,j)*zns+datdi(5,j)*ps)*deg2rad
714 | dr=datdi(6,j)*2.d0*sinphi*cosphi*sin(thetaf+zla)+
715 | * datdi(7,j)*2.d0*sinphi*cosphi*cos(thetaf+zla)
716 | dn=datdi(8,j)*(cosphi**2-sinphi**2)*sin(thetaf+zla)+
717 | * datdi(9,j)*(cosphi**2-sinphi**2)*cos(thetaf+zla)
718 | ***** following correction by V.Dehant to match eq.16b, p.81, 2003 Conventions
719 | ***** de=datdi(8,j)*sinphi*cos(thetaf+zla)+
720 | de=datdi(8,j)*sinphi*cos(thetaf+zla)-
721 | * datdi(9,j)*sinphi*sin(thetaf+zla)
722 | xcorsta(1)=xcorsta(1)+dr*cosla*cosphi-de*sinla
723 | * -dn*sinphi*cosla
724 | xcorsta(2)=xcorsta(2)+dr*sinla*cosphi+de*cosla
725 | * -dn*sinphi*sinla
726 | xcorsta(3)=xcorsta(3)+dr*sinphi+dn*cosphi
727 | enddo
728 |
729 | do i=1,3
730 | xcorsta(i)=xcorsta(i)/1000.d0
731 | enddo
732 |
733 | return
734 | end
735 | *-----------------------------------------------------------------------
736 | subroutine step2lon(xsta,fhr,t,xcorsta)
737 |
738 | implicit double precision (a-h,o-z)
739 | double precision deg2rad,fhr,t
740 | double precision xsta(3),xcorsta(3),datdi(9,5)
741 | data deg2rad/0.017453292519943295769d0/
742 |
743 | *** cf. table 7.5b of IERS conventions 2003 (TN.32, pg.82)
744 | *** columns are s,h,p,N',ps, dR(ip),dT(ip),dR(op),dT(op)
745 | *** IERS cols.= s,h,p,N',ps, dR(ip),dR(op),dT(ip),dT(op)
746 | *** units of mm
747 |
748 | data ((datdi(i,j),i=1,9),j=1,5)/
749 | * 0, 0, 0, 1, 0, 0.47, 0.23, 0.16, 0.07,
750 | * 0, 2, 0, 0, 0, -0.20,-0.12,-0.11,-0.05,
751 | * 1, 0,-1, 0, 0, -0.11,-0.08,-0.09,-0.04,
752 | * 2, 0, 0, 0, 0, -0.13,-0.11,-0.15,-0.07,
753 | * 2, 0, 0, 1, 0, -0.05,-0.05,-0.06,-0.03/
754 |
755 | s=218.31664563d0+481267.88194d0*t-0.0014663889d0*t*t
756 | * +0.00000185139d0*t**3
757 | pr=1.396971278*t+0.000308889*t*t+0.000000021*t**3
758 | * +0.000000007*t**4
759 | s=s+pr
760 | h=280.46645d0+36000.7697489d0*t+0.00030322222d0*t*t
761 | * +0.000000020*t**3-0.00000000654*t**4
762 | p=83.35324312d0+4069.01363525d0*t-0.01032172222d0*t*t
763 | * -0.0000124991d0*t**3+0.00000005263d0*t**4
764 | zns=234.95544499d0 +1934.13626197d0*t-0.00207561111d0*t*t
765 | * -0.00000213944d0*t**3+0.00000001650d0*t**4
766 | ps=282.93734098d0+1.71945766667d0*t+0.00045688889d0*t*t
767 | * -0.00000001778d0*t**3-0.00000000334d0*t**4
768 | rsta=dsqrt(xsta(1)**2+xsta(2)**2+xsta(3)**2)
769 | sinphi=xsta(3)/rsta
770 | cosphi=dsqrt(xsta(1)**2+xsta(2)**2)/rsta
771 | cosla=xsta(1)/cosphi/rsta
772 | sinla=xsta(2)/cosphi/rsta
773 |
774 | *** reduce angles to between 0 and 360
775 |
776 | s= dmod( s,360.d0)
777 | ***** tau=dmod(tau,360.d0) !*** tau not used here--09jul28
778 | h= dmod( h,360.d0)
779 | p= dmod( p,360.d0)
780 | zns=dmod(zns,360.d0)
781 | ps= dmod( ps,360.d0)
782 |
783 | dr_tot=0.d0
784 | dn_tot=0.d0
785 | do i=1,3
786 | xcorsta(i)=0.d0
787 | enddo
788 |
789 | *** 1 2 3 4 5 6 7 8 9
790 | *** columns are s,h,p,N',ps, dR(ip),dT(ip),dR(op),dT(op)
791 |
792 | do j=1,5
793 | thetaf=(datdi(1,j)*s+datdi(2,j)*h+datdi(3,j)*p+
794 | * datdi(4,j)*zns+datdi(5,j)*ps)*deg2rad
795 | dr=datdi(6,j)*(3.d0*sinphi**2-1.d0)/2.*cos(thetaf)+
796 | * datdi(8,j)*(3.d0*sinphi**2-1.d0)/2.*sin(thetaf)
797 | dn=datdi(7,j)*(cosphi*sinphi*2.d0)*cos(thetaf)+
798 | * datdi(9,j)*(cosphi*sinphi*2.d0)*sin(thetaf)
799 | de=0.d0
800 | dr_tot=dr_tot+dr
801 | dn_tot=dn_tot+dn
802 | xcorsta(1)=xcorsta(1)+dr*cosla*cosphi-de*sinla
803 | * -dn*sinphi*cosla
804 | xcorsta(2)=xcorsta(2)+dr*sinla*cosphi+de*cosla
805 | * -dn*sinphi*sinla
806 | xcorsta(3)=xcorsta(3)+dr*sinphi+dn*cosphi
807 | enddo
808 |
809 | do i=1,3
810 | xcorsta(i)=xcorsta(i)/1000.d0
811 | enddo
812 |
813 | return
814 | end
815 | *-----------------------------------------------------------------------
816 | subroutine st1idiu(xsta,xsun,xmon,fac2sun,fac2mon,xcorsta)
817 |
818 | *** this subroutine gives the out-of-phase corrections induced by
819 | *** mantle inelasticity in the diurnal band
820 |
821 | *** input: xsta,xsun,xmon,fac2sun,fac2mon
822 | *** output: xcorsta
823 |
824 | implicit double precision (a-h,o-z)
825 | double precision xsta(3),xsun(3),xmon(3),xcorsta(3)
826 | double precision fac2sun,fac2mon
827 | data dhi/-0.0025d0/,dli/-0.0007d0/
828 |
829 | rsta=enorm8(xsta)
830 | sinphi=xsta(3)/rsta
831 | cosphi=dsqrt(xsta(1)**2+xsta(2)**2)/rsta
832 | cos2phi=cosphi**2-sinphi**2
833 | sinla=xsta(2)/cosphi/rsta
834 | cosla=xsta(1)/cosphi/rsta
835 | rmon=enorm8(xmon)
836 | rsun=enorm8(xsun)
837 | drsun=-3.d0*dhi*sinphi*cosphi*fac2sun*xsun(3)*(xsun(1)*
838 | * sinla-xsun(2)*cosla)/rsun**2
839 | drmon=-3.d0*dhi*sinphi*cosphi*fac2mon*xmon(3)*(xmon(1)*
840 | * sinla-xmon(2)*cosla)/rmon**2
841 | dnsun=-3.d0*dli*cos2phi*fac2sun*xsun(3)*(xsun(1)*sinla-
842 | * xsun(2)*cosla)/rsun**2
843 | dnmon=-3.d0*dli*cos2phi*fac2mon*xmon(3)*(xmon(1)*sinla-
844 | * xmon(2)*cosla)/rmon**2
845 | desun=-3.d0*dli*sinphi*fac2sun*xsun(3)*
846 | * (xsun(1)*cosla+xsun(2)*sinla)/rsun**2
847 | demon=-3.d0*dli*sinphi*fac2mon*xmon(3)*
848 | * (xmon(1)*cosla+xmon(2)*sinla)/rmon**2
849 | dr=drsun+drmon
850 | dn=dnsun+dnmon
851 | de=desun+demon
852 | xcorsta(1)=dr*cosla*cosphi-de*sinla-dn*sinphi*cosla
853 | xcorsta(2)=dr*sinla*cosphi+de*cosla-dn*sinphi*sinla
854 | xcorsta(3)=dr*sinphi +dn*cosphi
855 |
856 | return
857 | end
858 | *-----------------------------------------------------------------------
859 | subroutine st1isem(xsta,xsun,xmon,fac2sun,fac2mon,xcorsta)
860 |
861 | *** this subroutine gives the out-of-phase corrections induced by
862 | *** mantle inelasticity in the diurnal band
863 |
864 | *** input: xsta,xsun,xmon,fac2sun,fac2mon
865 | *** output: xcorsta
866 |
867 | implicit double precision (a-h,o-z)
868 | double precision xsta(3),xsun(3),xmon(3),xcorsta(3)
869 | double precision fac2sun, fac2mon
870 | data dhi/-0.0022d0/,dli/-0.0007d0/
871 |
872 | rsta=enorm8(xsta)
873 | sinphi=xsta(3)/rsta
874 | cosphi=dsqrt(xsta(1)**2+xsta(2)**2)/rsta
875 | sinla=xsta(2)/cosphi/rsta
876 | cosla=xsta(1)/cosphi/rsta
877 | costwola=cosla**2-sinla**2
878 | sintwola=2.d0*cosla*sinla
879 | rmon=enorm8(xmon)
880 | rsun=enorm8(xsun)
881 | drsun=-3.d0/4.d0*dhi*cosphi**2*fac2sun*((xsun(1)**2-xsun(2)**2)*
882 | * sintwola-2.*xsun(1)*xsun(2)*costwola)/rsun**2
883 | drmon=-3.d0/4.d0*dhi*cosphi**2*fac2mon*((xmon(1)**2-xmon(2)**2)*
884 | * sintwola-2.*xmon(1)*xmon(2)*costwola)/rmon**2
885 | dnsun=1.5d0*dli*sinphi*cosphi*fac2sun*((xsun(1)**2-xsun(2)**2)*
886 | * sintwola-2.d0*xsun(1)*xsun(2)*costwola)/rsun**2
887 | dnmon=1.5d0*dli*sinphi*cosphi*fac2mon*((xmon(1)**2-xmon(2)**2)*
888 | * sintwola-2.d0*xmon(1)*xmon(2)*costwola)/rmon**2
889 | desun=-3.d0/2.d0*dli*cosphi*fac2sun*((xsun(1)**2-xsun(2)**2)*
890 | * costwola+2.*xsun(1)*xsun(2)*sintwola)/rsun**2
891 | demon=-3.d0/2.d0*dli*cosphi*fac2mon*((xmon(1)**2-xmon(2)**2)*
892 | * costwola+2.d0*xmon(1)*xmon(2)*sintwola)/rmon**2
893 | dr=drsun+drmon
894 | dn=dnsun+dnmon
895 | de=desun+demon
896 | xcorsta(1)=dr*cosla*cosphi-de*sinla-dn*sinphi*cosla
897 | xcorsta(2)=dr*sinla*cosphi+de*cosla-dn*sinphi*sinla
898 | xcorsta(3)=dr*sinphi+dn*cosphi
899 |
900 | return
901 | end
902 | *-----------------------------------------------------------------------
903 | subroutine sprod(x,y,scal,r1,r2)
904 |
905 | *** computation of the scalar-product of two vectors and their norms
906 |
907 | *** input: x(i),i=1,2,3 -- components of vector x
908 | *** y(i),i=1,2,3 -- components of vector y
909 | *** output: scal -- scalar product of x and y
910 | *** r1,r2 -- lengths of the two vectors x and y
911 |
912 | implicit double precision (a-h,o-z)
913 | double precision x(3),y(3)
914 | double precision r1,r2,scal
915 |
916 | r1=dsqrt(x(1)*x(1) + x(2)*x(2) + x(3)*x(3))
917 | r2=dsqrt(y(1)*y(1) + y(2)*y(2) + y(3)*y(3))
918 | scal= x(1)*y(1) + x(2)*y(2) + x(3)*y(3)
919 |
920 | return
921 | end
922 | *-----------------------------------------------------------------------
923 | double precision function enorm8(a)
924 |
925 | *** compute euclidian norm of a vector (of length 3)
926 |
927 | double precision a(3)
928 |
929 | enorm8=dsqrt(a(1)*a(1) + a(2)*a(2) + a(3)*a(3))
930 |
931 | return
932 | end
933 | *-----------------------------------------------------------------------
934 | subroutine zero_vec8(v)
935 |
936 | *** initialize a vector (of length 3) to zero
937 |
938 | double precision v(3)
939 |
940 | v(1)=0.d0
941 | v(2)=0.d0
942 | v(3)=0.d0
943 |
944 | return
945 | end
946 | *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
947 | subroutine moonxyz(mjd,fmjd,rm,lflag)
948 |
949 | *** get low-precision, geocentric coordinates for moon (ECEF)
950 | *** UTC version
951 |
952 | *** input: mjd/fmjd, is Modified Julian Date (and fractional) in UTC time
953 | *** output: rm, is geocentric lunar position vector [m] in ECEF
954 | *** lflag -- leap second table limit flag, false:flag not raised
955 | *** 1."satellite orbits: models, methods, applications" montenbruck & gill(2000)
956 | *** section 3.3.2, pg. 72-73
957 | *** 2."astronomy on the personal computer, 4th ed." montenbruck & pfleger (2005)
958 | *** section 3.2, pg. 38-39 routine MiniMoon
959 |
960 | implicit double precision(a-h,o-z)
961 | double precision rm(3)
962 | integer mjd
963 | double precision fmjd
964 | logical lflag,leapflag
965 | !*** leap second table limit flag
966 | !*** leap second table limit flag
967 | save /limitflag/
968 | common/limitflag/leapflag
969 | common/stuff/rad,pi,pi2
970 |
971 | *** use TT for lunar ephemerides
972 |
973 | leapflag=lflag
974 | !*** UTC time (sec of day)
975 | !*** TT time (sec of day)
976 | !*** TT time (fract. day)
977 | tsecutc=fmjd*86400.d0
978 | tsectt =utc2ttt(tsecutc)
979 | fmjdtt =tsectt/86400.d0
980 | lflag = leapflag
981 |
982 | *** julian centuries since 1.5 january 2000 (J2000)
983 | *** (note: also low precision use of mjd --> tjd)
984 |
985 | !*** Julian Date, TT
986 | !*** julian centuries, TT
987 | tjdtt = mjd+fmjdtt+2400000.5d0
988 | t = (tjdtt - 2451545.d0)/36525.d0
989 |
990 | *** el0 -- mean longitude of Moon (deg)
991 | *** el -- mean anomaly of Moon (deg)
992 | *** elp -- mean anomaly of Sun (deg)
993 | *** f -- mean angular distance of Moon from ascending node (deg)
994 | *** d -- difference between mean longitudes of Sun and Moon (deg)
995 |
996 | *** equations 3.47, p.72
997 |
998 | el0=218.31617d0 + 481267.88088d0*t -1.3972*t
999 | el =134.96292d0 + 477198.86753d0*t
1000 | elp=357.52543d0 + 35999.04944d0*t
1001 | f = 93.27283d0 + 483202.01873d0*t
1002 | d =297.85027d0 + 445267.11135d0*t
1003 |
1004 | *** longitude w.r.t. equinox and ecliptic of year 2000
1005 |
1006 | !*** eq 3.48, p.72
1007 | selond=el0
1008 | * +22640.d0/3600.d0*dsin((el )/rad)
1009 | * + 769.d0/3600.d0*dsin((el+el )/rad)
1010 | * - 4586.d0/3600.d0*dsin((el-d-d )/rad)
1011 | * + 2370.d0/3600.d0*dsin((d+d )/rad)
1012 | * - 668.d0/3600.d0*dsin((elp )/rad)
1013 | * - 412.d0/3600.d0*dsin((f+f )/rad)
1014 | * - 212.d0/3600.d0*dsin((el+el-d-d )/rad)
1015 | * - 206.d0/3600.d0*dsin((el+elp-d-d)/rad)
1016 | * + 192.d0/3600.d0*dsin((el+d+d )/rad)
1017 | * - 165.d0/3600.d0*dsin((elp-d-d )/rad)
1018 | * + 148.d0/3600.d0*dsin((el-elp )/rad)
1019 | * - 125.d0/3600.d0*dsin((d )/rad)
1020 | * - 110.d0/3600.d0*dsin((el+elp )/rad)
1021 | * - 55.d0/3600.d0*dsin((f+f-d-d )/rad)
1022 |
1023 | *** latitude w.r.t. equinox and ecliptic of year 2000
1024 |
1025 | !*** temporary term
1026 | q = 412.d0/3600.d0*dsin((f+f)/rad)
1027 | * +541.d0/3600.d0*dsin((elp)/rad)
1028 |
1029 | !*** eq 3.49, p.72
1030 | selatd=
1031 | * +18520.d0/3600.d0*dsin((f+selond-el0+q)/rad)
1032 | * - 526.d0/3600.d0*dsin((f-d-d )/rad)
1033 | * + 44.d0/3600.d0*dsin((el+f-d-d )/rad)
1034 | * - 31.d0/3600.d0*dsin((-el+f-d-d )/rad)
1035 | * - 25.d0/3600.d0*dsin((-el-el+f )/rad)
1036 | * - 23.d0/3600.d0*dsin((elp+f-d-d )/rad)
1037 | * + 21.d0/3600.d0*dsin((-el+f )/rad)
1038 | * + 11.d0/3600.d0*dsin((-elp+f-d-d)/rad)
1039 |
1040 | *** distance from Earth center to Moon (m)
1041 |
1042 | !*** eq 3.50, p.72
1043 | rse= 385000.d0*1000.d0
1044 | * - 20905.d0*1000.d0*dcos((el )/rad)
1045 | * - 3699.d0*1000.d0*dcos((d+d-el )/rad)
1046 | * - 2956.d0*1000.d0*dcos((d+d )/rad)
1047 | * - 570.d0*1000.d0*dcos((el+el )/rad)
1048 | * + 246.d0*1000.d0*dcos((el+el-d-d )/rad)
1049 | * - 205.d0*1000.d0*dcos((elp-d-d )/rad)
1050 | * - 171.d0*1000.d0*dcos((el+d+d )/rad)
1051 | * - 152.d0*1000.d0*dcos((el+elp-d-d)/rad)
1052 |
1053 | *** convert spherical ecliptic coordinates to equatorial cartesian
1054 |
1055 | *** precession of equinox wrt. J2000 (p.71)
1056 |
1057 | !*** degrees
1058 | selond=selond + 1.3972d0*t
1059 |
1060 | *** position vector of moon (mean equinox & ecliptic of J2000) (EME2000, ICRF)
1061 | *** (plus long. advance due to precession -- eq. above)
1062 |
1063 | !*** obliquity of the J2000 ecliptic
1064 | oblir=23.43929111d0/rad
1065 |
1066 | sselat=dsin(selatd/rad)
1067 | cselat=dcos(selatd/rad)
1068 | sselon=dsin(selond/rad)
1069 | cselon=dcos(selond/rad)
1070 |
1071 | !*** meters !*** eq. 3.51, p.72
1072 | !*** meters !*** eq. 3.51, p.72
1073 | !*** meters !*** eq. 3.51, p.72
1074 | t1 = rse*cselon*cselat
1075 | t2 = rse*sselon*cselat
1076 | t3 = rse* sselat
1077 |
1078 | !*** eq. 3.51, p.72
1079 | call rot1(-oblir,t1,t2,t3,rm1,rm2,rm3)
1080 |
1081 | *** convert position vector of moon to ECEF (ignore polar motion/LOD)
1082 |
1083 | !*** sec 2.3.1,p.33
1084 | !*** eq. 2.89, p.37
1085 | call getghar(mjd,fmjd,ghar)
1086 | call rot3(ghar,rm1,rm2,rm3,rm(1),rm(2),rm(3))
1087 |
1088 | return
1089 | end
1090 | ********************************************************************************
1091 | subroutine getghar(mjd,fmjd,ghar)
1092 |
1093 | *** convert mjd/fmjd in UTC time to Greenwich hour angle (in radians)
1094 |
1095 | *** "satellite orbits: models, methods, applications" montenbruck & gill(2000)
1096 | *** section 2.3.1, pg. 33
1097 |
1098 | implicit double precision(a-h,o-z)
1099 | integer mjd
1100 | double precision fmjd,ghar
1101 | common/stuff/rad,pi,pi2
1102 |
1103 | *** need UTC to get sidereal time ("astronomy on the personal computer", 4th ed)
1104 | *** (pg.43, montenbruck & pfleger, springer, 2005)
1105 |
1106 | !*** UTC time (sec of day)
1107 | !*** UTC time (fract. day)
1108 | tsecutc=fmjd*86400.d0
1109 | fmjdutc=tsecutc/86400.d0
1110 |
1111 | ***** d = MJD - 51544.5d0
1112 | !*** footnote
1113 | !*** days since J2000
1114 | d =(mjd-51544) + (fmjdutc-0.5d0)
1115 |
1116 | *** greenwich hour angle for J2000 (12:00:00 on 1 Jan 2000)
1117 |
1118 | ***** ghad = 100.46061837504d0 + 360.9856473662862d0*d
1119 | !*** eq. 2.85 (+digits)
1120 | !*** corrn. (+digits)
1121 | ghad = 280.46061837504d0 + 360.9856473662862d0*d
1122 |
1123 | **** normalize to 0-360 and convert to radians
1124 |
1125 | i = ghad/360.d0
1126 | ghar =(ghad-i*360.d0)/rad
1127 | 1 if(ghar.gt.pi2) then
1128 | ghar=ghar-pi2
1129 | go to 1
1130 | endif
1131 | 2 if(ghar.lt.0.d0) then
1132 | ghar=ghar+pi2
1133 | go to 2
1134 | endif
1135 |
1136 | return
1137 | end
1138 | ********************************************************************************
1139 | subroutine sunxyz(mjd,fmjd,rs,lflag)
1140 |
1141 | *** get low-precision, geocentric coordinates for sun (ECEF)
1142 |
1143 | *** input, mjd/fmjd, is Modified Julian Date (and fractional) in UTC time
1144 | *** output, rs, is geocentric solar position vector [m] in ECEF
1145 | *** lflag -- leap second table limit flag, false:flag not raised
1146 | *** 1."satellite orbits: models, methods, applications" montenbruck & gill(2000)
1147 | *** section 3.3.2, pg. 70-71
1148 | *** 2."astronomy on the personal computer, 4th ed." montenbruck & pfleger (2005)
1149 | *** section 3.2, pg. 39 routine MiniSun
1150 |
1151 | implicit double precision(a-h,o-z)
1152 | double precision rs(3)
1153 | double precision fmjd
1154 | integer mjd
1155 | logical lflag,leapflag
1156 | !*** leap second table limit flag
1157 | !*** leap second table limit flag
1158 | save /limitflag/
1159 | common/limitflag/leapflag
1160 | common/stuff/rad,pi,pi2
1161 |
1162 | *** mean elements for year 2000, sun ecliptic orbit wrt. Earth
1163 |
1164 | !*** obliquity of the J2000 ecliptic
1165 | obe =23.43929111d0/rad
1166 | sobe=dsin(obe)
1167 | cobe=dcos(obe)
1168 | !*** RAAN + arg.peri. (deg.)
1169 | opod=282.9400d0
1170 |
1171 | *** use TT for solar ephemerides
1172 |
1173 | leapflag=lflag
1174 | !*** UTC time (sec of day)
1175 | !*** TT time (sec of day)
1176 | !*** TT time (fract. day)
1177 | tsecutc=fmjd*86400.d0
1178 | tsectt =utc2ttt(tsecutc)
1179 | fmjdtt =tsectt/86400.d0
1180 | lflag = leapflag
1181 |
1182 | *** julian centuries since 1.5 january 2000 (J2000)
1183 | *** (note: also low precision use of mjd --> tjd)
1184 |
1185 | !*** Julian Date, TT
1186 | !*** julian centuries, TT
1187 | !*** degrees
1188 | !*** radians
1189 | !*** radians
1190 | tjdtt = mjd+fmjdtt+2400000.5d0
1191 | t = (tjdtt - 2451545.d0)/36525.d0
1192 | emdeg = 357.5256d0 + 35999.049d0*t
1193 | em = emdeg/rad
1194 | em2 = em+em
1195 |
1196 | *** series expansions in mean anomaly, em (eq. 3.43, p.71)
1197 |
1198 | !*** m.
1199 | r=(149.619d0-2.499d0*dcos(em)-0.021d0*dcos(em2))*1.d9
1200 | slond=opod + emdeg + (6892.d0*dsin(em)+72.d0*dsin(em2))/3600.d0
1201 |
1202 | *** precession of equinox wrt. J2000 (p.71)
1203 |
1204 | !*** degrees
1205 | slond=slond + 1.3972d0*t
1206 |
1207 | *** position vector of sun (mean equinox & ecliptic of J2000) (EME2000, ICRF)
1208 | *** (plus long. advance due to precession -- eq. above)
1209 |
1210 | !*** radians
1211 | slon =slond/rad
1212 | sslon=dsin(slon)
1213 | cslon=dcos(slon)
1214 |
1215 | !*** meters !*** eq. 3.46, p.71
1216 | !*** meters !*** eq. 3.46, p.71
1217 | !*** meters !*** eq. 3.46, p.71
1218 | rs1 = r*cslon
1219 | rs2 = r*sslon*cobe
1220 | rs3 = r*sslon*sobe
1221 |
1222 | *** convert position vector of sun to ECEF (ignore polar motion/LOD)
1223 |
1224 | !*** sec 2.3.1,p.33
1225 | !*** eq. 2.89, p.37
1226 | call getghar(mjd,fmjd,ghar)
1227 | call rot3(ghar,rs1,rs2,rs3,rs(1),rs(2),rs(3))
1228 |
1229 | return
1230 | end
1231 | ********************************************************************************
1232 | subroutine lhsaaz(u,v,w,ra,az,va)
1233 |
1234 | *** determine range,azimuth,vertical angle from local horizon coord.
1235 |
1236 | implicit double precision(a-h,o-z)
1237 | double precision u,v,w,ra,az,va
1238 |
1239 | s2=u*u+v*v
1240 | r2=s2+w*w
1241 |
1242 | s =dsqrt(s2)
1243 | ra=dsqrt(r2)
1244 |
1245 | az=datan2(v,u)
1246 | va=datan2(w,s)
1247 |
1248 | return
1249 | end
1250 | *-----------------------------------------------------------------------
1251 | subroutine geoxyz(gla,glo,eht,x,y,z)
1252 |
1253 | *** convert geodetic lat, long, ellip ht. to x,y,z
1254 |
1255 | implicit double precision(a-h,o-z)
1256 | double precision gla,glo,eht,x,y,z
1257 | common/comgrs/a,e2
1258 |
1259 | sla=dsin(gla)
1260 | cla=dcos(gla)
1261 | w2=1.d0-e2*sla*sla
1262 | w=dsqrt(w2)
1263 | en=a/w
1264 |
1265 | x=(en+eht)*cla*dcos(glo)
1266 | y=(en+eht)*cla*dsin(glo)
1267 | z=(en*(1.d0-e2)+eht)*sla
1268 |
1269 | return
1270 | end
1271 | *-----------------------------------------------------------------------
1272 | subroutine rge(gla,glo,u,v,w,x,y,z)
1273 |
1274 | *** given a rectangular cartesian system (x,y,z)
1275 | *** compute a geodetic h cartesian sys (u,v,w)
1276 |
1277 | implicit double precision(a-h,o-z)
1278 | double precision gla,glo,u,v,w,x,y,z
1279 |
1280 | sb=dsin(gla)
1281 | cb=dcos(gla)
1282 | sl=dsin(glo)
1283 | cl=dcos(glo)
1284 |
1285 | u=-sb*cl*x-sb*sl*y+cb*z
1286 | v=- sl*x+ cl*y
1287 | w= cb*cl*x+cb*sl*y+sb*z
1288 |
1289 | return
1290 | end
1291 | *-----------------------------------------------------------------------
1292 | subroutine rot1(theta,x,y,z,u,v,w)
1293 |
1294 | *** rotate coordinate axes about 1 axis by angle of theta radians
1295 | *** x,y,z transformed into u,v,w
1296 |
1297 | implicit double precision(a-h,o-z)
1298 | double precision theta,x,y,z,u,v,w
1299 |
1300 | s=dsin(theta)
1301 | c=dcos(theta)
1302 |
1303 | u=x
1304 | v=c*y+s*z
1305 | w=c*z-s*y
1306 |
1307 | return
1308 | end
1309 | *-----------------------------------------------------------------------
1310 | subroutine rot3(theta,x,y,z,u,v,w)
1311 |
1312 | *** rotate coordinate axes about 3 axis by angle of theta radians
1313 | *** x,y,z transformed into u,v,w
1314 |
1315 | implicit double precision(a-h,o-z)
1316 | double precision theta,x,y,z,u,v,w
1317 |
1318 | s=dsin(theta)
1319 | c=dcos(theta)
1320 |
1321 | u=c*x+s*y
1322 | v=c*y-s*x
1323 | w=z
1324 |
1325 | return
1326 | end
1327 | ************************************************************************
1328 | *** time conversion ****************************************************
1329 | ************************************************************************
1330 | subroutine setjd0(iyr,imo,idy)
1331 |
1332 | *** set the integer part of a modified julian date as epoch, mjd0
1333 | *** the modified julian day is derived from civil time as in civmjd()
1334 | *** allows single number expression of time in seconds w.r.t. mjd0
1335 |
1336 | implicit double precision(a-h,o-z)
1337 | integer y,iyr,imo,idy
1338 | save /mjdoff/
1339 | common/mjdoff/mjd0
1340 |
1341 | if(iyr.lt.1900) stop 34587
1342 |
1343 | if(imo.le.2) then
1344 | y=iyr-1
1345 | m=imo+12
1346 | else
1347 | y=iyr
1348 | m=imo
1349 | endif
1350 |
1351 | it1=365.25d0*y
1352 | it2=30.6001d0*(m+1)
1353 | mjd=it1+it2+idy-679019
1354 |
1355 | *** now set the epoch for future time computations
1356 |
1357 | mjd0=mjd
1358 |
1359 | return
1360 | end
1361 | subroutine civjts(iyr,imo,idy,ihr,imn,sec,tsec)
1362 |
1363 | *** convert civil date to time in seconds past mjd epoch, mjd0
1364 | *** requires initialization of mjd0 by setjd0()
1365 |
1366 | *** imo in range 1-12, idy in range 1-31
1367 | *** only valid in range mar-1900 thru feb-2100 (leap year protocols)
1368 | *** ref: hofmann-wellenhof, 2nd ed., pg 34-35
1369 | *** adapted from civmjd()
1370 |
1371 | implicit double precision(a-h,o-z)
1372 | integer y,iyr,imo,idy,ihr,imn
1373 | double precision sec,tsec
1374 | save /mjdoff/
1375 | common/mjdoff/mjd0
1376 |
1377 | if(iyr.lt.1900) stop 34589
1378 |
1379 | if(imo.le.2) then
1380 | y=iyr-1
1381 | m=imo+12
1382 | else
1383 | y=iyr
1384 | m=imo
1385 | endif
1386 |
1387 | it1=365.25d0*y
1388 | it2=30.6001d0*(m+1)
1389 | mjd=it1+it2+idy-679019
1390 |
1391 | tsec=(mjd-mjd0)*86400.d0+3600*ihr+60*imn+sec
1392 |
1393 | return
1394 | end
1395 | subroutine jtsciv(tsec,iyr,imo,idy,ihr,imn,sec)
1396 |
1397 | *** convert time in seconds past mjd0 epoch into civil date
1398 | *** requires initialization of mjd0 by setjd0()
1399 |
1400 | *** imo in range 1-12, idy in range 1-31
1401 | *** only valid in range mar-1900 thru feb-2100
1402 | *** ref: hofmann-wellenhof, 2nd ed., pg 34-35
1403 | *** adapted from mjdciv()
1404 |
1405 | implicit double precision(a-h,o-z)
1406 | integer iyr,imo,idy,ihr,imn
1407 | double precision sec,tsec
1408 | save /mjdoff/
1409 | common/mjdoff/mjd0
1410 |
1411 | mjd=mjd0+tsec/86400.d0
1412 | *** the following equation preserves significant digits
1413 | fmjd=dmod(tsec,86400.d0)/86400.d0
1414 |
1415 | rjd=mjd+fmjd+2400000.5d0
1416 | ia=(rjd+0.5d0)
1417 | ib=ia+1537
1418 | ic=(ib-122.1d0)/365.25d0
1419 | id=365.25d0*ic
1420 | ie=(ib-id)/30.6001d0
1421 |
1422 | *** the fractional part of a julian day is (fractional mjd + 0.5)
1423 | *** therefore, fractional part of julian day + 0.5 is (fractional mjd)
1424 |
1425 | it1=ie*30.6001d0
1426 | idy=ib-id-it1+fmjd
1427 | it2=ie/14.d0
1428 | imo=ie-1-12*it2
1429 | it3=(7+imo)/10.d0
1430 | iyr=ic-4715-it3
1431 |
1432 | tmp=fmjd*24.d0
1433 | ihr=tmp
1434 | tmp=(tmp-ihr)*60.d0
1435 | imn=tmp
1436 | sec=(tmp-imn)*60.d0
1437 |
1438 | return
1439 | end
1440 | ************************************************************************
1441 | subroutine civmjd(iyr,imo,idy,ihr,imn,sec,mjd,fmjd)
1442 |
1443 | *** convert civil date to modified julian date
1444 |
1445 | *** imo in range 1-12, idy in range 1-31
1446 | *** only valid in range mar-1900 thru feb-2100 (leap year protocols)
1447 | *** ref: hofmann-wellenhof, 2nd ed., pg 34-35
1448 | *** operation confirmed against table 3.3 values on pg.34
1449 |
1450 | implicit double precision(a-h,o-z)
1451 | integer y,iyr,imo,idy,ihr,imn,mjd
1452 | double precision sec,fmjd
1453 |
1454 | if(iyr.lt.1900) stop 34588
1455 |
1456 | if(imo.le.2) then
1457 | y=iyr-1
1458 | m=imo+12
1459 | else
1460 | y=iyr
1461 | m=imo
1462 | endif
1463 |
1464 | it1=365.25d0*y
1465 | it2=30.6001d0*(m+1)
1466 | mjd=it1+it2+idy-679019
1467 |
1468 | fmjd=(3600*ihr+60*imn+sec)/86400.d0
1469 |
1470 | return
1471 | end
1472 | *-----------------------------------------------------------------------
1473 | subroutine mjdciv(mjd,fmjd,iyr,imo,idy,ihr,imn,sec)
1474 |
1475 | *** convert modified julian date to civil date
1476 |
1477 | *** imo in range 1-12, idy in range 1-31
1478 | *** only valid in range mar-1900 thru feb-2100
1479 | *** ref: hofmann-wellenhof, 2nd ed., pg 34-35
1480 | *** operation confirmed for leap years (incl. year 2000)
1481 |
1482 | implicit double precision(a-h,o-z)
1483 | integer mjd,iyr,imo,idy,ihr,imn
1484 | double precision fmjd,sec
1485 |
1486 | rjd=mjd+fmjd+2400000.5d0
1487 | ia=(rjd+0.5d0)
1488 | ib=ia+1537
1489 | ic=(ib-122.1d0)/365.25d0
1490 | id=365.25d0*ic
1491 | ie=(ib-id)/30.6001d0
1492 |
1493 | *** the fractional part of a julian day is fractional mjd + 0.5
1494 | *** therefore, fractional part of julian day + 0.5 is fractional mjd
1495 |
1496 | it1=ie*30.6001d0
1497 | idy=ib-id-it1+fmjd
1498 | it2=ie/14.d0
1499 | imo=ie-1-12*it2
1500 | it3=(7+imo)/10.d0
1501 | iyr=ic-4715-it3
1502 |
1503 | tmp=fmjd*24.d0
1504 | ihr=tmp
1505 | tmp=(tmp-ihr)*60.d0
1506 | imn=tmp
1507 | sec=(tmp-imn)*60.d0
1508 |
1509 | return
1510 | end
1511 | ************************************************************************
1512 | *** new supplemental time functions ************************************
1513 | ************************************************************************
1514 | double precision function utc2ttt(tutc)
1515 |
1516 | *** convert utc (sec) to terrestrial time (sec)
1517 |
1518 | implicit double precision(a-h,o-z)
1519 | double precision tutc
1520 |
1521 | ttai = utc2tai(tutc)
1522 | utc2ttt= tai2tt(ttai)
1523 |
1524 | return
1525 | end
1526 | *-----------------------------------------------------------------------
1527 | double precision function gps2ttt(tgps)
1528 |
1529 | *** convert gps time (sec) to terrestrial time (sec)
1530 |
1531 | implicit double precision(a-h,o-z)
1532 | double precision tgps
1533 |
1534 | ttai = gps2tai(tgps)
1535 | gps2ttt= tai2tt(ttai)
1536 |
1537 | return
1538 | end
1539 | *-----------------------------------------------------------------------
1540 | double precision function utc2tai(tutc)
1541 |
1542 | *** convert utc (sec) to tai (sec)
1543 |
1544 | implicit double precision(a-h,o-z)
1545 | double precision tutc
1546 |
1547 | utc2tai = tutc - getutcmtai(tutc)
1548 |
1549 | return
1550 | end
1551 | *-----------------------------------------------------------------------
1552 | double precision function getutcmtai(tsec)
1553 |
1554 | *** get utc - tai (s)
1555 |
1556 | ***** "Julian Date Converter"
1557 | ***** http://aa.usno.navy.mil/data/docs/JulianDate.php
1558 | ***** http://www.csgnetwork.com/julianmodifdateconv.html
1559 |
1560 | implicit double precision(a-h,o-z)
1561 | double precision tsec
1562 | !*** upper limit, leap second table, 2025dec28
1563 | !*** lower limit, leap second table, 1972jan01
1564 | parameter(MJDUPPER=61037)
1565 | parameter(MJDLOWER=41317)
1566 |
1567 | !*** leap second table limit flag
1568 | logical leapflag
1569 | save /limitflag/
1570 | !*** leap second table limit flag
1571 | common/limitflag/leapflag
1572 |
1573 | save /mjdoff/
1574 | common/mjdoff/mjd0
1575 |
1576 | *** clone for tests (and do any rollover)
1577 |
1578 | ttsec=tsec
1579 | mjd0t=mjd0
1580 |
1581 | 1 if(ttsec.ge.86400.d0) then
1582 | ttsec=ttsec-86400.d0
1583 | mjd0t=mjd0t+1
1584 | go to 1
1585 | endif
1586 |
1587 | 2 if(ttsec.lt.0.d0) then
1588 | ttsec=ttsec+86400.d0
1589 | mjd0t=mjd0t-1
1590 | go to 2
1591 | endif
1592 |
1593 | *** test upper table limit (upper limit set by bulletin C memos)
1594 |
1595 | if(mjd0t.gt.MJDUPPER) then
1596 | !*** true means flag *IS* raised
1597 | !*** return the upper table value
1598 | leapflag =.true.
1599 | getutcmtai= -37.d0
1600 | return
1601 | endif
1602 |
1603 | *** test lower table limit
1604 |
1605 | if(mjd0t.lt.MJDLOWER) then
1606 | !*** true means flag *IS* raised
1607 | !*** return the lower table value
1608 | leapflag=.true.
1609 | getutcmtai= -10.d0
1610 | return
1611 | endif
1612 |
1613 | ***** https://maia.usno.navy.mil/ser7/tai-utc.dat
1614 | *** 1972 JAN 1 =JD 2441317.5 TAI-UTC= 10.0s
1615 | *** 1972 JUL 1 =JD 2441499.5 TAI-UTC= 11.0s
1616 | *** 1973 JAN 1 =JD 2441683.5 TAI-UTC= 12.0s
1617 | *** 1974 JAN 1 =JD 2442048.5 TAI-UTC= 13.0s
1618 | *** 1975 JAN 1 =JD 2442413.5 TAI-UTC= 14.0s
1619 | *** 1976 JAN 1 =JD 2442778.5 TAI-UTC= 15.0s
1620 | *** 1977 JAN 1 =JD 2443144.5 TAI-UTC= 16.0s
1621 | *** 1978 JAN 1 =JD 2443509.5 TAI-UTC= 17.0s
1622 | *** 1979 JAN 1 =JD 2443874.5 TAI-UTC= 18.0s
1623 | *** 1980 JAN 1 =JD 2444239.5 TAI-UTC= 19.0s
1624 | *** 1981 JUL 1 =JD 2444786.5 TAI-UTC= 20.0s
1625 | *** 1982 JUL 1 =JD 2445151.5 TAI-UTC= 21.0s
1626 | *** 1983 JUL 1 =JD 2445516.5 TAI-UTC= 22.0s
1627 | *** 1985 JUL 1 =JD 2446247.5 TAI-UTC= 23.0s
1628 | *** 1988 JAN 1 =JD 2447161.5 TAI-UTC= 24.0s
1629 | *** 1990 JAN 1 =JD 2447892.5 TAI-UTC= 25.0s
1630 | *** 1991 JAN 1 =JD 2448257.5 TAI-UTC= 26.0s
1631 | *** 1992 JUL 1 =JD 2448804.5 TAI-UTC= 27.0s
1632 | *** 1993 JUL 1 =JD 2449169.5 TAI-UTC= 28.0s
1633 | *** 1994 JUL 1 =JD 2449534.5 TAI-UTC= 29.0s
1634 | *** 1996 JAN 1 =JD 2450083.5 TAI-UTC= 30.0s
1635 | *** 1997 JUL 1 =JD 2450630.5 TAI-UTC= 31.0s
1636 | *** 1999 JAN 1 =JD 2451179.5 TAI-UTC= 32.0s
1637 | *** 2006 JAN 1 =JD 2453736.5 TAI-UTC= 33.0s
1638 | *** 2009 JAN 1 =JD 2454832.5 TAI-UTC= 34.0s
1639 | *** 2012 JUL 1 =JD 2456109.5 TAI-UTC= 35.0s
1640 | *** 2015 JUL 1 =JD 2457204.5 TAI-UTC= 36.0s
1641 | *** 2017 JAN 1 =JD 2457754.5 TAI-UTC= 37.0s
1642 | ***** other leap second references at:
1643 | ***** http://hpiers.obspm.fr/eoppc/bul/bulc/Leap_Second_History.dat
1644 | ***** http://hpiers.obspm.fr/eoppc/bul/bulc/bulletinc.dat
1645 | ***** File expires on 28 December 2025
1646 |
1647 | *** test against newest leaps first
1648 |
1649 | if (mjd0t.ge.57754) then
1650 | !*** 2017 JAN 1 = 57754
1651 | tai_utc = 37.d0
1652 | elseif(mjd0t.ge.57204) then
1653 | !*** 2015 JUL 1 = 57204
1654 | tai_utc = 36.d0
1655 | elseif(mjd0t.ge.56109) then
1656 | !*** 2012 JUL 1 = 56109
1657 | tai_utc = 35.d0
1658 | elseif(mjd0t.ge.54832) then
1659 | !*** 2009 JAN 1 = 54832
1660 | tai_utc = 34.d0
1661 | elseif(mjd0t.ge.53736) then
1662 | !*** 2006 JAN 1 = 53736
1663 | tai_utc = 33.d0
1664 | elseif(mjd0t.ge.51179) then
1665 | !*** 1999 JAN 1 = 51179
1666 | tai_utc = 32.d0
1667 | elseif(mjd0t.ge.50630) then
1668 | !*** 1997 JUL 1 = 50630
1669 | tai_utc = 31.d0
1670 | elseif(mjd0t.ge.50083) then
1671 | !*** 1996 JAN 1 = 50083
1672 | tai_utc = 30.d0
1673 | elseif(mjd0t.ge.49534) then
1674 | !*** 1994 JUL 1 = 49534
1675 | tai_utc = 29.d0
1676 | elseif(mjd0t.ge.49169) then
1677 | !*** 1993 JUL 1 = 49169
1678 | tai_utc = 28.d0
1679 | elseif(mjd0t.ge.48804) then
1680 | !*** 1992 JUL 1 = 48804
1681 | tai_utc = 27.d0
1682 | elseif(mjd0t.ge.48257) then
1683 | !*** 1991 JAN 1 = 48257
1684 | tai_utc = 26.d0
1685 | elseif(mjd0t.ge.47892) then
1686 | !*** 1990 JAN 1 = 47892
1687 | tai_utc = 25.d0
1688 | elseif(mjd0t.ge.47161) then
1689 | !*** 1988 JAN 1 = 47161
1690 | tai_utc = 24.d0
1691 | elseif(mjd0t.ge.46247) then
1692 | !*** 1985 JUL 1 = 46247
1693 | tai_utc = 23.d0
1694 | elseif(mjd0t.ge.45516) then
1695 | !*** 1983 JUL 1 = 45516
1696 | tai_utc = 22.d0
1697 | elseif(mjd0t.ge.45151) then
1698 | !*** 1982 JUL 1 = 45151
1699 | tai_utc = 21.d0
1700 | elseif(mjd0t.ge.44786) then
1701 | !*** 1981 JUL 1 = 44786
1702 | tai_utc = 20.d0
1703 | elseif(mjd0t.ge.44239) then
1704 | !*** 1980 JAN 1 = 44239
1705 | tai_utc = 19.d0
1706 | elseif(mjd0t.ge.43874) then
1707 | !*** 1979 JAN 1 = 43874
1708 | tai_utc = 18.d0
1709 | elseif(mjd0t.ge.43509) then
1710 | !*** 1978 JAN 1 = 43509
1711 | tai_utc = 17.d0
1712 | elseif(mjd0t.ge.43144) then
1713 | !*** 1977 JAN 1 = 43144
1714 | tai_utc = 16.d0
1715 | elseif(mjd0t.ge.42778) then
1716 | !*** 1976 JAN 1 = 42778
1717 | tai_utc = 15.d0
1718 | elseif(mjd0t.ge.42413) then
1719 | !*** 1975 JAN 1 = 42413
1720 | tai_utc = 14.d0
1721 | elseif(mjd0t.ge.42048) then
1722 | !*** 1974 JAN 1 = 42048
1723 | tai_utc = 13.d0
1724 | elseif(mjd0t.ge.41683) then
1725 | !*** 1973 JAN 1 = 41683
1726 | tai_utc = 12.d0
1727 | elseif(mjd0t.ge.41499) then
1728 | !*** 1972 JUL 1 = 41499
1729 | tai_utc = 11.d0
1730 | elseif(mjd0t.ge.41317) then
1731 | !*** 1972 JAN 1 = 41317
1732 | tai_utc = 10.d0
1733 |
1734 | *** should never, ever get here
1735 |
1736 | else
1737 | write(*,*) 'FATAL ERROR --'
1738 | write(*,*) 'fell thru tests in gpsleap()'
1739 | stop 66768
1740 | endif
1741 |
1742 | *** return utc - tai (in seconds)
1743 |
1744 | getutcmtai = -tai_utc
1745 |
1746 | return
1747 | end
1748 | *-----------------------------------------------------------------------
1749 | double precision function tai2tt(ttai)
1750 |
1751 | *** convert tai (sec) to terrestrial time (sec)
1752 |
1753 | implicit double precision(a-h,o-z)
1754 | double precision ttai
1755 |
1756 | ***** http://tycho.usno.navy.mil/systime.html
1757 | tai2tt = ttai + 32.184d0
1758 |
1759 | return
1760 | end
1761 | *-----------------------------------------------------------------------
1762 | double precision function gps2tai(tgps)
1763 |
1764 | *** convert gps time (sec) to tai (sec)
1765 |
1766 | implicit double precision(a-h,o-z)
1767 | double precision tgps
1768 |
1769 | ***** http://leapsecond.com/java/gpsclock.htm
1770 | ***** http://tycho.usno.navy.mil/leapsec.html
1771 | gps2tai = tgps + 19.d0
1772 |
1773 | return
1774 | end
1775 |
--------------------------------------------------------------------------------
/tests/grid.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Author: Zhang Yunjun, Jan 2021
3 | # Copyright 2020, by the California Institute of Technology.
4 |
5 |
6 | import os
7 | import sys
8 | import datetime as dt
9 |
10 | import numpy as np
11 |
12 | import pysolid
13 |
14 |
15 | if __name__ == '__main__':
16 |
17 | # print the file/module path
18 | print('-'*50)
19 | print(os.path.abspath(__file__))
20 |
21 | # prepare inputs
22 | dt_obj = dt.datetime(2020, 12, 25, 14, 7, 44)
23 | atr = {
24 | 'LENGTH' : 400,
25 | 'WIDTH' : 500,
26 | 'X_FIRST' : -118.2,
27 | 'Y_FIRST' : 33.8,
28 | 'X_STEP' : 0.000833333,
29 | 'Y_STEP' : -0.000833333,
30 | }
31 |
32 | # reference
33 | # calculated based on version 0.3.2.post6 on Jun 24, 2024
34 | # env: macOS with python-3.10, numpy-1.24
35 | # install: manual compilation via f2py
36 | tide_e_80_100 = np.array(
37 | [[0.01628786, 0.01630887, 0.01633078, 0.01635247, 0.01637394],
38 | [0.01633248, 0.01635348, 0.01637538, 0.01639706, 0.01641851],
39 | [0.01638009, 0.01640107, 0.01642296, 0.01644462, 0.01646606],
40 | [0.01642767, 0.01644864, 0.01647052, 0.01649217, 0.01651359],
41 | [0.01647523, 0.01649619, 0.01651805, 0.01653968, 0.01656109]],
42 | )
43 | tide_n_80_100 = np.array(
44 | [[-0.02406203, -0.02412341, -0.02418807, -0.02425273, -0.0243174 ],
45 | [-0.02407558, -0.02413699, -0.02420168, -0.02426637, -0.02433107],
46 | [-0.02408992, -0.02415136, -0.02421608, -0.02428081, -0.02434554],
47 | [-0.02410413, -0.0241656 , -0.02423036, -0.02429511, -0.02435988],
48 | [-0.02411821, -0.02417972, -0.0242445 , -0.02430929, -0.02437408]],
49 | )
50 | tide_u_80_100 = np.array(
51 | [[-0.05548462, -0.05533455, -0.05517631, -0.05501789, -0.05485928],
52 | [-0.05529561, -0.0551451 , -0.05498639, -0.0548275 , -0.05466843],
53 | [-0.05509374, -0.05494276, -0.05478355, -0.05462417, -0.05446461],
54 | [-0.05489176, -0.05474031, -0.05458061, -0.05442073, -0.05426067],
55 | [-0.05468968, -0.05453776, -0.05437757, -0.05421719, -0.05405664]],
56 | )
57 |
58 | # calculate
59 | (tide_e,
60 | tide_n,
61 | tide_u) = pysolid.calc_solid_earth_tides_grid(dt_obj, atr, verbose=True)
62 |
63 | # compare
64 | assert np.allclose(tide_e[::80, ::100], tide_e_80_100)
65 | assert np.allclose(tide_n[::80, ::100], tide_n_80_100)
66 | assert np.allclose(tide_u[::80, ::100], tide_u_80_100)
67 |
68 | # plot
69 | out_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'pic'))
70 | os.makedirs(out_dir, exist_ok=True)
71 |
72 | out_fig = os.path.join(out_dir, 'grid.png')
73 | pysolid.plot_solid_earth_tides_grid(
74 | tide_e, tide_n, tide_u, dt_obj,
75 | out_fig=out_fig,
76 | display=False)
77 |
78 | # open the plotted figures
79 | if sys.platform in ['linux']:
80 | os.system(f'display {out_fig}')
81 | elif sys.platform in ['darwin']:
82 | os.system(f'open {out_fig}')
83 | elif sys.platform.startswith('win'):
84 | os.system(out_fig)
85 | else:
86 | print(f'Unknown OS system ({sys.platform}). Check results in file: {out_fig}.')
87 |
--------------------------------------------------------------------------------
/tests/point.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Author: Zhang Yunjun, Jan 2021
3 | # Copyright 2020, by the California Institute of Technology.
4 |
5 |
6 | import os
7 | import sys
8 | import datetime as dt
9 |
10 | import numpy as np
11 |
12 | import pysolid
13 |
14 |
15 | if __name__ == '__main__':
16 |
17 | # print the file/module path
18 | print('-'*50)
19 | print(os.path.abspath(__file__))
20 |
21 | # prepare inputs
22 | lat, lon = 34.0, -118.0 # Los Angles, CA
23 | dt_obj0 = dt.datetime(2020, 11, 5, 12, 0, 0)
24 | dt_obj1 = dt.datetime(2020, 12, 31, 0, 0, 0)
25 |
26 | # reference
27 | # calculated based on version 0.3.2.post6 on Jun 24, 2024
28 | # env: macOS with python-3.10, numpy-1.24
29 | # install: manual compilation via f2py
30 | dt_out_8000 = np.array(
31 | [dt.datetime(2020, 11, 5, 12, 0),
32 | dt.datetime(2020, 11, 11, 1, 20),
33 | dt.datetime(2020, 11, 16, 14, 40),
34 | dt.datetime(2020, 11, 22, 4, 0),
35 | dt.datetime(2020, 11, 27, 17, 20),
36 | dt.datetime(2020, 12, 3, 6, 40),
37 | dt.datetime(2020, 12, 8, 20, 0),
38 | dt.datetime(2020, 12, 14, 9, 20),
39 | dt.datetime(2020, 12, 19, 22, 40),
40 | dt.datetime(2020, 12, 25, 12, 0)], dtype=object,
41 | )
42 | tide_e_8000 = np.array(
43 | [-0.02975027, 0.04146837, -0.02690945, -0.00019223, 0.01624152,
44 | 0.0532655 , -0.02140918, -0.05554432, 0.01371739, -0.00516968],
45 | )
46 | tide_n_8000 = np.array(
47 | [-0.01275229, -0.02834036, 0.00886857, -0.03247227, -0.05237735,
48 | -0.00590791, -0.01990448, -0.01964124, -0.04439581, -0.00410378],
49 | )
50 | tide_u_8000 = np.array(
51 | [ 0.16008235, -0.05721991, -0.15654693, -0.00041214, 0.03041098,
52 | 0.13082217, -0.1006462 , 0.24870719, -0.02648802, -0.08420228],
53 | )
54 |
55 | # calculate
56 | (dt_out,
57 | tide_e,
58 | tide_n,
59 | tide_u) = pysolid.calc_solid_earth_tides_point(lat, lon, dt_obj0, dt_obj1, verbose=False)
60 |
61 | # compare
62 | assert all(dt_out[::8000] == dt_out_8000)
63 | assert np.allclose(tide_e[::8000], tide_e_8000)
64 | assert np.allclose(tide_n[::8000], tide_n_8000)
65 | assert np.allclose(tide_u[::8000], tide_u_8000)
66 |
67 | # plot
68 | out_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'pic'))
69 | os.makedirs(out_dir, exist_ok=True)
70 |
71 | out_fig = os.path.join(out_dir, 'point.png')
72 | pysolid.plot_solid_earth_tides_point(
73 | dt_out, tide_e, tide_n, tide_u,
74 | lalo=[lat, lon],
75 | out_fig=out_fig,
76 | display=False)
77 |
78 | # open the saved figure
79 | if sys.platform in ['linux']:
80 | os.system(f'display {out_fig}')
81 | elif sys.platform in ['darwin']:
82 | os.system(f'open {out_fig}')
83 | elif sys.platform.startswith('win'):
84 | os.system(out_fig)
85 | else:
86 | print(f'Unknown OS system ({sys.platform}). Check results in file: {out_fig}.')
87 |
--------------------------------------------------------------------------------
/tests/requirements.txt:
--------------------------------------------------------------------------------
1 | # for packaging and installation
2 | #fortran-compiler # Fortran compiler across platforms through conda-forge channel
3 | meson
4 | pip
5 | setuptools_scm>=6.2
6 | # for testing
7 | matplotlib
8 |
--------------------------------------------------------------------------------