GEOS
WaveSolverUtils.hpp
Go to the documentation of this file.
1 /*
2  * ------------------------------------------------------------------------------------------------------------
3  * SPDX-License-Identifier: LGPL-2.1-only
4  *
5  * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
6  * Copyright (c) 2018-2024 TotalEnergies
7  * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
8  * Copyright (c) 2023-2024 Chevron
9  * Copyright (c) 2019- GEOS/GEOSX Contributors
10  * All rights reserved
11  *
12  * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
13  * ------------------------------------------------------------------------------------------------------------
14  */
15 
16 
21 #ifndef GEOS_PHYSICSSOLVERS_WAVEPROPAGATION_WAVESOLVERUTILS_HPP_
22 #define GEOS_PHYSICSSOLVERS_WAVEPROPAGATION_WAVESOLVERUTILS_HPP_
23 
26 #include "LvArray/src/tensorOps.hpp"
27 
28 namespace geos
29 {
30 
32 {
33  static constexpr real64 epsilonLoc = 1e-8;
34 
35  using EXEC_POLICY = parallelDevicePolicy< >;
36  using wsCoordType = real32;
37 
38  enum class DASType : integer
39  {
40  none,
41  dipole,
43  };
44 
45  enum class AttenuationType : integer
46  {
47  none,
48  sls,
49  };
50 
51 
53  static real32 evaluateRicker( real64 const time_n, real32 const f0, real32 const t0, localIndex const order )
54  {
55  real32 const delay = t0 > 0 ? t0 : 1 / f0;
56  real32 pulse = 0.0;
57  real32 const alpha = -pow( f0 * M_PI, 2 );
58  real32 const time_d = time_n - delay;
59  real32 const gaussian = exp( alpha * pow( time_d, 2 ));
60  localIndex const sgn = pow( -1, order + 1 );
61 
62  switch( order )
63  {
64  case 0:
65  pulse = sgn * gaussian;
66  break;
67  case 1:
68  pulse = sgn * (2 * alpha * time_d) * gaussian;
69  break;
70  case 2:
71  pulse = sgn * (2 * alpha + 4 * pow( alpha, 2 ) * pow( time_d, 2 )) * gaussian;
72  break;
73  case 3:
74  pulse = sgn * (12 * pow( alpha, 2 ) * time_d + 8 * pow( alpha, 3 ) * pow( time_d, 3 )) * gaussian;
75  break;
76  case 4:
77  pulse = sgn * (12 * pow( alpha, 2 ) + 48 * pow( alpha, 3 ) * pow( time_d, 2 ) + 16 * pow( alpha, 4 ) * pow( time_d, 4 )) * gaussian;
78  break;
79  default:
80  GEOS_ERROR( "This option is not supported yet, rickerOrder must be in range {0:4}" );
81  }
82 
83  return pulse;
84  }
85 
94  static void initTrace( char const * prefix,
95  string const & name,
96  bool const outputSeismoTrace,
97  localIndex const nReceivers,
98  arrayView1d< localIndex const > const receiverIsLocal )
99  {
100  if( !outputSeismoTrace ) return;
101 
102  string const outputDir = OutputBase::getOutputDirectory();
103  RAJA::ReduceSum< ReducePolicy< serialPolicy >, localIndex > count( 0 );
104 
105  forAll< serialPolicy >( nReceivers, [=] ( localIndex const ircv )
106  {
107  if( receiverIsLocal[ircv] == 1 )
108  {
109  count += 1;
110  string const fn = joinPath( outputDir, GEOS_FMT( "{}_{}_{:03}.txt", prefix, name, ircv ) );
111  std::ofstream f( fn, std::ios::out | std::ios::trunc );
112  }
113  } );
114 
115  localIndex const total = MpiWrapper::sum( count.get() );
116  GEOS_ERROR_IF( nReceivers != total, GEOS_FMT( ": Invalid distribution of receivers: nReceivers={} != MPI::sum={}.", nReceivers, total ) );
117  }
118 
131  static void writeSeismoTraceVector( char const * prefix,
132  string const & name,
133  bool const outputSeismoTrace,
134  localIndex const nReceivers,
135  arrayView1d< localIndex const > const receiverIsLocal,
136  localIndex const nsamplesSeismoTrace,
137  arrayView2d< real32 const > const varAtReceiversx,
138  arrayView2d< real32 const > const varAtReceiversy,
139  arrayView2d< real32 const > const varAtReceiversz )
140  {
141  writeSeismoTrace( prefix, name, outputSeismoTrace, nReceivers, receiverIsLocal, nsamplesSeismoTrace, varAtReceiversx );
142  writeSeismoTrace( prefix, name, outputSeismoTrace, nReceivers, receiverIsLocal, nsamplesSeismoTrace, varAtReceiversy );
143  writeSeismoTrace( prefix, name, outputSeismoTrace, nReceivers, receiverIsLocal, nsamplesSeismoTrace, varAtReceiversz );
144  }
145 
156  static void writeSeismoTrace( char const * prefix,
157  string const & name,
158  bool const outputSeismoTrace,
159  localIndex const nReceivers,
160  arrayView1d< localIndex const > const receiverIsLocal,
161  localIndex const nsamplesSeismoTrace,
162  arrayView2d< real32 const > const varAtReceivers )
163  {
164  if( !outputSeismoTrace ) return;
165 
166  string const outputDir = OutputBase::getOutputDirectory();
167  forAll< serialPolicy >( nReceivers, [=] ( localIndex const ircv )
168  {
169  if( receiverIsLocal[ircv] == 1 )
170  {
171  string const fn = joinPath( outputDir, GEOS_FMT( "{}_{}_{:03}.txt", prefix, name, ircv ) );
172  std::ofstream f( fn, std::ios::app );
173  if( f )
174  {
175  GEOS_LOG_RANK( GEOS_FMT( "Append to seismo trace file {}", fn ) );
176  for( localIndex iSample = 0; iSample < nsamplesSeismoTrace; ++iSample )
177  {
178  // index - time - value
179  f << iSample << " " << varAtReceivers[iSample][nReceivers] << " " << varAtReceivers[iSample][ircv] << std::endl;
180  }
181  f.close();
182  }
183  else
184  {
185  GEOS_WARNING( GEOS_FMT( "Failed to open output file {}", fn ) );
186  }
187  }
188  } );
189  }
190 
206  static void computeSeismoTrace( real64 const time_n,
207  real64 const dt,
208  real64 const timeSeismo,
209  localIndex const iSeismo,
210  arrayView2d< localIndex const > const receiverNodeIds,
211  arrayView2d< real64 const > const receiverConstants,
212  arrayView1d< localIndex const > const receiverIsLocal,
213  arrayView1d< real32 const > const var_np1,
214  arrayView1d< real32 const > const var_n,
215  arrayView2d< real32 > varAtReceivers,
216  arrayView1d< real32 > coeffs = {},
217  bool add = false )
218  {
219  real64 const time_np1 = time_n + dt;
220 
221  real32 const a1 = LvArray::math::abs( dt ) < epsilonLoc ? 1.0 : (time_np1 - timeSeismo) / dt;
222  real32 const a2 = 1.0 - a1;
223 
224  localIndex const nReceivers = receiverConstants.size( 0 );
225  forAll< EXEC_POLICY >( nReceivers, [=] GEOS_HOST_DEVICE ( localIndex const ircv )
226  {
227  if( receiverIsLocal[ircv] > 0 )
228  {
229  real32 vtmp_np1 = 0.0, vtmp_n = 0.0;
230  for( localIndex inode = 0; inode < receiverConstants.size( 1 ); ++inode )
231  {
232  if( receiverNodeIds( ircv, inode ) >= 0 )
233  {
234  vtmp_np1 += var_np1[receiverNodeIds( ircv, inode )] * receiverConstants( ircv, inode );
235  vtmp_n += var_n[receiverNodeIds( ircv, inode )] * receiverConstants( ircv, inode );
236  }
237  }
238  // linear interpolation between the pressure value at time_n and time_{n+1}
239  real32 receiverCoeff = coeffs.size( 0 ) == 0 ? 1.0 : coeffs( ircv );
240  if( add )
241  {
242  varAtReceivers( iSeismo, ircv ) += receiverCoeff * ( a1 * vtmp_n + a2 * vtmp_np1 );
243  }
244  else
245  {
246  varAtReceivers( iSeismo, ircv ) = receiverCoeff * ( a1 * vtmp_n + a2 * vtmp_np1 );
247  }
248  // NOTE: varAtReceivers has size(1) = numReceiversGlobal + 1, this does not OOB
249  // left in the forAll loop for sync issues since the following does not depend on `ircv`
250  varAtReceivers( iSeismo, nReceivers ) = a1 * time_n + a2 * time_np1;
251  }
252  } );
253  }
254 
270  static void compute2dVariableSeismoTrace( real64 const time_n,
271  real64 const dt,
272  localIndex const regionIndex,
273  arrayView1d< localIndex const > const receiverRegion,
274  real64 const timeSeismo,
275  localIndex const iSeismo,
276  arrayView1d< localIndex const > const receiverElem,
277  arrayView2d< real64 const > const receiverConstants,
278  arrayView1d< localIndex const > const receiverIsLocal,
279  arrayView2d< real32 const > const var_np1,
280  arrayView2d< real32 const > const var_n,
281  arrayView2d< real32 > varAtReceivers )
282  {
283  real64 const time_np1 = time_n + dt;
284 
285  real32 const a1 = dt < epsilonLoc ? 1.0 : (time_np1 - timeSeismo) / dt;
286  real32 const a2 = 1.0 - a1;
287 
288  localIndex const nReceivers = receiverConstants.size( 0 );
289 
290  forAll< EXEC_POLICY >( nReceivers, [=] GEOS_HOST_DEVICE ( localIndex const ircv )
291  {
292  if( receiverIsLocal[ircv] == 1 )
293  {
294  if( receiverRegion[ircv] == regionIndex )
295  {
296  real32 vtmp_np1 = 0.0, vtmp_n = 0.0;
297  for( localIndex inode = 0; inode < receiverConstants.size( 1 ); ++inode )
298  {
299  vtmp_np1 += var_np1( receiverElem[ircv], inode ) * receiverConstants( ircv, inode );
300  vtmp_n += var_n( receiverElem[ircv], inode ) * receiverConstants( ircv, inode );
301  }
302  // linear interpolation between the pressure value at time_n and time_{n+1}
303  varAtReceivers( iSeismo, ircv ) = a1 * vtmp_n + a2 * vtmp_np1;
304  // NOTE: varAtReceivers has size(1) = numReceiversGlobal + 1, this does not OOB
305  // left in the forAll loop for sync issues since the following does not depend on `ircv`
306  varAtReceivers( iSeismo, nReceivers ) = a1 * time_n + a2 * time_np1;
307  }
308  }
309  } );
310  }
311 
323  static bool
324  locateSourceElement( real64 const numFacesPerElem,
325  real64 const (&elemCenter)[3],
326  arrayView2d< real64 const > const faceNormal,
327  arrayView2d< real64 const > const faceCenter,
328  arraySlice1d< localIndex const > const elemsToFaces,
329  real64 const (&coords)[3] )
330  {
331  //Loop over the element faces
332  real64 tmpVector[3]{};
333  for( localIndex kfe = 0; kfe < numFacesPerElem; ++kfe )
334  {
335 
336  localIndex const iface = elemsToFaces[kfe];
337  real64 faceCenterOnFace[3] = {faceCenter[iface][0],
338  faceCenter[iface][1],
339  faceCenter[iface][2]};
340  real64 faceNormalOnFace[3] = {faceNormal[iface][0],
341  faceNormal[iface][1],
342  faceNormal[iface][2]};
343 
344  //Test to make sure if the normal is outwardly directed
345  LvArray::tensorOps::copy< 3 >( tmpVector, faceCenterOnFace );
346  LvArray::tensorOps::subtract< 3 >( tmpVector, elemCenter );
347  if( LvArray::tensorOps::AiBi< 3 >( tmpVector, faceNormalOnFace ) < 0.0 )
348  {
349  LvArray::tensorOps::scale< 3 >( faceNormalOnFace, -1 );
350  }
351 
352  // compute the vector face center to query point
353  LvArray::tensorOps::subtract< 3 >( faceCenterOnFace, coords );
354  localIndex const s = computationalGeometry::sign( LvArray::tensorOps::AiBi< 3 >( faceNormalOnFace, faceCenterOnFace ));
355 
356  // all dot products should be non-negative (we enforce outward normals)
357  if( s < 0 ) return false;
358 
359  }
360  return true;
361  }
362 
371  template< typename FE_TYPE >
373  static void
377  real64 (& coordsOnRefElem)[3] )
378  {
379  // only the eight corners of the mesh cell are needed to compute the Jacobian
380  real64 xLocal[8][3]{};
381  for( localIndex a = 0; a < 8; ++a )
382  {
383  LvArray::tensorOps::copy< 3 >( xLocal[a], nodeCoords[ elemsToNodes[ a ] ] );
384  }
385  // coordsOnRefElem = invJ*(coords-coordsNode_0)
386  real64 invJ[3][3]{};
387  FE_TYPE::invJacobianTransformation( 0, xLocal, invJ );
388  for( localIndex i = 0; i < 3; ++i )
389  {
390  // init at (-1,-1,-1) as the origin of the referential elem
391  coordsOnRefElem[i] = -1.0;
392  for( localIndex j = 0; j < 3; ++j )
393  {
394  coordsOnRefElem[i] += invJ[i][j] * (coords[j] - xLocal[0][j]);
395  }
396  }
397  }
398 
411  static void dotProduct( localIndex const size,
412  arrayView1d< real32 > const & vector1,
413  arrayView1d< real32 > const & vector2,
414  real64 & res )
415  {
416 
417  RAJA::ReduceSum< parallelDeviceReduce, real64 > tmp( 0.0 );
418  forAll< EXEC_POLICY >( size, [=] GEOS_HOST_DEVICE ( localIndex const a )
419  {
420  tmp+= vector1[a]*vector2[a];
421  } );
422 
423  res = tmp.get();
424 
425  }
426 
434  static
435  R1Tensor computeDASVector( real64 const dip, real64 const azimuth )
436  {
437  real64 cd = cos( dip );
438  real64 v1 = cd * cos( azimuth );
439  real64 v2 = cd * sin( azimuth );
440  real64 v3 = sin( dip );
441  R1Tensor dasVector = { v1, v2, v3 };
442  return dasVector;
443  }
444 
445 };
446 
449  "none",
450  "dipole",
451  "strainIntegration" );
452 
454  "none",
455  "sls" );
456 
457 } /* namespace geos */
458 
459 #endif /* GEOS_PHYSICSSOLVERS_WAVEPROPAGATION_WAVESOLVERUTILS_HPP_ */
#define GEOS_HOST_DEVICE
Marks a host-device function.
Definition: GeosxMacros.hpp:49
#define GEOS_LOG_RANK(msg)
Log a message to the rank output stream.
Definition: Logger.hpp:126
#define GEOS_ERROR(msg)
Raise a hard error and terminate the program.
Definition: Logger.hpp:157
#define GEOS_WARNING(msg)
Report a warning.
Definition: Logger.hpp:190
#define GEOS_ERROR_IF(EXP, msg)
Conditionally raise a hard error and terminate the program.
Definition: Logger.hpp:142
static string const & getOutputDirectory()
Getter for the output directory.
ArrayView< T, 1 > arrayView1d
Alias for 1D array view.
Definition: DataTypes.hpp:180
float real32
32-bit floating point type.
Definition: DataTypes.hpp:97
ENUM_STRINGS(LinearSolverParameters::SolverType, "direct", "cg", "gmres", "fgmres", "bicgstab", "preconditioner")
Declare strings associated with enumeration values.
double real64
64-bit floating point type.
Definition: DataTypes.hpp:99
GEOS_LOCALINDEX_TYPE localIndex
Local index type (for indexing objects within an MPI partition).
Definition: DataTypes.hpp:85
ArraySlice< T, 1, USD > arraySlice1d
Alias for 1D array slice.
Definition: DataTypes.hpp:184
std::int32_t integer
Signed integer type.
Definition: DataTypes.hpp:82
std::string joinPath(ARGS const &... args)
Join parts of a path.
Definition: Path.hpp:189
ArrayView< T, 2, USD > arrayView2d
Alias for 2D array view.
Definition: DataTypes.hpp:196
static T sum(T const &value, MPI_Comm comm=MPI_COMM_GEOS)
Convenience function for a MPI_Allreduce using a MPI_SUM operation.
static GEOS_HOST_DEVICE void computeCoordinatesOnReferenceElement(real64 const (&coords)[3], arraySlice1d< localIndex const, cells::NODE_MAP_USD - 1 > const elemsToNodes, arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const nodeCoords, real64(&coordsOnRefElem)[3])
Convert a mesh element point coordinate into a coordinate on the reference element.
static void compute2dVariableSeismoTrace(real64 const time_n, real64 const dt, localIndex const regionIndex, arrayView1d< localIndex const > const receiverRegion, real64 const timeSeismo, localIndex const iSeismo, arrayView1d< localIndex const > const receiverElem, arrayView2d< real64 const > const receiverConstants, arrayView1d< localIndex const > const receiverIsLocal, arrayView2d< real32 const > const var_np1, arrayView2d< real32 const > const var_n, arrayView2d< real32 > varAtReceivers)
Compute the seismo traces for 2d arrays.
static void writeSeismoTraceVector(char const *prefix, string const &name, bool const outputSeismoTrace, localIndex const nReceivers, arrayView1d< localIndex const > const receiverIsLocal, localIndex const nsamplesSeismoTrace, arrayView2d< real32 const > const varAtReceiversx, arrayView2d< real32 const > const varAtReceiversy, arrayView2d< real32 const > const varAtReceiversz)
Convenient helper for 3D vectors calling 3 times the scalar version with only the sampled variable ar...
static GEOS_HOST_DEVICE bool locateSourceElement(real64 const numFacesPerElem, real64 const (&elemCenter)[3], arrayView2d< real64 const > const faceNormal, arrayView2d< real64 const > const faceCenter, arraySlice1d< localIndex const > const elemsToFaces, real64 const (&coords)[3])
Check if the source point is inside an element or not.
static void dotProduct(localIndex const size, arrayView1d< real32 > const &vector1, arrayView1d< real32 > const &vector2, real64 &res)
Compute dotProduct between two vectors.
static void computeSeismoTrace(real64 const time_n, real64 const dt, real64 const timeSeismo, localIndex const iSeismo, arrayView2d< localIndex const > const receiverNodeIds, arrayView2d< real64 const > const receiverConstants, arrayView1d< localIndex const > const receiverIsLocal, arrayView1d< real32 const > const var_np1, arrayView1d< real32 const > const var_n, arrayView2d< real32 > varAtReceivers, arrayView1d< real32 > coeffs={}, bool add=false)
Compute the seismo traces.
@ none
deactivate attenuation (default)
@ sls
istandard-linear-solid description [Fichtner 2014]
static GEOS_HOST_DEVICE R1Tensor computeDASVector(real64 const dip, real64 const azimuth)
Converts the DAS direction from dip/azimuth to a 3D unit vector.
@ none
deactivate DAS computation
@ dipole
use dipole formulation for DAS
@ strainIntegration
use strain integration for DAS
static void writeSeismoTrace(char const *prefix, string const &name, bool const outputSeismoTrace, localIndex const nReceivers, arrayView1d< localIndex const > const receiverIsLocal, localIndex const nsamplesSeismoTrace, arrayView2d< real32 const > const varAtReceivers)
Write the seismo traces to a file.
static void initTrace(char const *prefix, string const &name, bool const outputSeismoTrace, localIndex const nReceivers, arrayView1d< localIndex const > const receiverIsLocal)
Initialize (clear) the trace file.