Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
117 changes: 117 additions & 0 deletions core/specfem/algorithms/coupling_integral1d_dnshape.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#pragma once

#include "specfem/assembly.hpp"
#include "specfem/element_coupling.hpp"
#include "specfem/execution.hpp"
#include "specfem/point.hpp"
#include <Kokkos_Core.hpp>

/**
* @brief Temporary file for integral in issue # 1550. this function should be
* merged with `coupling_integral1d.hpp:coupling_integral` once the intersection
* field typing is updated to store two fields.
*
*/

namespace specfem::algorithms {

/**
* @brief Takes a field `intersection_field` on the intersection and computes,
* for each self GLL point, the integral of `intersection_field` times the
* normal derivative of the shape function at that point. `intersection_field`
* should be call-accessible (e.g. Kokkos::View) with shape:
*
* (chunk_size, n_quad_intersection, self::components())
*
* After handling any other intersection forces, boundary conditions, etc. the
* result can be `atomic_add`ed to the acceleration field.
*
* @tparam IndexType The chunk_edge iterator type
* @tparam IntersectionFieldViewType The type of `intersection_field`
* @tparam IntersectionFactor A nonconforming chunk_edge accessor
* holding `intersection_factor`
* @tparam ShapeFunctionNormalDerivativesType a view holding the normal
* derivatives of the shape function (implementation may change)
* @tparam CallableType The callback function, which will be given the point
* index and corresponding evaluated integral
*
* @param ngllz - number of quadrature points in z
* @param ngllx - number of quadrature points in x
* @param chunk_index - the outer index (chunk_edge) that gets iterated for
* points
* @param intersection_field - the field to integrate
* @param intersection_factor - nonconforming chunk_edge accessor holding
* `intersection_factor`
* @param shape_function_normal_derivatives - (TEMPORARY, TO BE LOADED LATER BY
* THIS FUNCTION) normal derivatives of the shape functions at each intersection
* point.
* @param callback - callback function to capture integral values
* @ingroup AlgorithmsIntegration
*/
template <typename IndexType, typename IntersectionFieldViewType,
typename IntersectionFactor,
typename ShapeFunctionNormalDerivativesType, typename CallableType>
KOKKOS_FUNCTION void coupling_integral_dnshape(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Precomputing edge-related values and storing them in the assembly can clean up this code, improve readability, and facilitate integration into coupling_integral.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is it alright if we do this in a future PR? I was waiting for this one to go through before updating my work on #1342 in order to put the first coupling_integral in the same test executable.

Precomputation and storage would require changes to nonconforming_interfaces that will bloat this PR even further.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That sounds good. Can you create another issue for that refactor and link it here?

Also, see my other comment.

const int &ngllz, const int &ngllx, const IndexType &chunk_index,
const IntersectionFieldViewType &intersection_field,
const IntersectionFactor &intersection_factor,
const ShapeFunctionNormalDerivativesType &shape_function_normal_derivatives,
const CallableType &callback) {
constexpr specfem::element::dimension_tag dimension_tag =
specfem::element::dimension_tag::dim2;

constexpr auto self_medium_tag = specfem::element_coupling::attributes<
dimension_tag, IntersectionFactor::interface_tag>::self_medium();

using PointIndexType =
typename IndexType::iterator_type::index_type::index_type;
using PointFieldType =
specfem::point::acceleration<dimension_tag, self_medium_tag,
IntersectionFieldViewType::using_simd>;

constexpr int ncomp = PointFieldType::components;
constexpr int nquad_intersection = IntersectionFactor::n_quad_intersection;

const auto &team = chunk_index.get_policy_index();
const int &num_edges = chunk_index.nedges();
specfem::execution::for_each_level(
specfem::execution::TeamThreadMDRangeIterator(team, num_edges, ngllz,
ngllx),
[&](const auto &index) {
const int iedge = index(0);
const int iz = index(1);
const int ix = index(2);

PointFieldType result;
#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
#pragma unroll
#endif
for (int icomp = 0; icomp < ncomp; icomp++) {
result(icomp) = 0;
}

#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
#pragma unroll
#endif
for (int iquad = 0; iquad < nquad_intersection; iquad++) {

#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
#pragma unroll
#endif
for (int icomp = 0; icomp < ncomp; icomp++) {
result(icomp) +=
intersection_field(iedge, iquad, icomp) *
intersection_factor(iedge, iquad) *
shape_function_normal_derivatives(iedge, iz, ix, iquad);
}
}
// this index will always have ipoint = 0, but we will not use it
const auto edge_index = chunk_index.get_iterator()(iedge).get_index();

specfem::point::index<dimension_tag> self_index(edge_index.ispec, iz,
ix);
callback(self_index, result);
});
}

} // namespace specfem::algorithms
177 changes: 177 additions & 0 deletions core/specfem/algorithms/shape_function_normal_derivative.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#pragma once

