Skip to content

Commit aa7765b

Browse files
authored
tensorOps changes (#205)
* Add new tensorOps methods * Remove explicit mentions of R1Tensor * Fix and document gdb-printers * Add tests for new tensorOps functions
1 parent 988f155 commit aa7765b

File tree

5 files changed

+274
-35
lines changed

5 files changed

+274
-35
lines changed

docs/sphinx/developmentAids.rst

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,85 @@
22
Development Aids (Coming soon)
33
###############################################################################
44

5-
Comming soon: documentation for
5+
Coming soon: documentation for
66

77
- ``LvArray::arrayManipulation``
88
- ``LvArray::sortedArrayManipulation``
99
- ``LvArray::bufferManipulation``
1010
- Creating a new buffer type
11+
12+
Debugging helpers
13+
=================
14+
15+
TotalView
16+
---------
17+
18+
GDB
19+
---
20+
21+
``LvArray`` comes with a collection of custom `GDB pretty printers <https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html>`_
22+
for buffer and array classes that simplify inspection of data in a debug session by adding extra member(s) to the object display
23+
that "views" the data in a convenient format recognizable by GDB and IDEs. This eliminates the need for users of these classes
24+
to understand the particular class structure and spend time obtaining and casting the actual data pointer to the right type.
25+
26+
.. note::
27+
28+
In order to allow GDB to load the pretty printer script (``scripts/gdb-printers.py``), the following lines must be added to ``.gdbinit``
29+
(with the proper path substituted):
30+
31+
.. code-block:: none
32+
33+
add-auto-load-safe-path /path/to/LvArray/
34+
directory /path/to/LvArray/
35+
36+
The first line allows GDB to load python scripts located under ``LvArray`` source tree.
37+
The second line adds ``LvArray`` source tree to the source path used by GDB to lookup file names, which allows it to find the
38+
pretty printer script by relative path (which is how it is referenced from the compiled binary).
39+
The ``.gdbinit`` file may be located under the user's home directory or current working directory where GDB is invoked.
40+
41+
The following pretty printers are available:
42+
43+
- For buffer types (``StackBuffer``, ``MallocBuffer`` and ``ChaiBuffer``) the extra member ``gdb_view`` is a C-array of the appropriate type
44+
and size equal to the buffer size.
45+
- For multidimensional arrays and slices (``ArraySlice``, ``ArrayView`` and ``Array``) the extra member ``gdb_view`` is a multidimensional
46+
C-array with the sizes equal to current runtime size of the array or slice. This only works correctly for arrays with default
47+
(unpermuted) layout.
48+
- For ``ArrayOfArrays`` (and consequently all of its descendants) each contained sub-array is added as a separate child that is
49+
again a C-array of size equal to that of the sub-array.
50+
51+
Example below demonstrates the difference between pretty printer output and raw output:
52+
53+
.. code-block:: none
54+
55+
(gdb) p dofNumber
56+
$1 = const geosx::arrayView1d & of size [10] = {gdb_view = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45}}
57+
(gdb) p /r dofNumber
58+
$2 = (const geosx::arrayView1d &) @0x7ffcda2a8ab0: {static NDIM = 1, static USD = 0, m_dims = {data = {10}}, m_strides = {data = {1}}, m_dataBuffer = {static hasShallowCopy = <optimized out>, m_pointer = 0x55bec1de4860, m_capacity = 10, m_pointerRecord = 0x55bec1dfa5c0}, m_singleParameterResizeIndex = 0}
59+
60+
This is how the variable is viewed in a debugging session in CLion IDE:
61+
62+
.. code-block:: none
63+
64+
dofNumber = {const geosx::arrayView1d &}
65+
gdb_view = {const long long [10]}
66+
[0] = {const long long} 0
67+
[1] = {const long long} 5
68+
[2] = {const long long} 10
69+
[3] = {const long long} 15
70+
[4] = {const long long} 20
71+
[5] = {const long long} 25
72+
[6] = {const long long} 30
73+
[7] = {const long long} 35
74+
[8] = {const long long} 40
75+
[9] = {const long long} 45
76+
NDIM = {const int} 1
77+
USD = {const int} 0
78+
m_dims = {LvArray::typeManipulation::CArray<long, 1>}
79+
m_strides = {LvArray::typeManipulation::CArray<long, 1>}
80+
m_dataBuffer = {LvArray::ChaiBuffer<long long const>}
81+
m_singleParameterResizeIndex = {int} 0
82+
83+
.. warning::
84+
85+
GDB printers are for host-only data. Attempting to display an array or buffer whose active pointer is a device pointer may have
86+
a range of outcomes, from incorrect data being displayed, to debugger crashes. The printer script is yet to be tested with ``cuda-gdb``.

