Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 0133 create new spyro version were every dependency not pip installable is optional #137

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
827 changes: 159 additions & 668 deletions LICENSE

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@

setup(
name="spyro",
version="0.2.0",
license="GPL v3",
description="acoustic wave modeling with the finite element method",
author="Keith J. Roberts, Alexandre F. G. Olender, Lucas Franceschini",
url="https://github.com/krober10nd/spyro",
version="0.9.0",
license="LGPL v3",
description="Wave modeling with the finite element method",
author="Keith J. Roberts, Alexandre F. G. Olender, Eduardo Moscatelli de Souza, Daiane I. Dolci, Thiago Dias dos Santos, Lucas Franceschini",
url="https://github.com/NDF-Poli-USP/spyro",
packages=find_packages(),
install_requires=[
"firedrake",
"numpy",
"scipy",
"matplotlib",
"exdown==0.7.0",
"segyio"],
"segyio",
"meshio"],
)
14 changes: 11 additions & 3 deletions spyro/meshing/meshing_functions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import firedrake as fire
import SeismicMesh
import meshio

try:
import SeismicMesh
except ImportError:
SeismicMesh = None

Check warning on line 7 in spyro/meshing/meshing_functions.py

View check run for this annotation

Codecov / codecov/patch

spyro/meshing/meshing_functions.py#L6-L7

Added lines #L6 - L7 were not covered by tests


def cells_per_wavelength(method, degree, dimension):
cell_per_wavelength_dictionary = {
Expand All @@ -13,9 +17,9 @@
'mlt3tet': 3.72,
}

if dimension == 2 and (method == 'KMV' or method == 'CG'):
if dimension == 2 and (method == 'MLT' or method == 'CG'):
cell_type = 'tri'
if dimension == 3 and (method == 'KMV' or method == 'CG'):
if dimension == 3 and (method == 'MLT' or method == 'CG'):
cell_type = 'tet'