#include "specfem/assembly.hpp"
#include "specfem/assembly/edge_types.hpp"
#include "specfem/assembly/mesh.hpp"
#include "specfem/chunk_edge/nonconforming_interface.hpp"
#include "specfem/element/tags.hpp"
#include "specfem/element_coupling.hpp"
#include "specfem/execution.hpp"
#include "specfem/point.hpp"
#include <Kokkos_Core.hpp>

namespace specfem::algorithms {

/**
* @brief Takes a field `intersection_field` on the intersection and computes,
* for each self GLL point, the integral of `intersection_field` times the
* normal derivative of the shape function at that point. `intersection_field`
* should be call-accessible (e.g. Kokkos::View) with shape:
*
* (chunk_size, n_quad_intersection, self::components())
*
* After handling any other intersection forces, boundary conditions, etc. the
* result can be `atomic_add`ed to the acceleration field.
*
* @tparam dimension_tag dimension of the simulation
* @param nonconforming_interfaces - assembly.nonconforming_interfaces struct
*/
template <specfem::element_coupling::interface_tag interface_tag,
specfem::element::boundary_tag boundary_tag, int nquad_element_,
int nquad_intersection_, typename SelfEdgeListView,
typename HPrimeView, typename TransformedIntersectionNormalView>
Kokkos::View<type_real ****, Kokkos::DefaultExecutionSpace>
shape_function_self_normal_derivatives(
const SelfEdgeListView &self_edges,
const specfem::assembly::nonconforming_interfaces<
specfem::element::dimension_tag::dim2> &nonconforming_interfaces,
const int &ngllz, const int &ngllx, const HPrimeView &hprime,
const TransformedIntersectionNormalView
&intersection_normal_contravariant_edgelocal) {
const auto dimension_tag = specfem::element::dimension_tag::dim2;
using ReturnViewType =
Kokkos::View<type_real ****, Kokkos::DefaultExecutionSpace>;

// TODO: get nquad_intersection from somewhere else
const int nquad_intersection = nquad_intersection_;
if (nquad_intersection != nquad_intersection_) {
throw std::runtime_error(
std::string("shape_function_self_normal_derivatives() kernel run with "
"nquad_intersection = ") +
std::to_string(nquad_intersection_) +
", but assembly got nquad_intersection == " +
std::to_string(nquad_intersection));
}
ReturnViewType normal_derivs("shape_function_self_normal_derivatives",
self_edges.n_edges, ngllz, ngllx,
nquad_intersection);

using parallel_config =
specfem::parallel_configuration::default_chunk_edge_config<
dimension_tag, Kokkos::DefaultExecutionSpace>;
specfem::execution::ChunkedEdgeIterator chunk(parallel_config(), self_edges);

using TransferFunctionSelf = specfem::chunk_edge::transfer_function_self<
dimension_tag, interface_tag, boundary_tag, parallel_config::chunk_size,
nquad_intersection_, nquad_element_>;

specfem::execution::for_each_level(
"specfem::compute::shape_function_normal_derivative",
chunk.set_scratch_size(
0, Kokkos::PerTeam(TransferFunctionSelf::shmem_size())),
KOKKOS_LAMBDA(
const typename decltype(chunk)::index_type &chunk_iterator_index) {
const auto &chunk_index = chunk_iterator_index.get_index();
const auto &team = chunk_index.get_policy_index();
const int &num_edges = chunk_index.nedges();

TransferFunctionSelf transfer_function_self(team);
specfem::assembly::load_on_device(chunk_index, nonconforming_interfaces,
transfer_function_self);

specfem::execution::for_each_level(
specfem::execution::TeamThreadMDRangeIterator(team, num_edges,
ngllz, ngllx),
[&](const auto &index) {
const int iedge = index(0);
const int iz = index(1);
const int ix = index(2);

// this index will always have ipoint = 0, but we will not use it
const auto edge_index =
chunk_index.get_iterator()(iedge).get_index();

/*
* We convert local coordinates / indices ix, iz
* into an edge-local coordinate system:
*
* ================================== ipoint_s = ix for top
* | and bottom, iz for left
* | and right
* |
* ipoint_n = ngll-1
*
* ipoint_n = 0 represents the edge.
*/

const bool ipoint_n_is_ix =
edge_index.edge_type ==
specfem::mesh_entity::dim2::type::left ||
edge_index.edge_type ==
specfem::mesh_entity::dim2::type::right;
const int ngll_n = ipoint_n_is_ix ? ngllx : ngllz;
const int ngll_s = ipoint_n_is_ix ? ngllz : ngllx;

const int ipoint_s = ipoint_n_is_ix ? iz : ix;
int ipoint_n = ipoint_n_is_ix ? ix : iz;
if (edge_index.edge_type ==
specfem::mesh_entity::dim2::type::right ||
edge_index.edge_type ==
specfem::mesh_entity::dim2::type::top) {
// flip ipoint_n coord so edge is 0
ipoint_n = ngll_n - 1 - ipoint_n;
}

const type_real dlagrange_dn = -hprime(0, ipoint_n);

for (int iquad = 0; iquad < nquad_intersection; iquad++) {

// dshape_dn, local-normal derivative (derivative of
// normal-direction L, which used to be kronecker delta
// indicating on edge. Now has dlagrange_dn != 0 for all
// ipoint_n)

// first, in the local-normal component
type_real dshape_dn =
intersection_normal_contravariant_edgelocal(
edge_index.iedge, iquad, 0) *
(dlagrange_dn *
transfer_function_self(iedge, ipoint_s, iquad));
// dshape_dn, local-tangential derivative (differentiate
// transfer_function_self instead of normal-direction L)
if (ipoint_n == 0) {
// the local-tangential is constant zero if we are not on the
// edge.

// recover L' (at point ipoint_s) on intersection quadrature
// points: use transfer function.
type_real transfer_function_derivative = 0;
for (int ipoint_s2 = 0; ipoint_s2 < ngll_s; ipoint_s2++) {
transfer_function_derivative +=
hprime(ipoint_s2, ipoint_s) *
transfer_function_self(iedge, ipoint_s2, iquad);
}

dshape_dn += intersection_normal_contravariant_edgelocal(
edge_index.iedge, iquad, 1) *
transfer_function_derivative;
}

normal_derivs(edge_index.iedge, iz, ix, iquad) = dshape_dn;
}
});
// specfem::execution::for_each_level(
// chunk_index.get_iterator(),
// [&](const typename ChunkIndexType::iterator_type::index_type
// &iterator_index) {
// const auto index = iterator_index.get_index();
// const auto index_local = iterator_index.get_local_index();

// index.ielem
// });
Comment on lines +163 to +171
Copy link
Collaborator

Choose a reason for hiding this comment

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

?

});

return normal_derivs;
}

} // namespace specfem::algorithms
57 changes: 35 additions & 22 deletions core/specfem/assembly/edge_types/dim2/edge_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,28 +98,13 @@ specfem::assembly::edge_types<specfem::element::dimension_tag::dim2>::
"specfem::assembly::interface_types::self_edges", count, ngll);
_coupled_edges_ = EdgeViewType(
"specfem::assembly::interface_types::coupled_edges", count, ngll);
_h_self_edges_ = edge_types::create_mirror_view(_self_edges_);
_h_coupled_edges_ = edge_types::create_mirror_view(_coupled_edges_);

