From 77127a8b41f7659c2ddf033260796a51f7129a42 Mon Sep 17 00:00:00 2001 From: Joseph Capriotti Date: Wed, 15 Oct 2025 08:59:53 -0600 Subject: [PATCH] Updates for release 0.8.0 Introduces `__all__` for public API definition in EM modules to clarify intended usage. Adds a new kernels module and integrates it into documentation. Updates release workflow to install the package with extensions and build documentation. Minor fixes and improvements to the documentation build process. --- .github/workflows/release.yml | 16 ++++++-- .github/workflows/test_with_conda.yml | 2 +- docs/api/geoana.kernels.rst | 1 + docs/api/index.rst | 1 + geoana/earthquake/oksar.py | 6 +++ geoana/em/base.py | 8 +++- geoana/em/fdem/base.py | 7 ++++ geoana/em/fdem/halfspace.py | 3 ++ geoana/em/fdem/layered.py | 3 ++ geoana/em/fdem/simple_functions.py | 4 ++ geoana/em/fdem/wholespace.py | 5 +++ geoana/em/static/__init__.py | 8 ++-- geoana/em/static/freespace.py | 3 ++ geoana/em/static/sphere.py | 4 ++ geoana/em/static/wholespace.py | 3 +- geoana/em/tdem/__init__.py | 14 ++++++- geoana/em/tdem/base.py | 6 +++ geoana/em/tdem/halfspace.py | 3 ++ geoana/em/tdem/reference.py | 7 ++++ geoana/em/tdem/simple_functions.py | 10 +++++ geoana/em/tdem/wholespace.py | 5 +++ geoana/gravity.py | 5 +++ geoana/kernels/__init__.py | 40 ++++++++++++++++++- geoana/kernels/potential_field_prism.py | 12 ++++++ .../kernels/tranverse_electric_reflections.py | 13 ++++-- geoana/plotting_utils.py | 5 ++- geoana/shapes.py | 4 ++ geoana/spatial.py | 28 ++++++++++++- geoana/utils.py | 22 ++++++++++ 29 files changed, 229 insertions(+), 19 deletions(-) create mode 100644 docs/api/geoana.kernels.rst diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 40a01575..d1d147d6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,30 +83,35 @@ jobs: uses: actions/setup-python@v2 with: python-version: "3.13" - + - name: Create Conda environment file run: | python -m pip install pyyaml python .github/make_ci_environ.py env: PYTHON_VERSION: "3.13" + FREE_THREADED: false + NO_DOC_BUILD: false + NO_NUMBA: true ENV_NAME: geoana-test - NO_NUMBA: "true" - name: Setup Conda uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true - environment-file: .github/environment_ci.yml - activate-environment: geoana-test + environment-file: environment_ci.yml + activate-environment: geoana-test + - name: Install Our Package run: | pip install --no-build-isolation --editable . --config-settings=setup-args="-Dwith_extensions=true" + - name: Build documentation run: | cd docs make html cd .. + - name: GitHub Pages uses: crazy-max/ghaction-github-pages@v2.5.0 with: @@ -136,14 +141,17 @@ jobs: pattern: cibw-* path: dist merge-multiple: true + - name: Release to github uses: softprops/action-gh-release@v1 with: files: dist/* generate_release_notes: true prerelease: false + - name: Remove anywheel before pypi upload run: rm -f dist/geoana*none-any.whl + - name: Upload wheels to pypi uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/.github/workflows/test_with_conda.yml b/.github/workflows/test_with_conda.yml index 84f72c2b..38b81572 100644 --- a/.github/workflows/test_with_conda.yml +++ b/.github/workflows/test_with_conda.yml @@ -37,7 +37,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: "3.13" - + - name: Create Conda environment file run: | python -m pip install pyyaml diff --git a/docs/api/geoana.kernels.rst b/docs/api/geoana.kernels.rst new file mode 100644 index 00000000..2db9a60e --- /dev/null +++ b/docs/api/geoana.kernels.rst @@ -0,0 +1 @@ +.. automodule:: geoana.kernels diff --git a/docs/api/index.rst b/docs/api/index.rst index dfc9608d..5d7c29e3 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -50,3 +50,4 @@ functions for performing useful operations. geoana.utils geoana.spatial geoana.plotting_utils + geoana.kernels \ No newline at end of file diff --git a/geoana/earthquake/oksar.py b/geoana/earthquake/oksar.py index e0978ab8..109d92ef 100644 --- a/geoana/earthquake/oksar.py +++ b/geoana/earthquake/oksar.py @@ -27,6 +27,12 @@ from datetime import datetime from geoana.utils import requires +__all__ = [ + "EarthquakeInterferogram", + "Oksar", + "example", +] + def _date_time_from_json(value): if len(value) == 10: return datetime.strptime(value.replace('-', '/'),'%Y/%m/%d') diff --git a/geoana/em/base.py b/geoana/em/base.py index 4c089fc8..4a5792ba 100644 --- a/geoana/em/base.py +++ b/geoana/em/base.py @@ -1,13 +1,19 @@ import numpy as np from scipy.constants import mu_0, epsilon_0 +__all__ = [ + "BaseEM", + "BaseDipole", + "BaseElectricDipole", + "BaseMagneticDipole", + "BaseLineCurrent", +] ############################################################################### # # # Base Classes # # # ############################################################################### - class BaseEM: """Base electromagnetics class. diff --git a/geoana/em/fdem/base.py b/geoana/em/fdem/base.py index d8e30b77..8c575f76 100644 --- a/geoana/em/fdem/base.py +++ b/geoana/em/fdem/base.py @@ -2,6 +2,13 @@ import numpy as np from scipy.constants import mu_0, epsilon_0 +__all__ = [ + "omega", + "wavenumber", + "skin_depth", + "sigma_hat", + "BaseFDEM", +] ############################################################################### # # # Utility Functions # diff --git a/geoana/em/fdem/halfspace.py b/geoana/em/fdem/halfspace.py index 6646c17c..dce8a390 100644 --- a/geoana/em/fdem/halfspace.py +++ b/geoana/em/fdem/halfspace.py @@ -4,6 +4,9 @@ from geoana.em.base import BaseMagneticDipole from geoana.em.fdem.base import BaseFDEM +__all__ = [ + "MagneticDipoleHalfSpace", +] class MagneticDipoleHalfSpace(BaseFDEM, BaseMagneticDipole): r"""Class for a harmonic magnetic dipole in a wholespace. diff --git a/geoana/em/fdem/layered.py b/geoana/em/fdem/layered.py index bf363adc..3cf84ba4 100644 --- a/geoana/em/fdem/layered.py +++ b/geoana/em/fdem/layered.py @@ -5,6 +5,9 @@ from geoana.kernels.tranverse_electric_reflections import rTE_forward import libdlf +__all__ = [ + "MagneticDipoleLayeredHalfSpace", +] class MagneticDipoleLayeredHalfSpace(BaseFDEM, BaseMagneticDipole): """Simulation class for a harmonic magnetic dipole over a layered halfspace. diff --git a/geoana/em/fdem/simple_functions.py b/geoana/em/fdem/simple_functions.py index fba6d2ad..c43cc6b1 100644 --- a/geoana/em/fdem/simple_functions.py +++ b/geoana/em/fdem/simple_functions.py @@ -1,6 +1,10 @@ import numpy as np from scipy.constants import mu_0 +__all__ = [ + "vertical_magnetic_field_horizontal_loop", + "vertical_magnetic_flux_horizontal_loop", +] def vertical_magnetic_field_horizontal_loop( frequencies, sigma=1.0, mu=mu_0, radius=1.0, current=1.0, turns=1, secondary=True diff --git a/geoana/em/fdem/wholespace.py b/geoana/em/fdem/wholespace.py index d9d0cedb..851156e0 100644 --- a/geoana/em/fdem/wholespace.py +++ b/geoana/em/fdem/wholespace.py @@ -3,6 +3,11 @@ from geoana.utils import check_xyz_dim, append_ndim from geoana.em.base import BaseElectricDipole, BaseMagneticDipole +__all__ = [ + "ElectricDipoleWholeSpace", + "MagneticDipoleWholeSpace", + "HarmonicPlaneWave", +] class ElectricDipoleWholeSpace(BaseFDEM, BaseElectricDipole): r"""Class for simulating the fields and fluxes for a harmonic electric dipole in a wholespace. diff --git a/geoana/em/static/__init__.py b/geoana/em/static/__init__.py index 0014bf8c..9212bc48 100644 --- a/geoana/em/static/__init__.py +++ b/geoana/em/static/__init__.py @@ -23,11 +23,13 @@ PointCurrentWholeSpace PointCurrentHalfSpace DipoleHalfSpace + MagneticPrism """ -from geoana.em.static.sphere import ElectrostaticSphere - -from geoana.em.static.sphere import MagnetostaticSphere +from geoana.em.static.sphere import ( + ElectrostaticSphere, + MagnetostaticSphere, +) from geoana.em.static.wholespace import ( MagneticDipoleWholeSpace, diff --git a/geoana/em/static/freespace.py b/geoana/em/static/freespace.py index 9ed715cc..810efead 100644 --- a/geoana/em/static/freespace.py +++ b/geoana/em/static/freespace.py @@ -15,6 +15,9 @@ prism_fxyz, ) +__all__ = [ + "MagneticPrism", +] class MagneticPrism(BasePrism): """Class for magnetic field solutions for a prism. diff --git a/geoana/em/static/sphere.py b/geoana/em/static/sphere.py index 99e0d2e0..1e2182ac 100644 --- a/geoana/em/static/sphere.py +++ b/geoana/em/static/sphere.py @@ -2,6 +2,10 @@ from scipy.constants import epsilon_0 from geoana.utils import check_xyz_dim +__all__ = [ + "ElectrostaticSphere", + "MagnetostaticSphere", +] class ElectrostaticSphere: """Class for electrostatic solutions for a sphere in a wholespace. diff --git a/geoana/em/static/wholespace.py b/geoana/em/static/wholespace.py index 32612d56..5c6058b0 100644 --- a/geoana/em/static/wholespace.py +++ b/geoana/em/static/wholespace.py @@ -7,7 +7,8 @@ __all__ = [ "MagneticDipoleWholeSpace", "CircularLoopWholeSpace", - "MagneticPoleWholeSpace", "PointCurrentWholeSpace" + "MagneticPoleWholeSpace", "PointCurrentWholeSpace", + "LineCurrentWholeSpace" ] from ...kernels import prism_fzy diff --git a/geoana/em/tdem/__init__.py b/geoana/em/tdem/__init__.py index 38f3d206..779d09e6 100644 --- a/geoana/em/tdem/__init__.py +++ b/geoana/em/tdem/__init__.py @@ -34,7 +34,19 @@ magnetic_field_time_deriv_magnetic_dipole magnetic_flux_vertical_magnetic_dipole magnetic_flux_time_deriv_magnetic_dipole - + +Reference Functions +=================== +Useful reference functions from Ward and Hohmann. + +.. autosummary:: + :toctree: generated/ + + reference.hz_from_vert_4_69a + reference.dhz_from_vert_4_70 + reference.hp_from_vert_4_72 + reference.dhp_from_vert_4_74 + """ from geoana.em.tdem.base import peak_time, diffusion_distance, theta, BaseTDEM diff --git a/geoana/em/tdem/base.py b/geoana/em/tdem/base.py index ccc97c63..e5b918bd 100644 --- a/geoana/em/tdem/base.py +++ b/geoana/em/tdem/base.py @@ -2,6 +2,12 @@ import numpy as np from scipy.constants import mu_0 +__all__ = [ + "peak_time", + "diffusion_distance", + "theta", + "BaseTDEM", +] ############################################################################### # # diff --git a/geoana/em/tdem/halfspace.py b/geoana/em/tdem/halfspace.py index ae09fc3d..7260dc97 100644 --- a/geoana/em/tdem/halfspace.py +++ b/geoana/em/tdem/halfspace.py @@ -6,6 +6,9 @@ from geoana.em.tdem.simple_functions import magnetic_field_vertical_magnetic_dipole, magnetic_field_time_deriv_magnetic_dipole from geoana.utils import check_xyz_dim +__all__ = [ + "VerticalMagneticDipoleHalfSpace", +] class VerticalMagneticDipoleHalfSpace(BaseTDEM, BaseMagneticDipole): """Transient of a vertical magnetic dipole in a half space. diff --git a/geoana/em/tdem/reference.py b/geoana/em/tdem/reference.py index bdb60596..843e6f40 100644 --- a/geoana/em/tdem/reference.py +++ b/geoana/em/tdem/reference.py @@ -7,6 +7,13 @@ from scipy.special import erf, ive, iv import numpy as np +__all__ = [ + "hz_from_vert_4_69a", + "dhz_from_vert_4_70", + "hp_from_vert_4_72", + "dhp_from_vert_4_74", +] + def hz_from_vert_4_69a(m, theta, rho): front = m / (4 * np.pi * rho**3) tp = theta * rho diff --git a/geoana/em/tdem/simple_functions.py b/geoana/em/tdem/simple_functions.py index 43b96a84..4e6a6822 100644 --- a/geoana/em/tdem/simple_functions.py +++ b/geoana/em/tdem/simple_functions.py @@ -6,6 +6,16 @@ from geoana.em.tdem.base import theta from geoana.utils import append_ndim, check_xyz_dim +__all__ = [ + "vertical_magnetic_field_horizontal_loop", + "vertical_magnetic_flux_horizontal_loop", + "vertical_magnetic_field_time_deriv_horizontal_loop", + "vertical_magnetic_flux_time_deriv_horizontal_loop", + "magnetic_field_vertical_magnetic_dipole", + "magnetic_field_time_deriv_magnetic_dipole", + "magnetic_flux_vertical_magnetic_dipole", + "magnetic_flux_time_deriv_magnetic_dipole", +] def vertical_magnetic_field_horizontal_loop( t, sigma=1.0, mu=mu_0, radius=1.0, current=1.0, turns=1 diff --git a/geoana/em/tdem/wholespace.py b/geoana/em/tdem/wholespace.py index f862b726..8a56ba28 100644 --- a/geoana/em/tdem/wholespace.py +++ b/geoana/em/tdem/wholespace.py @@ -6,6 +6,11 @@ from geoana.utils import check_xyz_dim, append_ndim from geoana.em.base import BaseElectricDipole +__all__ = [ + "ElectricDipoleWholeSpace", + "TransientPlaneWave", +] + ############################################################################### # # # Classes # diff --git a/geoana/gravity.py b/geoana/gravity.py index 64fb1eda..5441f5b9 100644 --- a/geoana/gravity.py +++ b/geoana/gravity.py @@ -23,6 +23,11 @@ from geoana.shapes import BasePrism from geoana.kernels import prism_f, prism_fz, prism_fzx, prism_fzy, prism_fzz +__all__ = [ + "PointMass", + "Sphere", + "Prism", +] class PointMass: """Class for gravitational solutions for a point mass. diff --git a/geoana/kernels/__init__.py b/geoana/kernels/__init__.py index 55cc5e2a..b94aef5a 100644 --- a/geoana/kernels/__init__.py +++ b/geoana/kernels/__init__.py @@ -1,4 +1,42 @@ -"""This module contains kernals for (semi-)analytic geophysical responses +""" +=========================================== +Kernel Functions (:mod:`geoana.kernels`) +=========================================== +.. currentmodule:: geoana.kernels + +This module contains kernals for (semi-)analytic geophysical responses +These are optionally available as compiled extensions with fallback +`numpy` implementations. + +Inverse Distance Integral Kernels +================================== +Kernels used to evaluate analytic integrals of the +1/r volume integrand (and it's derivatives) + +Note that if `numba` is installed and `geoana` has compiled +extensions, these are callable in no-python mode. + +.. autosummary:: + :toctree: generated/ + + prism_f + prism_fz + prism_fzz + prism_fzx + prism_fzy + prism_fzzz + prism_fxxy + prism_fxxz + prism_fxyz + +Layered Electromagnetic Reflection Kernels +========================================== +.. autosummary:: + :toctree: generated/ + + rTE_forward + rTE_gradient + """ from geoana.kernels.tranverse_electric_reflections import rTE_forward, rTE_gradient from geoana.kernels.potential_field_prism import ( diff --git a/geoana/kernels/potential_field_prism.py b/geoana/kernels/potential_field_prism.py index 6d7682fa..40d8ce6e 100644 --- a/geoana/kernels/potential_field_prism.py +++ b/geoana/kernels/potential_field_prism.py @@ -1,6 +1,18 @@ import numpy as np from scipy.special import xlogy +__all__ = [ + "prism_f", + "prism_fz", + "prism_fzz", + "prism_fzx", + "prism_fzy", + "prism_fzzz", + "prism_fxxy", + "prism_fxxz", + "prism_fxyz", +] + def _prism_f(x, y, z): """ diff --git a/geoana/kernels/tranverse_electric_reflections.py b/geoana/kernels/tranverse_electric_reflections.py index ea9a9628..25f1edb6 100644 --- a/geoana/kernels/tranverse_electric_reflections.py +++ b/geoana/kernels/tranverse_electric_reflections.py @@ -1,12 +1,17 @@ import numpy as np from scipy.constants import mu_0 +__all__ = [ + "rTE_forward", + "rTE_gradient", +] + def _rTE_forward(frequencies, lamb, sigma, mu, thicknesses): """Compute reflection coefficients for Transverse Electric (TE) mode. The first layer is considered to be the top most layer. The last - layer is considered to have infinite thickness. All physical properties - are defined starting from the top most layer. + layer is considered to have infinite thickness. All physical properties + are defined starting from the top most layer. Parameters ---------- @@ -48,8 +53,8 @@ def _rTE_gradient(frequencies, lamb, sigma, mu, thicknesses): """Compute reflection coefficients for Transverse Electric (TE) mode. The first layer is considered to be the top most layer. The last - layer is considered to have infinite thickness. All physical properties - are defined starting from the top most layer. + layer is considered to have infinite thickness. All physical properties + are defined starting from the top most layer. Parameters ---------- diff --git a/geoana/plotting_utils.py b/geoana/plotting_utils.py index 48631a50..43077479 100644 --- a/geoana/plotting_utils.py +++ b/geoana/plotting_utils.py @@ -24,6 +24,9 @@ except ImportError: matplotlib = False +__all__ = [ + "plot2Ddata" +] @requires({"matplotlib": matplotlib}) def plot2Ddata( @@ -61,7 +64,7 @@ def plot2Ddata( data : (n) or (n, 2) np.ndarray Data values. Either scalar or 2D vector. vec : bool - Default = ``False``. Plot vector data. If ``False``, the ``data`` input argument is + Default = ``False``. Plot vector data. If ``False``, the ``data`` input argument is scalar-valued. If ``True``, the ``data`` input arument is vector. nx : int number of x grid locations diff --git a/geoana/shapes.py b/geoana/shapes.py index 8487633c..0fda9ecf 100644 --- a/geoana/shapes.py +++ b/geoana/shapes.py @@ -16,6 +16,10 @@ """ import numpy as np +__all__ = [ + "BasePrism", +] + class BasePrism: """Class for basic geometry of a prism. diff --git a/geoana/spatial.py b/geoana/spatial.py index 401d4305..01e1f030 100644 --- a/geoana/spatial.py +++ b/geoana/spatial.py @@ -8,18 +8,28 @@ functions for converting between Cartesian, cylindrical and spherical coordinates. +Coordinate Transformations +==================== .. autosummary:: :toctree: generated/ cylindrical_to_cartesian cartesian_to_cylindrical + spherical_to_cartesian + cartesian_to_spherical + rotation_matrix_from_normals + rotate_points_from_normals + +Vector Operations +================= +.. autosummary:: + :toctree: generated/ + vector_magnitude vector_distance distance vector_dot repeat_scalar - rotation_matrix_from_normals - rotate_points_from_normals """ import numpy as np @@ -27,6 +37,20 @@ from .utils import mkvc +__all__ = [ + "cylindrical_to_cartesian", + "cartesian_to_cylindrical", + "spherical_to_cartesian", + "cartesian_to_spherical", + "rotation_matrix_from_normals", + "rotate_points_from_normals", + "vector_magnitude", + "vector_distance", + "distance", + "vector_dot", + "repeat_scalar", +] + def cylindrical_to_cartesian(grid, vec=None): """ diff --git a/geoana/utils.py b/geoana/utils.py index 574531cf..3fff4345 100644 --- a/geoana/utils.py +++ b/geoana/utils.py @@ -13,10 +13,20 @@ mkvc ndgrid check_xyz_dim + append_ndim + requires """ import numpy as np +__all__ = [ + "mkvc", + "ndgrid", + "check_xyz_dim", + "append_ndim", + "requires", +] + def mkvc(x, n_dims=1): """Creates a vector with specified dimensionality. @@ -182,6 +192,18 @@ def check_xyz_dim(xyz, dim=3, dtype=float): def append_ndim(arr, ndim): + """Appends dimensions to a numpy ndarray + + Parameters + ---------- + arr : array_like + ndim : int + The number of dimensions to append to arr + + Returns + ------- + numpy.ndarray + """ arr = np.asarray(arr) for _ in range(ndim): arr = arr[..., None]