scripts/gdb-printers.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def format_array(arr):
1010
return '[' + ']['.join([str(arr[i]) for i in range(array_length(arr))]) + ']'
1111

1212

13-
class CxxUtilsPrinter(gdb.printing.PrettyPrinter):
13+
class LvArrayPrinter(gdb.printing.PrettyPrinter):
1414
"""Base printer for LvArray classes"""
1515

1616
def __init__(self, val):
@@ -20,14 +20,14 @@ def real_type(self):
2020
return self.val.type.strip_typedefs()
2121

2222
def to_string(self):
23-
return self.real_type().name
23+
return str(self.real_type())
2424

2525
def children(self):
2626
return [(f.name, self.val.cast(f.type)) for f in self.real_type().fields() if f.is_base_class] \
2727
+ [(f.name, self.val[f.name]) for f in self.real_type().fields() if not f.is_base_class]
2828

2929

30-
class BufferPrinter(CxxUtilsPrinter):
30+
class BufferPrinter(LvArrayPrinter):
3131
"""Base class for printing buffer contents"""
3232

3333
def size(self):
@@ -46,15 +46,15 @@ def __setitem__(self, key, value):
4646
self.data()[key] = value
4747

4848
def to_string(self):
49-
return '%s of size %d' % (self.real_type().name, self.size())
49+
return '%s of size %d' % (self.real_type(), self.size())
5050

5151
def children(self):
5252
if self.data() != 0:
5353
array_type = self.value_type().array(self.size()-1)
5454
arr = self.data().dereference().cast(array_type)
5555
else:
5656
arr = self.data()
57-
return [('gdb_view', arr)] + CxxUtilsPrinter.children(self)
57+
return [('gdb_view', arr)] + LvArrayPrinter.children(self)
5858

5959

6060
class StackBufferPrinter(BufferPrinter):
@@ -77,10 +77,21 @@ def data(self):
7777
return self.val['m_pointer']
7878

7979

80+
class MallocBufferPrinter(BufferPrinter):
81+
"""Pretty-print a MallocBuffer"""
82+
83+
def size(self):
84+
return self.val['m_capacity']
85+
86+
def data(self):
87+
return self.val['m_data']
88+
89+
8090
def build_buffer_printer():
8191
pp = gdb.printing.RegexpCollectionPrettyPrinter("LvArray-buffers")
8292
pp.add_printer('LvArray::StackBuffer', 'LvArray::StackBuffer<.*>', StackBufferPrinter)
8393
pp.add_printer('LvArray::ChaiBuffer', 'LvArray::ChaiBuffer<.*>', ChaiBufferPrinter)
94+
pp.add_printer('LvArray::MallocBuffer', 'LvArray::MallocBuffer<.*>', MallocBufferPrinter)
8495
return pp
8596

8697

@@ -92,7 +103,7 @@ def build_buffer_printer():
92103
pass
93104

94105

95-
class ArraySlicePrinter(CxxUtilsPrinter):
106+
class ArraySlicePrinter(LvArrayPrinter):
96107
"""Pretty-print an ArraySlice"""
97108