for (int iedge = 0; iedge < count; iedge++) {
_h_self_edges_.element_index(iedge) = self_collect[iedge].ispec;
_h_self_edges_.edge_index(iedge) = self_collect[iedge].iedge;
_h_self_edges_.edge_types(iedge) = self_collect[iedge].edge_type;
_h_coupled_edges_.element_index(iedge) = coupled_collect[iedge].ispec;
_h_coupled_edges_.edge_index(iedge) = coupled_collect[iedge].iedge;
_h_coupled_edges_.edge_types(iedge) =
coupled_collect[iedge].edge_type;
for (int ipoint = 0; ipoint < ngll; ipoint++) {
const auto [iz1, ix1] =
element.map_coordinates(self_collect[iedge].edge_type, ipoint);
_h_self_edges_.iz(iedge, ipoint) = iz1;
_h_self_edges_.ix(iedge, ipoint) = ix1;
const auto [iz2, ix2] = element.map_coordinates(
coupled_collect[iedge].edge_type, ipoint);
_h_coupled_edges_.iz(iedge, ipoint) = iz2;
_h_coupled_edges_.ix(iedge, ipoint) = ix2;
}
}

_h_self_edges_ = edge_view_from_collected_edges(
"specfem::assembly::interface_types::self_edges_host_mirror",
self_collect, element);
_h_coupled_edges_ = edge_view_from_collected_edges(
"specfem::assembly::interface_types::coupled_edges_host_mirror",
coupled_collect, element);

