Skip to content

Commit f658ff4

Browse files
authored
Merge pull request #1608 from PrincetonUniversity/issue-1592
Issue 1592 - Implements computation of tau_sigma for attenuation
2 parents ef7bf27 + 06590ed commit f658ff4

File tree

11 files changed

+276
-2
lines changed

11 files changed

+276
-2
lines changed

core/specfem/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Add existing subdirectories
22
add_subdirectory(algorithms)
33
add_subdirectory(assembly)
4+
add_subdirectory(attenuation)
45
add_subdirectory(jacobian)
56
add_subdirectory(kokkos_kernels)
67
add_subdirectory(logger)

core/specfem/attenuation.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
#include "attenuation/compute_tau_sigma.hpp"
3+
#include "attenuation/compute_tau_sigma.tpp"
4+
5+
6+
/**
7+
* @brief Basic functions for the computation of attenuation related parameters.
8+
*
9+
*/
10+
namespace specfem::attenuation {} // namespace specfem::attenuation
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Create header-only interface library for specfem::attenuation
2+
add_library(specfem_attenuation INTERFACE)
3+
4+
# Create alias for consistent naming
5+
add_library(specfem::attenuation ALIAS specfem_attenuation)
6+
7+
# Add include directories so the library can find its headers
8+
target_include_directories(specfem_attenuation
9+
INTERFACE
10+
${CMAKE_SOURCE_DIR}/core
11+
)
12+
13+
target_link_libraries(specfem_attenuation INTERFACE
14+
Kokkos::kokkos
15+
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include "constants.hpp"
4+
#include "specfem_setup.hpp"
5+
#include <Kokkos_Core.hpp>
6+
#include <cmath>
7+
8+
namespace specfem {
9+
namespace attenuation {
10+
11+
/**
12+
* @brief Compute stress relaxation times \f$\tau_{\sigma}\f$ equally spaced in
13+
* log10 frequency.
14+
*
15+
* The routine constructs a set of relaxation times corresponding to
16+
* frequencies that are equally spaced in base-10 logarithmic scale. Let
17+
* \f$f_1 = 1/T_{\text{max}}\f$ and \f$f_2 = 1/T_{\text{min}}\f$ be the minimum and
18+
* maximum frequencies, where \f$T_{\text{min}}\f$ and \f$T_{\text{max}}\f$ are the input minimum and maximum periods.
19+
* and
20+
* \f[\Delta = \frac{\log_{10} f_2 - \log_{10} f_1}{N\_SLS - 1}.\f]
21+
* For index \f$i\in[0, N\_SLS-1]\f$ the relaxation time is
22+
* \f[\tau_{\sigma,i} = \frac{1}{2\pi\,10^{\log_{10} f_1 + i\Delta}}.\f]
23+
*
24+
* @tparam N_SLS Number of standard linear solids (requires \f$N\_SLS>1\f$)
25+
* @param min_period Minimum period (s)
26+
* @param max_period Maximum period (s)
27+
* @return Kokkos::View<type_real[N_SLS], Kokkos::LayoutRight, Kokkos::HostSpace>
28+
*
29+
* @code
30+
* // Example: build 4 SLS between 0.1s and 10s
31+
* auto tau = specfem::attenuation::compute_tau_sigma<4>(0.1_rt, 10.0_rt);
32+
* for (int i = 0; i < 4; ++i) std::cout << tau(i) << "\n";
33+
* @endcode
34+
*/
35+
template <int N_SLS>
36+
Kokkos::View<type_real[N_SLS], Kokkos::LayoutRight, Kokkos::HostSpace>
37+
compute_tau_sigma(const type_real min_period, const type_real max_period);
38+
39+
} // namespace attenuation
40+
} // namespace specfem
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
#include "compute_tau_sigma.hpp"
3+
#include "constants.hpp"
4+
#include "specfem_setup.hpp"
5+
#include <Kokkos_Core.hpp>
6+
#include <cmath>
7+
8+
namespace specfem {
9+
namespace attenuation {
10+
11+
template <int N_SLS>
12+
Kokkos::View<type_real[N_SLS], Kokkos::LayoutRight, Kokkos::HostSpace>
13+
compute_tau_sigma(const type_real min_period, const type_real max_period) {
14+
static_assert(N_SLS > 1, "N_SLS must be greater than 1 to avoid division by zero");
15+
16+
Kokkos::View<type_real[N_SLS], Kokkos::LayoutRight, Kokkos::HostSpace> tau_s(
17+
"tau_sigma");
18+
19+
// min/max frequencies
20+
const type_real f1 = 1.0 / max_period;
21+
const type_real f2 = 1.0 / min_period;
22+
23+
// logarithms
24+
const type_real exp1 = std::log10(f1);
25+
const type_real exp2 = std::log10(f2);
26+
27+
// equally spaced in log10 frequency
28+
const type_real dexpval =
29+
(exp2 - exp1) / (static_cast<type_real>(N_SLS) - 1);
30+
31+
for (int i = 0; i < N_SLS; ++i) {
32+
tau_s(i) = 1.0 / (pi * 2.0 * std::pow(10.0, exp1 + i * dexpval));
33+
}
34+
35+
return tau_s;
36+
}
37+
38+
} // namespace attenuation
39+
} // namespace specfem
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
``specfem::attenuation``
2+
========================
3+
4+
.. doxygennamespace:: specfem::attenuation
5+
:desc-only:
6+
7+
.. toctree::
8+
:maxdepth: 1
9+
10+
tau_sigma.rst
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
``specfem::attenuation::compute_tau_sigma``
2+
===========================================
3+
4+
.. doxygenfunction:: specfem::attenuation::compute_tau_sigma

docs/sections/api/specfem/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
algorithms/index
1111
assembly/index
12+
attenuation/index
1213
boundary_conditions/index
1314
chunk_edge/index
1415
chunk_element/index

tests/unit-tests/CMakeLists.txt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,19 @@ target_link_libraries(
124124
Kokkos::kokkos
125125
)
126126

127+
add_executable(
128+
attenuation_tests
129+
attenuation/runner.cpp
130+
attenuation/compute_tau_sigma_tests.cpp
131+
)
132+
133+
target_link_libraries(
134+
attenuation_tests
135+
gtest_main
136+
specfem::attenuation
137+
Kokkos::kokkos
138+
)
139+
127140
add_executable(
128141
gll_tests
129142
gll/gll_tests.cpp
@@ -895,15 +908,14 @@ target_link_libraries(
895908
# )
896909

897910

898-
899-
900911
# Link to gtest only if MPI is disabled
901912
if(NOT MPI_PARALLEL)
902913
include(GoogleTest)
903914
set(TEST_TARGETS
904915
abort_tests
905916
assembly_receivers_tests
906917
assembly_tests
918+
attenuation_tests
907919
# compute_acoustic_tests
908920
# compute_coupled_interfaces_tests
909921
# compute_elastic_tests
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#include "specfem/attenuation.hpp"
2+
#include <cmath>
3+
#include <gtest/gtest.h>
4+
5+
using specfem::attenuation::compute_tau_sigma;
6+
7+
// Test that the function returns the correct number of elements
8+
TEST(Attenuation_ComputeTauSigma, ReturnsCorrectSize) {
9+
constexpr type_real min_period = 0.01;
10+
constexpr type_real max_period = 10.0;
11+
12+
auto tau_s_3 = compute_tau_sigma<3>(min_period, max_period);
13+
EXPECT_EQ(tau_s_3.extent(0), 3);
14+
15+
auto tau_s_5 = compute_tau_sigma<5>(min_period, max_period);
16+
EXPECT_EQ(tau_s_5.extent(0), 5);
17+
}
18+
19+
// Test that tau_sigma values are positive
20+
TEST(Attenuation_ComputeTauSigma, ValuesArePositive) {
21+
constexpr type_real min_period = 0.01;
22+
constexpr type_real max_period = 10.0;
23+
24+
auto tau_s = compute_tau_sigma<3>(min_period, max_period);
25+
26+
for (int i = 0; i < 3; ++i) {
27+
EXPECT_GT(tau_s(i), 0.0) << "tau_s(" << i << ") should be positive";
28+
}
29+
}
30+
31+
// Test that tau_sigma values are monotonically decreasing
32+
// (higher frequency = smaller tau)
33+
TEST(Attenuation_ComputeTauSigma, ValuesAreMonotonicallyDecreasing) {
34+
constexpr type_real min_period = 0.01;
35+
constexpr type_real max_period = 10.0;
36+
37+
auto tau_s = compute_tau_sigma<5>(min_period, max_period);
38+
39+
for (int i = 1; i < 5; ++i) {
40+
EXPECT_LT(tau_s(i), tau_s(i - 1))
41+
<< "tau_s(" << i << ") should be less than tau_s(" << i - 1 << ")";
42+
}
43+
}
44+
45+
// Test that tau_sigma values are equally spaced in log10 frequency
46+
TEST(Attenuation_ComputeTauSigma, EquallySpacedInLog10Frequency) {
47+
constexpr type_real min_period = 0.01;
48+
constexpr type_real max_period = 10.0;
49+
constexpr int N_SLS = 5;
50+
51+
auto tau_s = compute_tau_sigma<N_SLS>(min_period, max_period);
52+
53+
// Convert tau_s to frequencies: f = 1 / (2 * pi * tau_s)
54+
// Then check that log10(f) values are equally spaced
55+
std::array<type_real, N_SLS> log_freq;
56+
for (int i = 0; i < N_SLS; ++i) {
57+
type_real freq = 1.0 / (2.0 * pi * tau_s(i));
58+
log_freq[i] = std::log10(freq);
59+
}
60+
61+
// Check that differences between consecutive log10(freq) are equal
62+
type_real expected_spacing = log_freq[1] - log_freq[0];
63+
for (int i = 2; i < N_SLS; ++i) {
64+
type_real actual_spacing = log_freq[i] - log_freq[i - 1];
65+
EXPECT_NEAR(actual_spacing, expected_spacing, 1e-10)
66+
<< "Log10 frequency spacing should be constant";
67+
}
68+
}
69+
70+
// Test boundary values match expected frequencies
71+
TEST(Attenuation_ComputeTauSigma, BoundaryFrequenciesMatch) {
72+
constexpr type_real min_period = 0.01;
73+
constexpr type_real max_period = 10.0;
74+
constexpr int N_SLS = 3;
75+
76+
auto tau_s = compute_tau_sigma<N_SLS>(min_period, max_period);
77+
78+
// Expected frequencies at boundaries
79+
type_real f_min = 1.0 / max_period; // 0.1 Hz
80+
type_real f_max = 1.0 / min_period; // 100 Hz
81+
82+
// Convert tau_s to frequency: f = 1 / (2 * pi * tau_s)
83+
type_real freq_first = 1.0 / (2.0 * pi * tau_s(0));
84+
type_real freq_last = 1.0 / (2.0 * pi * tau_s(N_SLS - 1));
85+
86+
// First tau_s corresponds to f_min, last to f_max
87+
EXPECT_NEAR(freq_first, f_min, f_min * 1e-10)
88+
<< "First frequency should match f_min";
89+
EXPECT_NEAR(freq_last, f_max, f_max * 1e-10)
90+
<< "Last frequency should match f_max";
91+
}
92+
93+
// Test with different period ranges
94+
TEST(Attenuation_ComputeTauSigma, DifferentPeriodRanges) {
95+
// Narrow range
96+
{
97+
auto tau_s = compute_tau_sigma<3>(1.0, 10.0);
98+
EXPECT_GT(tau_s(0), 0.0);
99+
EXPECT_GT(tau_s(1), 0.0);
100+
EXPECT_GT(tau_s(2), 0.0);
101+
}
102+
103+
// Wide range
104+
{
105+
auto tau_s = compute_tau_sigma<3>(0.001, 100.0);
106+
EXPECT_GT(tau_s(0), 0.0);
107+
EXPECT_GT(tau_s(1), 0.0);
108+
EXPECT_GT(tau_s(2), 0.0);
109+
}
110+
}
111+
112+
// Test N_SLS = 2 edge case (minimum valid value)
113+
// Note: N_SLS=1 causes division by zero in dexpval calculation
114+
TEST(Attenuation_ComputeTauSigma, TwoSLS) {
115+
constexpr type_real min_period = 0.01;
116+
constexpr type_real max_period = 10.0;
117+
118+
auto tau_s = compute_tau_sigma<2>(min_period, max_period);
119+
120+
EXPECT_EQ(tau_s.extent(0), 2);
121+
EXPECT_GT(tau_s(0), 0.0);
122+
EXPECT_GT(tau_s(1), 0.0);
123+
124+
// First should correspond to f_min, second to f_max
125+
type_real f_min = 1.0 / max_period;
126+
type_real f_max = 1.0 / min_period;
127+
type_real freq_first = 1.0 / (2.0 * pi * tau_s(0));
128+
type_real freq_last = 1.0 / (2.0 * pi * tau_s(1));
129+
130+
EXPECT_NEAR(freq_first, f_min, f_min * 1e-10);
131+
EXPECT_NEAR(freq_last, f_max, f_max * 1e-10);
132+
}

0 commit comments

Comments
 (0)