98109
def value_type(self):
@@ -111,16 +122,16 @@ def data(self):
111122
return self.val['m_data']
112123

113124
def to_string(self):
114-
return '%s of size %s' % (self.real_type().name, format_array(self.dims()))
125+
return '%s of size %s' % (self.real_type(), format_array(self.dims()))
115126

116127
def children(self):
117128
array_type = self.value_type()
118129
for i in range(self.ndim()):
119130
array_type = array_type.array(self.dims()[self.ndim() - i - 1] - 1)
120-
return [('gdb_view', self.data().dereference().cast(array_type))] + CxxUtilsPrinter.children(self)
131+
return [('gdb_view', self.data().dereference().cast(array_type))] + LvArrayPrinter.children(self)
121132

122133

123-
class ArrayViewPrinter(CxxUtilsPrinter):
134+
class ArrayViewPrinter(LvArrayPrinter):
124135
"""Pretty-print an ArrayView"""
125136

126137
def value_type(self):
@@ -130,25 +141,25 @@ def ndim(self):
130141
return self.val.type.template_argument(1)
131142

132143
def dims(self):
133-
return self.val['m_dims']
144+
return self.val['m_dims']['data']
134145

135146
def strides(self):
136-
return self.val['m_strides']
147+
return self.val['m_strides']['data']
137148

138149
def data(self):
139150
return buffer_printer(self.val['m_dataBuffer']).data()
140151

141152
def to_string(self):
142-
return '%s of size %s' % (self.real_type().name, format_array(self.dims()))
153+
return '%s of size %s' % (self.real_type(), format_array(self.dims()))
143154

144155
def children(self):
145156
array_type = self.value_type()
146157
for i in range(self.ndim()):
147158
array_type = array_type.array(self.dims()[self.ndim() - i - 1] - 1)
148-
return [('gdb_view', self.data().dereference().cast(array_type))] + CxxUtilsPrinter.children(self)
159+
return [('gdb_view', self.data().dereference().cast(array_type))] + LvArrayPrinter.children(self)
149160

150161

151-
class ArrayOfArraysViewPrinter(CxxUtilsPrinter):
162+
class ArrayOfArraysViewPrinter(LvArrayPrinter):
152163
"""Pretty-print an ArrayOfArraysView"""
153164

154165
def value_type(self):
@@ -158,7 +169,7 @@ def data(self):
158169
return buffer_printer(self.val['m_values']).data()
159170

160171
def size(self):
161-
return buffer_printer(self.val['m_sizes']).size()
172+
return self.val['m_numArrays']
162173

163174
def size_of_array(self, i):
164175
return buffer_printer(self.val['m_sizes'])[i]
@@ -173,14 +184,14 @@ def get_array(self, i):
173184
return self.ptr_to_array(i).dereference().cast(self.value_type().array(self.size_of_array(i)-1))
174185

175186
def to_string(self):
176-
return '%s of size %d' % (self.real_type().name, self.size())
187+
return '%s of size %d' % (self.real_type(), self.size())
177188

178189
def child_arrays(self):
179190
for i in range(self.size()):
180191
yield str(i), self.get_array(i)
181192

182193
def children(self):
183-
return itertools.chain(CxxUtilsPrinter.children(self), self.child_arrays())
194+
return itertools.chain(LvArrayPrinter.children(self), self.child_arrays())
184195

185196

186197
def build_array_printer():

src/genericTensorOps.hpp

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,6 @@
1616
#include "ArraySlice.hpp"
1717
#include "math.hpp"
1818