edge_types::deep_copy(_self_edges_, _h_self_edges_);
edge_types::deep_copy(_coupled_edges_, _h_coupled_edges_);
Expand Down Expand Up @@ -171,3 +156,31 @@ specfem::assembly::edge_types<specfem::element::dimension_tag::dim2>::
throw std::runtime_error(
"Connection type, interface type or boundary type not found");
}

specfem::assembly::edge_types<
specfem::element::dimension_tag::dim2>::EdgeViewType::HostMirror
specfem::assembly::edge_view_from_collected_edges(
const std::string &label,
const std::vector<
specfem::mesh_entity::edge<specfem::element::dimension_tag::dim2> >
&self_collect,
const specfem::mesh_entity::element<specfem::element::dimension_tag::dim2>
&element) {
const int &ngll = element.ngllx;
const int &count = self_collect.size();

specfem::assembly::edge_types<specfem::element::dimension_tag::dim2>::
EdgeViewType::HostMirror self_edges(label, count, ngll);
for (int iedge = 0; iedge < count; iedge++) {
self_edges.element_index(iedge) = self_collect[iedge].ispec;
self_edges.edge_index(iedge) = self_collect[iedge].iedge;
self_edges.edge_types(iedge) = self_collect[iedge].edge_type;
for (int ipoint = 0; ipoint < ngll; ipoint++) {
const auto [iz1, ix1] =
element.map_coordinates(self_collect[iedge].edge_type, ipoint);
self_edges.iz(iedge, ipoint) = iz1;
self_edges.ix(iedge, ipoint) = ix1;
}
}
return self_edges;
}
10 changes: 10 additions & 0 deletions core/specfem/assembly/edge_types/dim2/edge_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,14 @@ template <> struct edge_types<specfem::element::dimension_tag::dim2> {
(EdgeViewType::HostMirror, h_coupled_edges)))
};

specfem::assembly::edge_types<
specfem::element::dimension_tag::dim2>::EdgeViewType::HostMirror
edge_view_from_collected_edges(
const std::string &label,
const std::vector<
specfem::mesh_entity::edge<specfem::element::dimension_tag::dim2> >
&collected_edges,
const specfem::mesh_entity::element<specfem::element::dimension_tag::dim2>
&element);

} // namespace specfem::assembly
Loading