Loading...
Searching...
No Matches
advection_diffusion.hpp
Go to the documentation of this file.
1
2#pragma once
8
10
11template < typename ScalarT >
13{
14 public:
17 using ScalarType = ScalarT;
18
19 private:
22
23 static constexpr int num_velocity_components = 3;
24 static constexpr int num_neighbors = GH::num_neighbors;
25
27
32
34
35 ScalarT diffusivity_;
36 ScalarT dt_;
37 bool subtract_divergence_;
38
40
44
45 public:
50 const grid::Grid4DDataVec< ScalarT, 3 >& cell_centers,
53 const ScalarT diffusivity,
54 const ScalarT dt,
55 const bool subtract_divergence = true )
56 : domain_( domain )
57 , grid_( grid )
58 , radii_( radii )
59 , cell_centers_( cell_centers )
60 , boundary_mask_( boundary_mask )
61 , velocity_( velocity )
62 , diffusivity_( diffusivity )
63 , dt_( dt )
64 , subtract_divergence_( subtract_divergence )
65 , ghost_bufs_( domain )
66 {}
67
68 ScalarT& dt() { return dt_; }
69 const ScalarT& dt() const { return dt_; }
70
71 /// @brief Compute the implicit-step right-hand side \f$b = M\,T^n + \Delta t\,M\,f\f$.
72 ///
73 /// For the semi-implicit FCT loop the linear system to solve is
74 /// \f$(M + \Delta t\,A)\,T^L = M\,T^n\f$ (no source), or
75 /// \f$(M + \Delta t\,A)\,T^L = M\,T^n + \Delta t\,M\,f\f$ with a source term.
76 /// This method forms the right-hand side using the same Felippa 3×2 quadrature as
77 /// `Mass::apply_impl`, so no separate `Mass` operator is needed when a source is present.
78 ///
79 /// @param T_old Current scalar field \f$T^n\f$.
80 /// @param rhs [out] Receives \f$M\,T^n\f$ (or \f$M\,T^n + \Delta t\,M\,f\f$).
81 /// @param source Optional volumetric source term \f$f\f$ [T/time] (default: none).
83 const SrcVectorType& T_old,
84 DstVectorType& rhs,
85 const grid::Grid4DDataScalar< ScalarT >& source = {} )
86 {
87 const bool has_source = ( source.data() != nullptr );
88
89 const auto T_old_grid = T_old.grid_data();
90 const auto rhs_grid = rhs.grid_data();
91 const auto grid = grid_;
92 const auto radii = radii_;
93 const auto dt = dt_;
94
95 Kokkos::parallel_for(
96 "compute_rhs",
97 Kokkos::MDRangePolicy(
98 { 0, 1, 1, 1 },
99 { T_old_grid.extent( 0 ),
100 T_old_grid.extent( 1 ) - 1,
101 T_old_grid.extent( 2 ) - 1,
102 T_old_grid.extent( 3 ) - 1 } ),
103 KOKKOS_LAMBDA( const int id, const int x, const int y, const int r ) {
106 fe::wedge::wedge_surface_physical_coords( wedge_phy_surf, grid, id, x - 1, y - 1 );
107
108 const ScalarT r_1 = radii( id, r - 1 );
109 const ScalarT r_2 = radii( id, r );
110
111 constexpr auto num_quad_points = fe::wedge::quadrature::quad_felippa_3x2_num_quad_points;
112 dense::Vec< ScalarT, 3 > quad_points[num_quad_points];
113 ScalarT quad_weights[num_quad_points];
116
117 ScalarT M_ii = ScalarT( 0 );
118 for ( int wedge = 0; wedge < fe::wedge::num_wedges_per_hex_cell; ++wedge )
119 for ( int q = 0; q < num_quad_points; ++q )
120 M_ii += Kokkos::abs( fe::wedge::jac( wedge_phy_surf[wedge], r_1, r_2, quad_points[q] ).det() ) *
121 quad_weights[q];
122
123 ScalarT val = M_ii * T_old_grid( id, x, y, r );
124 if ( has_source )
125 val += dt * M_ii * source( id, x, y, r );
126 rhs_grid( id, x, y, r ) = val;
127 } );
128
129 Kokkos::fence();
130 }
131
132 void apply_impl( const SrcVectorType& src, DstVectorType& dst )
133 {
134 communication::shell::update_fv_ghost_layers( domain_, src.grid_data(), ghost_bufs_ );
135
136 src_ = src.grid_data();
137 dst_ = dst.grid_data();
138 vel_grid_ = velocity_.grid_data();
139
140 Kokkos::parallel_for(
141 "matvec",
142 Kokkos::MDRangePolicy(
143 { 0, 1, 1, 1 },
144 { src.grid_data().extent( 0 ),
145 src.grid_data().extent( 1 ) - 1,
146 src.grid_data().extent( 2 ) - 1,
147 src.grid_data().extent( 3 ) - 1 } ),
148 *this );
149
150 Kokkos::fence();
151 }
152
153 public:
154 KOKKOS_INLINE_FUNCTION void
155 operator()( const int local_subdomain_id, const int x_cell, const int y_cell, const int r_cell ) const
156 {
157 constexpr int cell_offset_x[num_neighbors] = { -1, 1, 0, 0, 0, 0 };
158 constexpr int cell_offset_y[num_neighbors] = { 0, 0, -1, 1, 0, 0 };
159 constexpr int cell_offset_r[num_neighbors] = { 0, 0, 0, 0, -1, 1 };
160
161 ScalarT beta[num_neighbors];
162 ScalarT M_ii;
163 Vec3 S_f[num_neighbors];
165 grid_, radii_, cell_centers_, vel_grid_, local_subdomain_id, x_cell, y_cell, r_cell, beta, M_ii, S_f );
166
167 // Assemble (M + dt*A_upwind) stencil.
168 ScalarT AD_ii = ScalarT( 0 );
169 ScalarT AD_ij[num_neighbors] = {};
170 ScalarT beta_sum = ScalarT( 0 ); // Σ_j β_ij, for optional divergence correction
171
172 for ( int n = 0; n < num_neighbors; ++n )
173 {
174 // Advection (first-order upwind).
175 if ( beta[n] >= ScalarT( 0 ) )
176 AD_ii += beta[n];
177 else
178 AD_ij[n] += beta[n]; // inflow: negative beta → neighbour contribution
179
180 beta_sum += beta[n];
181
182 // Diffusion (two-point flux, cell-to-cell vector from precomputed centers).
183 if ( diffusivity_ != ScalarT( 0 ) )
184 {
185 const int nx = x_cell + GH::cell_offset_x( n );
186 const int ny = y_cell + GH::cell_offset_y( n );
187 const int nr = r_cell + GH::cell_offset_r( n );
188 const Vec3 neighbor_center{
189 cell_centers_( local_subdomain_id, nx, ny, nr, 0 ),
190 cell_centers_( local_subdomain_id, nx, ny, nr, 1 ),
191 cell_centers_( local_subdomain_id, nx, ny, nr, 2 ) };
192 const Vec3 cell_center{
193 cell_centers_( local_subdomain_id, x_cell, y_cell, r_cell, 0 ),
194 cell_centers_( local_subdomain_id, x_cell, y_cell, r_cell, 1 ),
195 cell_centers_( local_subdomain_id, x_cell, y_cell, r_cell, 2 ) };
196 const Vec3 dx = neighbor_center - cell_center;
197 const ScalarType denom = dx.dot( S_f[n] );
198 const ScalarType offdiag_diffusion = -diffusivity_ * ( S_f[n].dot( S_f[n] ) / denom );
199 AD_ii -= offdiag_diffusion; // diagonal gets the negative of offdiag
200 AD_ij[n] += offdiag_diffusion;
201 }
202 }
203
204 // Divergence correction: subtract Σ_j β_ij from the diagonal so the operator
205 // represents u·∇T instead of ∇·(uT).
206 if ( subtract_divergence_ )
207 AD_ii -= beta_sum;
208
209 // Apply: (M_ii + dt*AD_ii)*T_i + dt*sum_n AD_ij[n]*T_j
210 ScalarType result = ( M_ii + dt_ * AD_ii ) * src_( local_subdomain_id, x_cell, y_cell, r_cell );
211 for ( int n = 0; n < num_neighbors; ++n )
212 {
213 result += dt_ * AD_ij[n] * src_(
214 local_subdomain_id,
215 x_cell + GH::cell_offset_x( n ),
216 y_cell + GH::cell_offset_y( n ),
217 r_cell + GH::cell_offset_r( n ) );
218 }
219 dst_( local_subdomain_id, x_cell, y_cell, r_cell ) = result;
220 }
221};
222
223} // namespace terra::fv::hex::operators
Pre-allocated send/recv buffers and sorted face-pair lists for FV ghost layer comm.
Definition fv_communication.hpp:184
Definition advection_diffusion.hpp:13
ScalarT ScalarType
Definition advection_diffusion.hpp:17
const ScalarT & dt() const
Definition advection_diffusion.hpp:69
UnsteadyAdvectionDiffusion(const grid::shell::DistributedDomain &domain, const grid::Grid3DDataVec< ScalarT, 3 > &grid, const grid::Grid2DDataScalar< ScalarT > &radii, const grid::Grid4DDataVec< ScalarT, 3 > &cell_centers, const grid::Grid4DDataScalar< grid::shell::ShellBoundaryFlag > &boundary_mask, const linalg::VectorQ1Vec< ScalarT, num_velocity_components > &velocity, const ScalarT diffusivity, const ScalarT dt, const bool subtract_divergence=true)
Definition advection_diffusion.hpp:46
void compute_rhs(const SrcVectorType &T_old, DstVectorType &rhs, const grid::Grid4DDataScalar< ScalarT > &source={})
Compute the implicit-step right-hand side .
Definition advection_diffusion.hpp:82
void operator()(const int local_subdomain_id, const int x_cell, const int y_cell, const int r_cell) const
Definition advection_diffusion.hpp:155
ScalarT & dt()
Definition advection_diffusion.hpp:68
void apply_impl(const SrcVectorType &src, DstVectorType &dst)
Definition advection_diffusion.hpp:132
Parallel data structure organizing the thick spherical shell metadata for distributed (MPI parallel) ...
Definition spherical_shell.hpp:2518
Finite volume vector on distributed shell grid with one DoF per hex (merging 2 wedges) and ghost-laye...
Definition vector_fv.hpp:23
const grid::Grid4DDataScalar< ScalarType > & grid_data() const
Get const reference to grid data.
Definition vector_fv.hpp:145
const grid::Grid4DDataVec< ScalarType, VecDim > & grid_data() const
Get const reference to grid data.
Definition vector_q1.hpp:288
Ghost layer communication for scalar finite volume (FV) fields on the shell grid.
Shared FV hex cell geometry: face-normal surface integrals, velocity fluxes, cell volume.
void update_fv_ghost_layers(const grid::shell::DistributedDomain &domain, const grid::Grid4DDataScalar< ScalarType > &data, FVGhostLayerBuffers< ScalarType > &bufs)
Fills all ghost layers of a scalar FV field from neighbouring subdomains.
Definition fv_communication.hpp:316
constexpr void quad_felippa_3x2_quad_weights(T(&quad_weights)[quad_felippa_3x2_num_quad_points])
Definition wedge/quadrature/quadrature.hpp:93
constexpr int quad_felippa_3x2_num_quad_points
Definition wedge/quadrature/quadrature.hpp:66
constexpr void quad_felippa_3x2_quad_points(dense::Vec< T, 3 >(&quad_points)[quad_felippa_3x2_num_quad_points])
Definition wedge/quadrature/quadrature.hpp:70
constexpr int num_nodes_per_wedge_surface
Definition kernel_helpers.hpp:6
void wedge_surface_physical_coords(dense::Vec< T, 3 >(&wedge_surf_phy_coords)[num_wedges_per_hex_cell][num_nodes_per_wedge_surface], const grid::Grid3DDataVec< T, 3 > &lateral_grid, const int local_subdomain_id, const int x_cell, const int y_cell)
Extracts the (unit sphere) surface vertex coords of the two wedges of a hex cell.
Definition kernel_helpers.hpp:26
constexpr int num_wedges_per_hex_cell
Definition kernel_helpers.hpp:5
constexpr dense::Mat< T, 3, 3 > jac(const dense::Vec< T, 3 > &p1_phy, const dense::Vec< T, 3 > &p2_phy, const dense::Vec< T, 3 > &p3_phy, const T r_1, const T r_2, const T xi, const T eta, const T zeta)
Definition integrands.hpp:657
Definition advection_diffusion.hpp:9
Kokkos::View< ScalarType ***[VecDim], Layout > Grid3DDataVec
Definition grid_types.hpp:42
Kokkos::View< ScalarType ****, Layout > Grid4DDataScalar
Definition grid_types.hpp:27
Kokkos::View< ScalarType **, Layout > Grid2DDataScalar
Definition grid_types.hpp:21
T dot(const Vec &other) const
Definition vec.hpp:39
Stateless geometry helper for a single FV hex cell.
Definition geometry_helper.hpp:32
static constexpr int cell_offset_y(int n)
Definition geometry_helper.hpp:42
static constexpr int cell_offset_r(int n)
Definition geometry_helper.hpp:47
static constexpr int cell_offset_x(int n)
Definition geometry_helper.hpp:37
static constexpr int num_neighbors
Definition geometry_helper.hpp:35
static void compute_geometry(const grid::Grid3DDataVec< ScalarT, 3 > &grid, const grid::Grid2DDataScalar< ScalarT > &radii, const grid::Grid4DDataVec< ScalarT, 3 > &cell_centers, const grid::Grid4DDataVec< ScalarT, 3 > &vel_grid, const int local_subdomain_id, const int x_cell, const int y_cell, const int r_cell, ScalarT(&beta)[num_neighbors], ScalarT &M_ii, Vec3(&S_f)[num_neighbors])
Compute beta[6], M_ii, and S_f[6] for the cell at (x_cell, y_cell, r_cell).
Definition geometry_helper.hpp:191