19-
/**
20-
* @brief Forward declaration of R1Tensor for compatability with GEOSX.
21-
* @note Will be removed shortly.
22-
*/
23-
template< int T_dim >
24-
class R1TensorT;
25-
2619
/**
2720
* @name Initialization and assignment macros.
2821
* @brief Macros that aid in to initializng and assigning to common vector and matrix sizes.
@@ -156,18 +149,25 @@ namespace tensorOps
156149
namespace internal
157150
{
158151

152+
HAS_STATIC_MEMBER( SIZE );
153+
HAS_STATIC_MEMBER( NDIM );
154+
159155
/**
160-
* @brief Verify at compile time that the size of the R1Tensor is as expected.
161-
* @tparam PROVIDED_SIZE The size the R1Tensor should be.
162-
* @tparam INFERRED_SIZE The size of the R1Tensor.
163-
* TODO: Remove this.
156+
* @brief Verify at compile time that the size of a user-provided type is as expected.
157+
* @tparam ISIZE The size the array should be.
158+
* @tparam T The provided type.
159+
* @param src The value to check.
160+
* @note This overload is enabled for user-defined types that expose a compile-time size
161+
* parameter through a public static integral member variable SIZE
164162
*/
165-
template< std::ptrdiff_t PROVIDED_SIZE, int INFERRED_SIZE >
163+
template< std::ptrdiff_t ISIZE, typename T >
166164
LVARRAY_HOST_DEVICE inline constexpr
167-
void checkSizes( R1TensorT< INFERRED_SIZE > const & )
165+
std::enable_if_t< HasStaticMember_SIZE< T > >
166+
checkSizes( T const & src )
168167
{
169-
static_assert( PROVIDED_SIZE == INFERRED_SIZE,
170-
"Expected the first dimension of size PROVIDED_N, got an array of size INFERRED_N." );
168+
static_assert( ISIZE == T::SIZE,
169+
"Expected the first dimension of size ISIZE, got an type of size T::SIZE." );
170+
LVARRAY_UNUSED_VARIABLE( src );
171171
}
172172