key = method.lower()+str(degree)+cell_type
Expand Down Expand Up @@ -221,6 +225,8 @@
-------
None
"""
if SeismicMesh is None:
raise ImportError("SeismicMesh is not available. Please install it to use this function.")

Check warning on line 229 in spyro/meshing/meshing_functions.py

View check run for this annotation

Codecov / codecov/patch

spyro/meshing/meshing_functions.py#L229

Added line #L229 was not covered by tests
if cpw is not None:
self.cpw = cpw
if velocity_model is not None:
Expand Down Expand Up @@ -258,6 +264,8 @@
if self.mesh_type == "firedrake_mesh":
return self.create_firedrake_mesh()
elif self.mesh_type == "SeismicMesh":
if SeismicMesh is None:
raise ImportError("SeismicMesh is not available. Please install it to use this function.")

Check warning on line 268 in spyro/meshing/meshing_functions.py

View check run for this annotation

Codecov / codecov/patch

spyro/meshing/meshing_functions.py#L268

Added line #L268 was not covered by tests
return self.create_seismicmesh_mesh()
else:
raise ValueError("mesh_type is not supported")
Expand Down
9 changes: 8 additions & 1 deletion spyro/solvers/acoustic_wave.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import firedrake as fire
import warnings
import os
from SeismicMesh import write_velocity_model

from .wave import Wave

Expand All @@ -20,6 +19,12 @@
from ..utils.typing import override
from .functionals import acoustic_energy

try:
from SeismicMesh import write_velocity_model
SEISMIC_MESH_AVAILABLE = True
except ImportError:
SEISMIC_MESH_AVAILABLE = False

Check warning on line 26 in spyro/solvers/acoustic_wave.py

View check run for this annotation

Codecov / codecov/patch

spyro/solvers/acoustic_wave.py#L25-L26

Added lines #L25 - L26 were not covered by tests


class AcousticWave(Wave):
def __init__(self, dictionary, comm=None):
Expand Down Expand Up @@ -109,6 +114,8 @@
raise ValueError("No velocity model or velocity file to load.")

if self.initial_velocity_model_file.endswith(".segy"):
if not SEISMIC_MESH_AVAILABLE:
raise ImportError("SeismicMesh is required to convert segy files.")

Check warning on line 118 in spyro/solvers/acoustic_wave.py

View check run for this annotation

Codecov / codecov/patch

spyro/solvers/acoustic_wave.py#L117-L118

Added lines #L117 - L118 were not covered by tests
vp_filename, vp_filetype = os.path.splitext(
self.initial_velocity_model_file
)
Expand Down
1 change: 1 addition & 0 deletions spyro/tools/velocity_smoother.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
plt.xlabel("x-direction (m)")
plt.ylabel("z-direction (m)")
ax.axis("equal")
plt.savefig(output_filename+".png")

Check warning on line 73 in spyro/tools/velocity_smoother.py

View check run for this annotation

Codecov / codecov/patch

spyro/tools/velocity_smoother.py#L73

Added line #L73 was not covered by tests
SouzaEM marked this conversation as resolved.
Show resolved Hide resolved
plt.show()

return None
31 changes: 27 additions & 4 deletions test/test_cpw_calc.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import numpy as np
import pytest
import spyro


def test_cpw_calc():
def is_seismicmesh_installed():
try:
import SeismicMesh # noqa: F401
return True
except ImportError:
return False


def run_test_cpw_calc(FEM_method_to_evaluate, correct_cpw):
grid_point_calculator_parameters = {
# Experiment parameters
# Here we define the frequency of the Ricker wavelet source
Expand All @@ -17,7 +26,7 @@ def test_cpw_calc():
"velocity_model_file_name": None,
# FEM to evaluate such as `KMV` or `spectral`
# (GLL nodes on quads and hexas)
"FEM_method_to_evaluate": "mass_lumped_triangle",
"FEM_method_to_evaluate": FEM_method_to_evaluate,
"dimension": 2, # Domain dimension. Either 2 or 3.
# Either near or line. Near defines a receiver grid near to the source,
"receiver_setup": "near",
Expand Down Expand Up @@ -61,11 +70,25 @@ def test_cpw_calc():
# Check if cpw is within error TOL, starting search at min
min = Cpw_calc.find_minimum()
print(f"Minimum of {min}")
test3 = np.isclose(2.3, min)
test3 = np.isclose(correct_cpw, min)

print("END")
assert all([test1, test2, test3])


@pytest.mark.skipif(not is_seismicmesh_installed(), reason="SeismicMesh is not installed")
def test_cpw_calc_triangles():
method = "mass_lumped_triangle"
correct_cpw = 2.3
return run_test_cpw_calc(method, correct_cpw)


def test_cpw_calc_quads():
method = "spectral_quadrilateral"
correct_cpw = 2.5
return run_test_cpw_calc(method, correct_cpw)


if __name__ == "__main__":
test_cpw_calc()
test_cpw_calc_triangles()
test_cpw_calc_quads()
17 changes: 17 additions & 0 deletions test/test_elastic_local_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,43 @@
expected_mechanical_energy = 0.25


def has_sufficient_memory():
meminfo = {}
with open('/proc/meminfo') as f:
for line in f:
parts = line.split(':')
if len(parts) == 2:
meminfo[parts[0].strip()] = parts[1].strip()
total_memory_kb = int(meminfo.get('MemTotal', '0 kB').split()[0])
total_memory_gb = total_memory_kb / 1024 / 1024
print(f"Total system memory {total_memory_gb}")
return total_memory_gb > 16


@pytest.mark.skipif(not has_sufficient_memory(), reason="Insufficient memory")
def test_stacey_abc():
wave = build_solver("Stacey", "backward")
wave.forward_solve()
last_mechanical_energy = wave.field_logger.get("mechanical_energy")
assert last_mechanical_energy < expected_mechanical_energy


@pytest.mark.skipif(not has_sufficient_memory(), reason="Insufficient memory")
def test_clayton_engquist_abc():
wave = build_solver("CE_A1", "backward")
wave.forward_solve()
last_mechanical_energy = wave.field_logger.get("mechanical_energy")
assert last_mechanical_energy < expected_mechanical_energy


@pytest.mark.skipif(not has_sufficient_memory(), reason="Insufficient memory")
def test_with_central():
wave = build_solver("Stacey", "central")
with pytest.raises(AssertionError):
wave.forward_solve()


@pytest.mark.skipif(not has_sufficient_memory(), reason="Insufficient memory")
def test_with_backward_2nd():
wave = build_solver("Stacey", "backward_2nd")
wave.forward_solve()
Expand Down
16 changes: 16 additions & 0 deletions test/test_meshing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from spyro.meshing.meshing_functions import cells_per_wavelength
import numpy as np


def test_cpw_for_acoustic():
method = 'MLT'
degree = 3
dimension = 2
mlt3tri = cells_per_wavelength(method, degree, dimension)
dimension = 3
mlt3tet = cells_per_wavelength(method, degree, dimension)
assert np.isclose(mlt3tri, 3.70) and np.isclose(mlt3tet, 3.72)


if __name__ == "__main__":
test_cpw_for_acoustic()
10 changes: 10 additions & 0 deletions test/test_seismicmesh_integration.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import spyro
import firedrake as fire
import numpy as np
import pytest


def is_seismicmesh_installed():
try:
import SeismicMesh # noqa: F401
return True
except ImportError:
return False


def mean_edge_length(triangle):
Expand All @@ -14,6 +23,7 @@ def mean_edge_length(triangle):
return (l0+l1+l2)/3.0


@pytest.mark.skipif(not is_seismicmesh_installed(), reason="SeismicMesh is not installed")
def test_spyro_seimicmesh_2d_homogeneous_generation():
Lz = 1.0
Lx = 2.0
Expand Down
73 changes: 73 additions & 0 deletions test/test_velocity_smoother.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import firedrake as fire
import spyro
import segyio
import numpy as np
import matplotlib.pyplot as plt


def get_vp_from_2dsegy(filename):
"""
Extracts velocity profile (vp) data from a 2D SEG-Y file.
Parameters:
filename (str): The path to the SEG-Y file.
Returns:
np.ndarray: A 2D numpy array containing the velocity profile data,
with shape (nz, nx) where nz is the number of samples
and nx is the number of traces.
"""

with segyio.open(filename, ignore_geometry=True) as f:
nz, nx = len(f.samples), len(f.trace)
show_vp = np.zeros(shape=(nz, nx))
for index, trace in enumerate(f.trace):
show_vp[:, index] = trace

return show_vp


def test_write_segy_and_smooth(show=False):
vp_name = "velocity_models/test"
segy_file = vp_name + ".segy"
smoothed_file = "smoothed_test.segy"
mesh = fire.UnitSquareMesh(50, 50)
mesh.coordinates.dat.data[:, 0] *= -1

V = fire.FunctionSpace(mesh, "CG", 3)
x, y = fire.SpatialCoordinate(mesh)
r = 0.2
xc = -0.5
yc = 0.5

vp = fire.Function(V)

c = fire.conditional((x - xc) ** 2 + (y - yc) ** 2 < r**2, 3.0, 1.5)

vp.interpolate(c)

xi, yi, zi = spyro.io.write_function_to_grid(vp, V, 10.0 / 1000.0)
spyro.io.create_segy(zi, segy_file)
original_vp = get_vp_from_2dsegy(segy_file)

if show is True:
fig, ax = plt.subplots()
plt.pcolormesh(original_vp, shading="auto")
plt.title("Non smoothed model model")
plt.colorbar(label="P-wave velocity (km/s)")
plt.xlabel("x-direction (m)")
plt.ylabel("z-direction (m)")
ax.axis("equal")
plt.savefig("nonsmoothedtest.png")
SouzaEM marked this conversation as resolved.
Show resolved Hide resolved
plt.show()

spyro.tools.smooth_velocity_field_file(segy_file, smoothed_file, 5, show=show)

smoothed_vp = get_vp_from_2dsegy(smoothed_file)
check_boundary = np.isclose(original_vp[0, 0], smoothed_vp[0, 0])
check_centre = np.isclose(original_vp[48, 48], smoothed_vp[48, 48], rtol=1e-3)
check_halfway = original_vp[0, 0]*1.1 < smoothed_vp[24, 48] < original_vp[48, 48]*0.9

assert all([check_boundary, check_halfway, check_centre])


if __name__ == "__main__":
test_write_segy_and_smooth(show=True)
Loading