GEOS
TableFunction.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 Total, S.A
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 
20 #ifndef GEOS_FUNCTIONS_TABLEFUNCTION_HPP_
21 #define GEOS_FUNCTIONS_TABLEFUNCTION_HPP_
22 
23 #include "FunctionBase.hpp"
24 
26 #include "LvArray/src/tensorOps.hpp"
28 #include "common/Units.hpp"
29 
30 namespace geos
31 {
32 
39 {
40 public:
41 
44  {
45  Linear,
46  Nearest,
47  Upper,
48  Lower
49  };
50 
53  {
55  bool writeCSV;
57  bool writeInLog;
58  };
59 
61  static constexpr integer maxDimensions = 4;
62 
69  {
70 public:
71 
75 
76  KernelWrapper() = default;
77  KernelWrapper( KernelWrapper const & ) = default;
78  KernelWrapper( KernelWrapper && ) = default;
79  KernelWrapper & operator=( KernelWrapper const & ) = default;
80 
84  {
85  m_coordinates = std::move( other.m_coordinates );
86  m_values = std::move( other.m_values );
87  m_interpolationMethod = other.m_interpolationMethod;
88  return *this;
89  }
90 
92 
99  template< typename IN_ARRAY >
101  real64 compute( IN_ARRAY const & input ) const;
102 
109  template< typename IN_ARRAY, typename OUT_ARRAY >
111  real64 compute( IN_ARRAY const & input, OUT_ARRAY && derivatives ) const;
112 
120  void move( LvArray::MemorySpace const space, bool const touch )
121  {
122  m_coordinates.move( space, touch );
123  m_values.move( space, touch );
124  }
125 
126 private:
127 
128  friend class TableFunction; // Allow only parent class to construct the wrapper
129 
141  KernelWrapper( InterpolationType interpolationMethod,
142  ArrayOfArraysView< real64 const > const & coordinates,
143  arrayView1d< real64 const > const & values );
144 
150  template< typename IN_ARRAY >
152  real64
153  interpolateLinear( IN_ARRAY const & input ) const;
154 
161  template< typename IN_ARRAY, typename OUT_ARRAY >
163  real64
164  interpolateLinear( IN_ARRAY const & input, OUT_ARRAY && derivatives ) const;
165 
171  template< typename IN_ARRAY >
173  real64
174  interpolateRound( IN_ARRAY const & input ) const;
175 
182  template< typename IN_ARRAY, typename OUT_ARRAY >
184  real64
185  interpolateRound( IN_ARRAY const & input, OUT_ARRAY && derivatives ) const;
186 
188  TableFunction::InterpolationType m_interpolationMethod = InterpolationType::Linear;
189 
191  ArrayOfArraysView< real64 const > m_coordinates;
192 
195  };
196 
202  TableFunction( const string & name,
203  dataRepository::Group * const parent );
204 
209  static string catalogName() { return "TableFunction"; }
210 
214  virtual void initializeFunction() override;
215 
220 
228  virtual void evaluate( dataRepository::Group const & group,
229  real64 const time,
231  arrayView1d< real64 > const & result ) const override final
232  {
233  FunctionBase::evaluateT< TableFunction, parallelHostPolicy >( group, time, set, result );
234  }
235 
241  virtual real64 evaluate( real64 const * const input ) const override final;
242 
250  void checkCoord( real64 coord, localIndex dim ) const;
251 
255  integer numDimensions() const { return LvArray::integerConversion< integer >( m_coordinates.size() ); }
256 
261  ArrayOfArraysView< real64 const > getCoordinates() const { return m_coordinates.toViewConst(); }
262 
266  ArrayOfArraysView< real64 > getCoordinates() { return m_coordinates.toView(); }
267 
272  arrayView1d< real64 const > getValues() const { return m_values.toViewConst(); }
273 
277  array1d< real64 > & getValues() { return m_values; }
278 
283  InterpolationType getInterpolationMethod() const { return m_interpolationMethod; }
284 
289  units::Unit getDimUnit( localIndex const dim ) const
290  {
291  return size_t(dim) < m_dimUnits.size() ? m_dimUnits[dim] : units::Unknown;
292  }
293 
299 
305  void setTableCoordinates( array1d< real64_array > const & coordinates,
306  std::vector< units::Unit > const & dimUnits = {} );
307 
312  void setDimUnits( std::vector< units::Unit > const & dimUnits )
313  {
314  m_dimUnits = dimUnits;
315  }
316 
322  void setTableValues( real64_array values, units::Unit unit = units::Unknown );
323 
329  {
330  m_valueUnit = unit;
331  }
332 
336  units::Unit getValueUnit() const { return m_valueUnit; }
337 
338 
343  void outputPVTTableData( OutputOptions const pvtOutputOpts ) const;
344 
350 
353  {
355  static constexpr char const * coordinatesString() { return "coordinates"; }
357  static constexpr char const * valuesString() { return "values"; }
359  static constexpr char const * interpolationString() { return "interpolation"; }
361  static constexpr char const * coordinateFilesString() { return "coordinateFiles"; }
363  static constexpr char const * voxelFileString() { return "voxelFile"; }
364  };
365 
366 private:
367 
374  void readFile( string const & filename, array1d< real64 > & target );
375 
376 
378  array1d< real64 > m_tableCoordinates1D;
379 
381  path_array m_coordinateFiles;
382 
384  Path m_voxelFile;
385 
387  InterpolationType m_interpolationMethod;
388 
390  ArrayOfArrays< real64 > m_coordinates;
391 
393  array1d< real64 > m_values;
394 
396  std::vector< units::Unit > m_dimUnits;
397 
399  units::Unit m_valueUnit;
400 
402  KernelWrapper m_kernelWrapper;
403 
404 };
406 template< typename IN_ARRAY >
409 real64
410 TableFunction::KernelWrapper::compute( IN_ARRAY const & input ) const
411 {
412  if( m_interpolationMethod == TableFunction::InterpolationType::Linear )
413  {
414  return interpolateLinear( input );
415  }
416  else // Nearest, Upper, Lower interpolation methods
417  {
418  return interpolateRound( input );
419  }
420 }
421 
422 template< typename IN_ARRAY >
425 real64
426 TableFunction::KernelWrapper::interpolateLinear( IN_ARRAY const & input ) const
427 {
428  integer const numDimensions = LvArray::integerConversion< integer >( m_coordinates.size() );
429  localIndex bounds[maxDimensions][2]{};
430  real64 weights[maxDimensions][2]{};
431 
432  // Determine position, weights
433  for( localIndex dim = 0; dim < numDimensions; ++dim )
434  {
435  arraySlice1d< real64 const > const coords = m_coordinates[dim];
436  if( input[dim] <= coords[0] )
437  {
438  // Coordinate is to the left of this axis
439  bounds[dim][0] = 0;
440  bounds[dim][1] = 0;
441  weights[dim][0] = 0.0;
442  weights[dim][1] = 1.0;
443  }
444  else if( input[dim] >= coords[coords.size() - 1] )
445  {
446  // Coordinate is to the right of this axis
447  bounds[dim][0] = coords.size() - 1;
448  bounds[dim][1] = bounds[dim][0];
449  weights[dim][0] = 1.0;
450  weights[dim][1] = 0.0;
451  }
452  else
453  {
454  // Find the coordinate index
456  // Note: find uses a binary search... If we assume coordinates are
457  // evenly spaced, we can speed things up considerably
458  auto const lower = LvArray::sortedArrayManipulation::find( coords.begin(), coords.size(), input[dim] );
459  bounds[dim][1] = LvArray::integerConversion< localIndex >( lower );
460  bounds[dim][0] = bounds[dim][1] - 1;
461 
462  real64 const dx = coords[bounds[dim][1]] - coords[bounds[dim][0]];
463  weights[dim][0] = 1.0 - ( input[dim] - coords[bounds[dim][0]]) / dx;
464  weights[dim][1] = 1.0 - weights[dim][0];
465  }
466  }
467 
468  // Calculate the result
469  real64 value = 0.0;
470  integer const numCorners = 1 << numDimensions;
471  for( integer point = 0; point < numCorners; ++point )
472  {
473  // Find array index
474  localIndex tableIndex = 0;
475  localIndex stride = 1;
476  for( integer dim = 0; dim < numDimensions; ++dim )
477  {
478  integer const corner = (point >> dim) & 1;
479  tableIndex += bounds[dim][corner] * stride;
480  stride *= m_coordinates.sizeOfArray( dim );
481  }
482 
483  // Determine weighted value
484  real64 cornerValue = m_values[tableIndex];
485  for( integer dim = 0; dim < numDimensions; ++dim )
486  {
487  integer const corner = (point >> dim) & 1;
488  cornerValue *= weights[dim][corner];
489  }
490  value += cornerValue;
491  }
492  return value;
493 }
494 
495 template< typename IN_ARRAY >
498 real64
499 TableFunction::KernelWrapper::interpolateRound( IN_ARRAY const & input ) const
500 {
501  integer const numDimensions = LvArray::integerConversion< integer >( m_coordinates.size() );
502 
503  // Determine the index to the nearest table entry
504  localIndex tableIndex = 0;
505  localIndex stride = 1;
506  for( integer dim = 0; dim < numDimensions; ++dim )
507  {
508  arraySlice1d< real64 const > const coords = m_coordinates[dim];
509  // Determine the index along each table axis
510  localIndex subIndex;
511  if( input[dim] <= coords[0] )
512  {
513  // Coordinate is to the left of the table axis
514  subIndex = 0;
515  }
516  else if( input[dim] >= coords[coords.size() - 1] )
517  {
518  // Coordinate is to the right of the table axis
519  subIndex = coords.size() - 1;
520  }
521  else
522  {
523  // Coordinate is within the table axis
524  // Note: find() will return the index of the upper table vertex
525  auto const lower = LvArray::sortedArrayManipulation::find( coords.begin(), coords.size(), input[dim] );
526  subIndex = LvArray::integerConversion< localIndex >( lower );
527 
528  // Interpolation types:
529  // - Nearest returns the value of the closest table vertex
530  // - Upper returns the value of the next table vertex
531  // - Lower returns the value of the previous table vertex
532  if( m_interpolationMethod == TableFunction::InterpolationType::Nearest )
533  {
534  if( ( input[dim] - coords[subIndex - 1]) <= ( coords[subIndex] - input[dim]) )
535  {
536  --subIndex;
537  }
538  }
539  else if( m_interpolationMethod == TableFunction::InterpolationType::Lower )
540  {
541  if( subIndex > 0 )
542  {
543  --subIndex;
544  }
545  }
546  }
547 
548  // Increment the global table index
549  tableIndex += subIndex * stride;
550  stride *= coords.size();
551  }
552 
553  // Retrieve the nearest value
554  return m_values[tableIndex];
555 }
556 
557 template< typename IN_ARRAY, typename OUT_ARRAY >
560 real64
561 TableFunction::KernelWrapper::compute( IN_ARRAY const & input, OUT_ARRAY && derivatives ) const
562 {
563  // Linear interpolation
564  if( m_interpolationMethod == TableFunction::InterpolationType::Linear )
565  {
566  return interpolateLinear( input, derivatives );
567  }
568  // Nearest, Upper, Lower interpolation methods
569  else
570  {
571  return interpolateRound( input, derivatives );
572  }
573 }
574 
575 template< typename IN_ARRAY, typename OUT_ARRAY >
578 real64
579 TableFunction::KernelWrapper::interpolateLinear( IN_ARRAY const & input, OUT_ARRAY && derivatives ) const
580 {
581  integer const numDimensions = LvArray::integerConversion< integer >( m_coordinates.size() );
582 
583  localIndex bounds[maxDimensions][2]{};
584  real64 weights[maxDimensions][2]{};
585  real64 dWeights_dInput[maxDimensions][2]{};
586 
587  // Determine position, weights
588  for( integer dim = 0; dim < numDimensions; ++dim )
589  {
590  arraySlice1d< real64 const > const coords = m_coordinates[dim];
591  if( input[dim] <= coords[0] )
592  {
593  // Coordinate is to the left of this axis
594  bounds[dim][0] = 0;
595  bounds[dim][1] = 0;
596  weights[dim][0] = 0;
597  weights[dim][1] = 1;
598  dWeights_dInput[dim][0] = 0;
599  dWeights_dInput[dim][1] = 0;
600  }
601  else if( input[dim] >= coords[coords.size() - 1] )
602  {
603  // Coordinate is to the right of this axis
604  bounds[dim][0] = coords.size() - 1;
605  bounds[dim][1] = bounds[dim][0];
606  weights[dim][0] = 1;
607  weights[dim][1] = 0;
608  dWeights_dInput[dim][0] = 0;
609  dWeights_dInput[dim][1] = 0;
610  }
611  else
612  {
613  // Find the coordinate index
615  // Note: find uses a binary search... If we assume coordinates are
616  // evenly spaced, we can speed things up considerably
617  auto lower = LvArray::sortedArrayManipulation::find( coords.begin(), coords.size(), input[dim] );
618  bounds[dim][1] = LvArray::integerConversion< localIndex >( lower );
619  bounds[dim][0] = bounds[dim][1] - 1;
620 
621  real64 const dx = coords[bounds[dim][1]] - coords[bounds[dim][0]];
622  weights[dim][0] = 1.0 - ( input[dim] - coords[bounds[dim][0]]) / dx;
623  weights[dim][1] = 1.0 - weights[dim][0];
624  dWeights_dInput[dim][0] = -1.0 / dx;
625  dWeights_dInput[dim][1] = -dWeights_dInput[dim][0];
626  }
627  }
628 
629  // Calculate the result
630  real64 value = 0.0;
631  for( integer dim = 0; dim < numDimensions; ++dim )
632  {
633  derivatives[dim] = 0.0;
634  }
635 
636  integer const numCorners = 1 << numDimensions;
637  for( integer point = 0; point < numCorners; ++point )
638  {
639  // Find array index
640  localIndex tableIndex = 0;
641  localIndex stride = 1;
642  for( integer dim = 0; dim < numDimensions; ++dim )
643  {
644  integer const corner = (point >> dim) & 1;
645  tableIndex += bounds[dim][corner] * stride;
646  stride *= m_coordinates.sizeOfArray( dim );
647  }
648 
649  // Determine weighted value
650  real64 cornerValue = m_values[tableIndex];
651  real64 dCornerValue_dInput[maxDimensions]{};
652  for( integer dim = 0; dim < numDimensions; ++dim )
653  {
654  dCornerValue_dInput[dim] = cornerValue;
655  }
656 
657  for( integer dim = 0; dim < numDimensions; ++dim )
658  {
659  integer const corner = (point >> dim) & 1;
660  cornerValue *= weights[dim][corner];
661  for( integer kk = 0; kk < numDimensions; ++kk )
662  {
663  dCornerValue_dInput[kk] *= ( dim == kk ) ? dWeights_dInput[dim][corner] : weights[dim][corner];
664  }
665  }
666 
667  for( integer dim = 0; dim < numDimensions; ++dim )
668  {
669  derivatives[dim] += dCornerValue_dInput[dim];
670  }
671  value += cornerValue;
672  }
673  return value;
674 }
675 
676 template< typename IN_ARRAY, typename OUT_ARRAY >
679 real64
680 TableFunction::KernelWrapper::interpolateRound( IN_ARRAY const & input, OUT_ARRAY && derivatives ) const
681 {
682  GEOS_UNUSED_VAR( input, derivatives );
683  GEOS_ERROR( "Rounding interpolation with derivatives not implemented" );
684  return 0.0;
685 }
686 
688 
691  "linear",
692  "nearest",
693  "upper",
694  "lower" );
695 
701 template<>
702 string TableTextFormatter::toString< TableFunction >( TableFunction const & tableData ) const;
703 
709 template<>
710 string TableCSVFormatter::toString< TableFunction >( TableFunction const & tableData ) const;
711 
712 } /* namespace geos */
713 
714 #endif /* GEOS_FUNCTIONS_TABLEFUNCTION_HPP_ */
#define GEOS_HOST_DEVICE
Marks a host-device function.
Definition: GeosxMacros.hpp:49
#define GEOS_UNUSED_VAR(...)
Mark an unused variable and silence compiler warnings.
Definition: GeosxMacros.hpp:84
#define GEOS_FORCE_INLINE
Marks a function or lambda for inlining.
Definition: GeosxMacros.hpp:51
#define GEOS_ERROR(msg)
Raise a hard error and terminate the program.
Definition: Logger.hpp:157
Enumerates the Units that are in use in GEOS and regroups useful conversion and formatting functions.
Unit
Enumerator of available unit types. Units are in SI by default.
Definition: Units.hpp:54
Class describing a file Path.
Definition: Path.hpp:33
GEOS_HOST_DEVICE real64 compute(IN_ARRAY const &input, OUT_ARRAY &&derivatives) const
Interpolate in the table with derivatives.
void move(LvArray::MemorySpace const space, bool const touch)
Move the KernelWrapper to the given execution space, optionally touching it.
GEOS_HOST_DEVICE real64 compute(IN_ARRAY const &input) const
Interpolate in the table.
InterpolationType
Enumerator of available interpolation types.
void setTableValues(real64_array values, units::Unit unit=units::Unknown)
Set the table values.
array1d< real64 > & getValues()
Get the table values.
arrayView1d< real64 const > getValues() const
Get the table values.
void checkCoord(real64 coord, localIndex dim) const
Check if the given coordinate is in the bounds of the table coordinates in the specified dimension,...
virtual void evaluate(dataRepository::Group const &group, real64 const time, SortedArrayView< localIndex const > const &set, arrayView1d< real64 > const &result) const override final
Method to evaluate a function on a target object.
ArrayOfArraysView< real64 > getCoordinates()
Get the table axes definitions.
void outputPVTTableData(OutputOptions const pvtOutputOpts) const
Print the table(s) in the log and/or CSV files when requested by the user.
void setTableCoordinates(array1d< real64_array > const &coordinates, std::vector< units::Unit > const &dimUnits={})
Set the table coordinates.
void setValueUnits(units::Unit unit)
Set the table value units.
void setDimUnits(std::vector< units::Unit > const &dimUnits)
Set the units of each dimension.
void reInitializeFunction()
Build the maps used to evaluate the table function.
KernelWrapper createKernelWrapper() const
Create an instance of the kernel wrapper.
TableFunction(const string &name, dataRepository::Group *const parent)
The constructor.
units::Unit getValueUnit() const
void setInterpolationMethod(InterpolationType const method)
Set the interpolation method.
static constexpr integer maxDimensions
maximum dimensions for the coordinates in the table
virtual real64 evaluate(real64 const *const input) const override final
Method to evaluate a function.
static string catalogName()
The catalog name interface.
virtual void initializeFunction() override
Initialize the table function.
ArrayOfArraysView< real64 const > getCoordinates() const
Get the table axes definitions.
integer numDimensions() const
units::Unit getDimUnit(localIndex const dim) const
InterpolationType getInterpolationMethod() const
Get the interpolation method.
Group & operator=(Group const &)=delete
Deleted copy assignment operator.
ArrayView< T, 1 > arrayView1d
Alias for 1D array view.
Definition: DataTypes.hpp:180
ENUM_STRINGS(LinearSolverParameters::SolverType, "direct", "cg", "gmres", "fgmres", "bicgstab", "preconditioner")
Declare strings associated with enumeration values.
LvArray::ArrayOfArraysView< T, INDEX_TYPE const, CONST_SIZES, LvArray::ChaiBuffer > ArrayOfArraysView
View of array of variable-sized arrays. See LvArray::ArrayOfArraysView for details.
Definition: DataTypes.hpp:286
array1d< Path > path_array
A 1-dimensional array of geos::Path types.
Definition: DataTypes.hpp:395
std::set< T > set
A set of local indices.
Definition: DataTypes.hpp:263
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
std::int32_t integer
Signed integer type.
Definition: DataTypes.hpp:82
array1d< real64 > real64_array
A 1-dimensional array of geos::real64 types.
Definition: DataTypes.hpp:389
std::size_t size_t
Unsigned size type.
Definition: DataTypes.hpp:79
LvArray::SortedArrayView< T, localIndex, LvArray::ChaiBuffer > SortedArrayView
A sorted array view of local indices.
Definition: DataTypes.hpp:271
Array< T, 1 > array1d
Alias for 1D array.
Definition: DataTypes.hpp:176
LvArray::ArrayOfArrays< T, INDEX_TYPE, LvArray::ChaiBuffer > ArrayOfArrays
Array of variable-sized arrays. See LvArray::ArrayOfArrays for details.
Definition: DataTypes.hpp:282
Struct containing output options.
bool writeInLog
Output PVT in log.
bool writeCSV
Output PVT in CSV file.
Struct containing lookup keys for data repository wrappers.
static constexpr char const * coordinatesString()
static constexpr char const * interpolationString()
static constexpr char const * valuesString()
static constexpr char const * voxelFileString()
static constexpr char const * coordinateFilesString()