173173
/**
@@ -216,7 +216,8 @@ void checkSizes( T const ( &src )[ INFERRED_M ][ INFERRED_N ] )
216216
*/
217217
template< std::ptrdiff_t ISIZE, typename ARRAY >
218218
LVARRAY_HOST_DEVICE inline CONSTEXPR_WITHOUT_BOUNDS_CHECK
219-
void checkSizes( ARRAY const & array )
219+
std::enable_if_t< HasStaticMember_NDIM< ARRAY > >
220+
checkSizes( ARRAY const & array )
220221
{
221222
static_assert( ARRAY::NDIM == 1, "Must be a 1D array." );
222223
#ifdef LVARRAY_BOUNDS_CHECK
@@ -235,7 +236,8 @@ void checkSizes( ARRAY const & array )
235236
*/
236237
template< std::ptrdiff_t ISIZE, std::ptrdiff_t JSIZE, typename ARRAY >
237238
LVARRAY_HOST_DEVICE inline CONSTEXPR_WITHOUT_BOUNDS_CHECK
238-
void checkSizes( ARRAY const & array )
239+
std::enable_if_t< HasStaticMember_NDIM< ARRAY > >
240+
checkSizes( ARRAY const & array )
239241
{
240242
static_assert( ARRAY::NDIM == 2, "Must be a 1D array." );
241243
#ifdef LVARRAY_BOUNDS_CHECK
@@ -486,6 +488,28 @@ void scaledCopy( DST_MATRIX && LVARRAY_RESTRICT_REF dstMatrix,
486488
}
487489
}
488490

491+
/**
492+
* @brief Add @p value to @p dstVector.
493+
* @tparam M The length of @p dstVector.
494+
* @tparam DST_VECTOR The type of @p dstVector.
495+
* @param dstVector The destination vector, of length M.
496+
* @param value The value to add.
497+
* @details Performs the operation @code dstVector[ i ] += value @endcode
498+
*/
499+
template< std::ptrdiff_t M, typename DST_VECTOR >
500+
LVARRAY_HOST_DEVICE CONSTEXPR_WITHOUT_BOUNDS_CHECK inline
501+
void addScalar( DST_VECTOR && LVARRAY_RESTRICT_REF dstVector,
502+
std::remove_reference_t< decltype( dstVector[0] ) > const value )
503+
{
504+
static_assert( M > 0, "M must be greater than zero." );
505+
internal::checkSizes< M >( dstVector );
506+
507+
for( std::ptrdiff_t i = 0; i < M; ++i )
508+
{
509+
dstVector[ i ] += value;
510+
}
511+
}
512+
489513
/**
490514
* @brief Add @p srcVector to @p dstVector.
491515
* @tparam ISIZE The length of @p dstVector and @p srcVector.
@@ -772,6 +796,33 @@ void Rij_eq_AiBj( DST_MATRIX && LVARRAY_RESTRICT_REF dstMatrix,
772796
}
773797
}
774798

799+
/**
800+
* @brief Perform the outer product of @p vectorA with itself writing the result to @p dstMatrix.
801+
* @tparam M The size of both dimensions of @p dstMatrix and the length of @p vectorA.
802+
* @tparam DST_MATRIX The type of @p dstMatrix.
803+
* @tparam VECTOR_A The type of @p vectorA.
804+
* @param dstMatrix The matrix the result is written to, of size M x N.
805+
* @param vectorA The first vector in the outer product, of length M.
806+
* @details Performs the operations @code dstMatrix[ i ][ j ] = vectorA[ i ] * vectorA[ j ] @endcode
807+
*/
808+
template< std::ptrdiff_t M, typename DST_MATRIX, typename VECTOR_A >
809+
LVARRAY_HOST_DEVICE CONSTEXPR_WITHOUT_BOUNDS_CHECK inline
810+
void Rij_eq_AiAj( DST_MATRIX && LVARRAY_RESTRICT_REF dstMatrix,
811+
VECTOR_A const & LVARRAY_RESTRICT_REF vectorA )
812+
{
813+
static_assert( M > 0, "M must be greater than zero." );
814+
internal::checkSizes< M, M >( dstMatrix );
815+
internal::checkSizes< M >( vectorA );
816+
817+
for( std::ptrdiff_t i = 0; i < M; ++i )
818+
{
819+
for( std::ptrdiff_t j = 0; j < M; ++j )
820+
{
821+
dstMatrix[ i ][ j ] = vectorA[ i ] * vectorA[ j ];
822+
}
823+
}
824+
}
825+
775826
/**
776827
* @brief Perform the outer product of @p vectorA and @p vectorB adding the result to @p dstMatrix.
777828
* @tparam ISIZE The size of the first dimension of @p dstMatrix and the length of @p vectorA.

src/typeManipulation.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,24 @@ public: \
7979
#define HAS_MEMBER_FUNCTION_NO_RTYPE( NAME, ... ) \
8080
IS_VALID_EXPRESSION( HasMemberFunction_ ## NAME, CLASS, std::declval< CLASS & >().NAME( __VA_ARGS__ ) )
8181

82+
/**
83+
* @brief Macro that expands to a static constexpr bool templated on a type that is only true when
84+
* the type has a nested type (or type alias) called @p NAME.
85+
* The name of the boolean variable is HasMemberType_ ## @p NAME.
86+
* @param NAME The name of the type to look for.
87+
*/
88+
#define HAS_MEMBER_TYPE( NAME ) \
89+
IS_VALID_EXPRESSION( HasMemberType_ ## NAME, CLASS, std::declval< typename CLASS::NAME >() )
90+
91+
/**
92+
* @brief Macro that expands to a static constexpr bool templated on a type that is only true when
93+
* the type has a static member called @p NAME.
94+
* The name of the boolean variable is HasStaticMember_ ## @p NAME.
95+
* @param NAME The name of the variable to look for.
96+
*/
97+
#define HAS_STATIC_MEMBER( NAME ) \
98+
IS_VALID_EXPRESSION( HasStaticMember_ ## NAME, CLASS, std::enable_if_t< !std::is_member_pointer< decltype( &CLASS::NAME ) >::value, bool >{} )
99+
82100
namespace LvArray
83101
{
84102

0 commit comments

Comments
 (0)