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

velocity to strain rate with gauge length support #399

Merged
merged 13 commits into from
Jun 18, 2024
1 change: 1 addition & 0 deletions dascore/core/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ def iresample(self, *args, **kwargs):
integrate = transform.integrate
spectrogram = transform.spectrogram
velocity_to_strain_rate = transform.velocity_to_strain_rate
velocity_to_strain_rate_fd = transform.velocity_to_strain_rate_fd
dispersion_phase_shift = transform.dispersion_phase_shift

# --- Method Namespaces
Expand Down
2 changes: 1 addition & 1 deletion dascore/transform/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
from .fourier import dft, idft
from .integrate import integrate
from .spectro import spectrogram
from .strain import velocity_to_strain_rate
from .strain import velocity_to_strain_rate, velocity_to_strain_rate_fd
from .dispersion import dispersion_phase_shift
73 changes: 67 additions & 6 deletions dascore/transform/strain.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"""Transformations to strain rates."""
from __future__ import annotations

import numpy as np

import dascore as dc
from dascore.constants import PatchType
from dascore.transform.differentiate import differentiate
from dascore.utils.patch import patch_function
Expand All @@ -16,27 +19,85 @@ def velocity_to_strain_rate(
order: int = 2,
) -> PatchType:
"""
Convert velocity das data to strain rate.
Convert velocity DAS data to strain rate.

This is primarily used with Terra15 data and simply uses
[patch.differentiate](`dascore.transform.differentiate.differentiate`)
under the hood.
under the hood which allows higher order differenciating.
It doesn't change the shape of the array.

Parameters
----------
patch
A patch object containing das data. Note: attrs['data_type'] should be
A patch object containing DAS data. Note: attrs['data_type'] should be
velocity.
gauge_multiple
The multiples of spatial sampling to make the simulated gauge length.
Must be equal to 1 for now.
order
The order for the finite difference 1st derivative stencil (accuracy).

See Also
--------
See also [velocity_to_strain_rate_fd]\
(`dascore.transform.strain.velocity_to_strain_rate_fd`)
"""
assert gauge_multiple == 1, "only supporting 1 for now."
coord = patch.get_coord("distance", require_evenly_sampled=True)
step = coord.step
distance_step = coord.step
patch = differentiate.func(patch, dim="distance", order=order)
data = patch.data / distance_step
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@d-chambers can you please double check if we need to divide by gauge length here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't actually need this; numpy.grad takes a dx parameter to properly normalize in that function call. This makes me feel better, I was worried the outputs were scaled wrong 😓.

new_attrs = patch.attrs.update(
data_type="strain_rate", gauge_length=distance_step * gauge_multiple
)
return patch.update(data=data, attrs=new_attrs)


@patch_function(
required_dims=("distance",),
required_attrs={"data_type": "velocity"},
)
def velocity_to_strain_rate_fd(
patch: PatchType,
gauge_multiple: int = 1,
) -> PatchType:
"""
Convert velocity DAS data to strain rate.

This is primarily used with Terra15 data and forward differences the data.
It does change the shape of the array.

Parameters
----------
patch
A patch object containing DAS data. Note: attrs['data_type'] should be
velocity.
gauge_multiple : int, optional
The multiples of spatial sampling to make the simulated gauge length.

See Also
--------
[velocity_to_strain_rate](`dascore.transform.strain.velocity_to_strain_rate`)
"""
assert isinstance(gauge_multiple, int), "gauge_multiple must be an integer."
coord = patch.get_coord("distance", require_evenly_sampled=True)
distance_step = coord.step
gauge_length = gauge_multiple * distance_step

data_1 = patch.select(distance=(gauge_multiple, None), samples=True).data
data_2 = patch.select(distance=(None, -gauge_multiple), samples=True).data
strain_rate = (data_1 - data_2) / gauge_length

dist_1 = int(np.floor(gauge_multiple / 2))
dist_2 = -int(np.ceil(gauge_multiple / 2))
new_coords, _ = patch.coords.select(distance=(dist_1, dist_2), samples=True)

dist_unit = dc.get_quantity(patch.get_coord("distance").units)
new_data_units = dc.get_quantity(patch.attrs["data_units"]) / dist_unit
new_attrs = patch.attrs.update(
data_type="strain_rate", gauge_length=step * gauge_multiple
data_type="strain_rate",
gauge_length=distance_step * gauge_multiple,
data_units=new_data_units,
)
return patch.update(attrs=new_attrs)

return dc.Patch(data=strain_rate, coords=new_coords, attrs=new_attrs)
84 changes: 84 additions & 0 deletions tests/test_transform/test_velocity_to_strain_rate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""Tests for converting velocity to strain-rate."""
from __future__ import annotations

import numpy as np
import pytest

from dascore.exceptions import PatchAttributeError
from dascore.units import get_quantity


class TestStrainRateConversion:
"""Tests for converting velocity to strain-rate."""

@pytest.fixture(scope="class")
def patch_strain_rate_default(self, terra15_das_patch):
"""Return the default terra15 converted to strain rate."""
return terra15_das_patch.velocity_to_strain_rate()

def test_attrs(self, patch_strain_rate_default):
"""Ensure the attributes were updated with strain_rate."""
attrs = patch_strain_rate_default.attrs
assert attrs["data_type"] == "strain_rate"

def test_data_different(self, patch_strain_rate_default, terra15_das_patch):
"""The data should have changed."""
data1 = patch_strain_rate_default.data
data2 = terra15_das_patch.data
assert not np.all(np.equal(data1, data2))

def test_raises_on_strain_rate(self, patch_strain_rate_default):
"""It does not make sense to apply this twice."""
with pytest.raises(PatchAttributeError, match="velocity"):
_ = patch_strain_rate_default.velocity_to_strain_rate()

def test_update_units(self, patch_strain_rate_default, terra15_das_patch):
"""Ensure units are updated. See issue #144."""
new_units = get_quantity(patch_strain_rate_default.attrs.data_units)
old_units = get_quantity(terra15_das_patch.attrs.data_units)
assert new_units != old_units
assert new_units == old_units / get_quantity("m")


class TestFdStrainRateConversion:
"""Tests for converting velocity to strain-rate in the forward diff function."""

@pytest.fixture(scope="class")
def patch_strain_rate_default(self, terra15_das_patch):
"""Return the default terra15 converted to strain rate."""
return terra15_das_patch.velocity_to_strain_rate_fd()

def test_attrs(self, patch_strain_rate_default):
"""Ensure the attributes were updated with strain_rate."""
attrs = patch_strain_rate_default.attrs
assert attrs["data_type"] == "strain_rate"

def test_raises_on_strain_rate(self, patch_strain_rate_default):
"""It does not make sense to apply this twice."""
with pytest.raises(PatchAttributeError, match="velocity"):
_ = patch_strain_rate_default.velocity_to_strain_rate_fd()

def test_update_units(self, patch_strain_rate_default, terra15_das_patch):
"""Ensure units are updated. See issue #144."""
new_units = get_quantity(patch_strain_rate_default.attrs.data_units)
old_units = get_quantity(terra15_das_patch.attrs.data_units)
assert new_units != old_units
assert new_units == old_units / get_quantity("m")

def test_even_gauge_multiple(self, terra15_das_patch):
"""Compare output shape with coord shape for even gauge multiple."""
strain_rate_patch = terra15_das_patch.velocity_to_strain_rate_fd(4)
assert strain_rate_patch.data.shape == strain_rate_patch.coords.shape

def test_odd_gauge_multiple(self, terra15_das_patch):
"""Compare output shape with coord shape for odd gauge multiple."""
strain_rate_patch = terra15_das_patch.velocity_to_strain_rate_fd(5)
assert strain_rate_patch.data.shape == strain_rate_patch.coords.shape

@pytest.mark.parametrize("sample", (1, 2, 3, 4, 5))
def test_shape_different(self, terra15_das_patch, sample):
"""Ensure shape of the output is correctly changed."""
strain_rate_patch = terra15_das_patch.velocity_to_strain_rate_fd(sample)
shape_1 = len(strain_rate_patch.coords.get_array("distance"))
shape_2 = len(terra15_das_patch.coords.get_array("distance"))
assert shape_1 == shape_2 - sample
40 changes: 0 additions & 40 deletions tests/test_transform/test_velocity_to_strainrate.py

This file was deleted.

Loading