Add guide information for accessing/allocating data in arrays

In the previous guide, this was (mostly) in its own chapter. This may
have been an excessive amount of splitting things up.

There is still a missing section on the interface to the execution
environment (i.e. PrepareFor*). I plan to put that in the execution
objects chapter as this is the most likely place for a user to use them.
This commit is contained in:
Kenneth Moreland 2024-07-09 15:21:44 -04:00
parent 67b0fea23a
commit d67d27e0fc
8 changed files with 137 additions and 19 deletions

@ -180,9 +180,8 @@ Vecs from Portals
|VTKm| provides further examples of |Veclike| objects as well.
For example, the :class:`vtkm::VecFromPortal` and :class:`vtkm::VecFromPortalPermute` objects allow you to treat a subsection of an arbitrarily large array as a :class:`vtkm::Vec`.
These objects work by attaching to array portals, which are described in Section~\ref{sec:ArrayPortals}.
.. todo:: Fix reference to array portals.
These objects work by attaching to array portals, which are described in
:secref:`basic-array-handles:Array Portals`.
.. doxygenclass:: vtkm::VecFromPortal
:members:

@ -40,11 +40,8 @@ This is convenient for creating arrays used as the output for algorithms.
:file: GuideExampleArrayHandle.cxx
:caption: Creating an :class:`vtkm::cont::ArrayHandle` for output data.
Chapter \ref{chap:AccessingAllocatingArrays} describes in detail how to allocate memory and access data in an :class:`vtkm::cont::ArrayHandle`.
However, you can use the :func:`vtkm::cont::make_ArrayHandle` function for a simplified way to create an :class:`vtkm::cont::ArrayHandle` with data.
.. todo:: Update chapter reference above. Also consider moving the access/allocation chapter earlier.
There are times when you will wish to create a :class:`vtkm::cont::ArrayHandle` populated with existing data.
This can be done with the :func:`vtkm::cont::make_ArrayHandle` function.
:func:`vtkm::cont::make_ArrayHandle` has many forms.
An easy form to use takes an initializer list and creates a basic :class:`vtkm::cont::ArrayHandle` with it.
This allows you to create a short :class:`vtkm::cont::ArrayHandle` from literals.
@ -119,6 +116,37 @@ This solution is shown in :exlineref:ex:ArrayOutOfScope:MoveVector`.
.. todo:: Document moving basic C arrays somewhere.
------------------------------
Allocating
------------------------------
.. index::
double: array handle; allocate
:class:`vtkm::cont::ArrayHandle` is capable of allocating its own memory.
The most straightforward way to allocate memory is to call the :func:`vtkm::cont::ArrayHandle::Allocate` method.
The :func:`vtkm::cont::ArrayHandle::Allocate` method takes a single argument, which is the number of elements to make the array.
.. load-example:: ArrayHandleAllocate
:file: GuideExampleArrayHandle.cxx
:caption: Allocating an :class:`vtkm::cont::ArrayHandle`.
By default when you :func:`vtkm::cont::ArrayHandle::Allocate` an array, it potentially destroys any existing data in it.
However, there are cases where you wish to grow or shrink an array while preserving the existing data.
To preserve the existing data when allocating an array, pass :enumerator:`vtkm::CopyFlag::On` as an optional second argument.
.. load-example:: ArrayHandleReallocate
:file: GuideExampleArrayHandle.cxx
:caption: Resizing an :class:`vtkm::cont::ArrayHandle`.
It is also possible to initialize new values in an allocated :class:`vtkm::cont::ArrayHandle` by using the :func:`vtkm::cont::ArrayHandle::AllocateAndFill` method.
.. didyouknow::
The ability to allocate memory is a key difference between :class:`vtkm::cont::ArrayHandle` and many other common forms of smart pointers.
When one :class:`vtkm::cont::ArrayHandle` allocates new memory, all other :class:`vtkm::cont::ArrayHandle`'s pointing to the same managed memory get the newly allocated memory.
This feature makes it possible to pass a :class:`vtkm::cont::ArrayHandle` to a method to be reallocated and filled without worrying about C++ details on how to reference the :class:`vtkm::cont::ArrayHandle` object itself.
------------------------------
Deep Array Copies
------------------------------
@ -150,6 +178,72 @@ The destination array will be properly reallocated to the correct size.
.. doxygenfunction:: vtkm::cont::ArrayCopy(const SourceArrayType&, vtkm::cont::UnknownArrayHandle&)
------------------------------
Array Portals
------------------------------
.. index::
single: array portal
single: array handle; array portal
The :class:`vtkm::cont::ArrayHandle` class does not provide direct access to the data in the array.
This is because the control and access to arrays is often in different parts of the code in |VTKm|.
To get direct access to the data, you must retrieve an *array portal* to the array.
There is no single :class:`ArrayPortal` class declared, but the structure of all such classes has the following members.
.. cpp:class:: ArrayPortal
A class that provides access to the data in an array.
Each :class:`vtkm::cont::ArrayHandle` type defines its own array portal.
.. cpp:type:: T ArrayPortal::ValueType
The type for each item in the array.
.. cpp:function:: vtkm::Id ArrayPortal::GetNumberOfValues() const
Returns the number of entries in the array.
.. cpp:function:: ArrayPortal::ValueType ArrayPortal::Get(vtkm::Id index) const
Returns the value in the array at the given index.
.. cpp:function:: void ArrayPortal::Set(vtkm::Id index, const ArrayPortal::ValueType& value) const
Sets the entry at the given index of the array to the provided value.
A :class:`vtkm::cont::ArrayHandle` provides its own array portal of an internal type.
The correct type for the array portal is :type:`vtkm::cont::ArrayHandle::ReadPortalType` for read-only access and :type:`vtkm::cont::ArrayHandle::WritePortalType` for read-write access.
:class:`vtkm::cont::ArrayHandle` provides the methods :func:`vtkm::cont::ArrayHandle::ReadPortal` and :func:`vtkm::cont::ArrayHandle::WritePortal` to get the associated array portal objects to access the data in the control environment.
These methods also have the side effect of refreshing the control environment copy of the data as if you called :func:`vtkm::cont::ArrayHandle::SyncControlArray`.
Be aware that calling :func:`vtkm::cont::ArrayHandle::WritePortal` will invalidate any copy in the execution environment, meaning that any subsequent use will cause the data to be copied back again.
.. load-example:: ArrayHandlePopulate
:file: GuideExampleArrayHandle.cxx
:caption: Populating a :class:`vtkm::cont::ArrayHandle`.
.. didyouknow::
Most operations on arrays in |VTKm| should really be done in the execution environment.
Keep in mind that whenever doing an operation using a control array portal, that operation will likely be slow for large arrays.
However, some operations, like performing file I/O, make sense in the control environment.
.. commonerrors::
The portal returned from :func:`vtkm::cont::ArrayHandle::ReadPortal` or :func:`vtkm::cont::ArrayHandle::WritePortal` is only good as long as the data in the :class:`vtkm::cont::ArrayHandle` are not moved or reallocated.
For example, if you call :func:`vtkm::cont::ArrayHandle::Allocate`, any previously created array portals are likely to become invalid, and using them will result in undefined behavior.
Thus, you should keep portals only as long as is necessary to complete an operation.
|VTKm| provides a pair of functions, :func:`vtkm::cont::ArrayPortalToIteratorBegin` and :func:`vtkm::cont::ArrayPortalToIterationEnd`, to convert an :class:`ArrayPortal` into a C++ STL iterator.
This makes it easy to operate on |VTKm| arrays like other C++ STL containers, but keep in mind this will all be done in serial on the host processor.
.. doxygenfunction:: vtkm::cont::ArrayPortalToIteratorBegin
.. doxygenfunction:: vtkm::cont::ArrayPortalToIteratorEnd
.. load-example:: ControlPortals
:file: GuideExampleArrayHandle.cxx
:caption: Using portals as C++ iterators.
----------------------------------------
The Hidden Second Template Parameter
----------------------------------------

@ -492,9 +492,13 @@ The :class:`vtkm::cont::Field` class also has several convenience methods for qu
.. doxygenfunction:: vtkm::cont::Field::GetRange() const
Details on how to get data from a :class:`vtkm::cont::ArrayHandle` them is given in Chapter \ref{chap:AccessingAllocatingArrays}.
.. todo:: Fix above reference to array handle chapter.
.. didyouknow::
The :class:`vtkm::cont::Field` class does not give direct access to the data in the field.
This is in part because the field can hold any number of data types and in part because data access is more efficient in filters and other features that run in parallel.
The :func:`vtkm::cont::Field::PrintSummary` function can be used to get some summary information for debugging.
To get direct access to the data, you will first have to get a :class:`vtkm::cont::UnknownArrayHandle` from :func:`vtkm::cont::Field::Data`.
The :class:`vtkm::cont::UnknownArrayHandle` then has to be converted to a :class:`vtkm::cont::ArrayHandle` of the proper type as described in :chapref:`unknown-array-handle:Unknown Array Handles`.
Once the proper :class:`vtkm::cont::ArrayHandle` is retrieved, the data can finally be accessed through an array portal as described in :secref:`basic-array-handles:Array Portals`.
------------------------------

@ -308,7 +308,9 @@ public:
using StorageTag = StorageTag_;
using StorageType = vtkm::cont::internal::Storage<ValueType, StorageTag>;
/// The type of portal used when accessing data in a read-only mode.
using ReadPortalType = typename StorageType::ReadPortalType;
/// The type of portal used when accessing data in a read-write mode.
using WritePortalType = typename StorageType::WritePortalType;
/// Constructs an empty ArrayHandle.

@ -66,10 +66,10 @@ CoordinateSystem::GetDataAsMultiplexer() const
}
VTKM_CONT
void CoordinateSystem::PrintSummary(std::ostream& out) const
void CoordinateSystem::PrintSummary(std::ostream& out, bool full) const
{
out << " Coordinate System ";
this->Superclass::PrintSummary(out);
this->Superclass::PrintSummary(out, full);
}
template VTKM_CONT_EXPORT CoordinateSystem::CoordinateSystem(

@ -132,10 +132,8 @@ public:
return vtkm::Bounds(ranges[0], ranges[1], ranges[2]);
}
void PrintSummary(std::ostream& out) const override;
void PrintSummary(std::ostream& out, bool full = false) const override;
/// Releases any resources being used in the execution environment (that are
/// not being shared by the control environment).
VTKM_CONT void ReleaseResourcesExecution() override
{
this->Superclass::ReleaseResourcesExecution();

@ -79,7 +79,7 @@ Field& Field::operator=(vtkm::cont::Field&& src) noexcept
VTKM_CONT
void Field::PrintSummary(std::ostream& out) const
void Field::PrintSummary(std::ostream& out, bool full) const
{
out << " " << this->Name;
out << " assoc= ";
@ -104,7 +104,7 @@ void Field::PrintSummary(std::ostream& out) const
out << "Global ";
break;
}
this->Data.PrintSummary(out);
this->Data.PrintSummary(out, full);
}
VTKM_CONT

@ -92,9 +92,11 @@ public:
VTKM_CONT
Field() = default;
/// Create a field with the given name, association, and data.
VTKM_CONT
Field(std::string name, Association association, const vtkm::cont::UnknownArrayHandle& data);
/// Create a field with the given name, association, and data.
template <typename T, typename Storage>
VTKM_CONT Field(std::string name,
Association association,
@ -111,27 +113,39 @@ public:
VTKM_CONT Field& operator=(const vtkm::cont::Field& src);
VTKM_CONT Field& operator=(vtkm::cont::Field&& src) noexcept;
/// Return true if this field is associated with cells.
VTKM_CONT bool IsCellField() const { return this->FieldAssociation == Association::Cells; }
/// Return true if this field is associated with points.
VTKM_CONT bool IsPointField() const { return this->FieldAssociation == Association::Points; }
/// Return true if this field is associated with the whole data set.
VTKM_CONT bool IsWholeDataSetField() const
{
return this->FieldAssociation == Association::WholeDataSet;
}
/// Return true if this field is associated with partitions in a partitioned data set.
VTKM_CONT bool IsPartitionsField() const
{
return this->FieldAssociation == Association::Partitions;
}
/// Return true if this field is global.
/// A global field is applied to a `vtkm::cont::PartitionedDataSet` to refer to data that
/// applies across an entire collection of data.
VTKM_CONT bool IsGlobalField() const { return this->FieldAssociation == Association::Global; }
/// Returns true if the array of the field has a value type that matches something in
/// `VTKM_FIELD_TYPE_LIST` and a storage that matches something in `VTKM_FIELD_STORAGE_LIST`.
VTKM_CONT bool IsSupportedType() const;
/// Return the number of values in the field array.
VTKM_CONT vtkm::Id GetNumberOfValues() const { return this->Data.GetNumberOfValues(); }
/// Return the name of the field.
VTKM_CONT const std::string& GetName() const { return this->Name; }
/// Return the association of the field.
VTKM_CONT Association GetAssociation() const { return this->FieldAssociation; }
/// Get the array of the data for the field.
const vtkm::cont::UnknownArrayHandle& GetData() const;
/// Get the array of the data for the field.
vtkm::cont::UnknownArrayHandle& GetData();
/// @brief Returns the range of each component in the field array.
@ -144,6 +158,11 @@ public:
/// the range.
VTKM_CONT const vtkm::cont::ArrayHandle<vtkm::Range>& GetRange() const;
/// @brief Returns the range of each component in the field array.
///
/// A C array of `vtkm::Range` objects is passed in as a place to store the result.
/// It is imperative that the array be allocated to be large enough to hold an entry
/// for each component.
VTKM_CONT void GetRange(vtkm::Range* range) const;
/// \brief Get the data as an array with `vtkm::FloatDefault` components.
@ -204,9 +223,11 @@ public:
this->SetData(vtkm::cont::UnknownArrayHandle(newdata));
}
/// Print a summary of the data in the field.
VTKM_CONT
virtual void PrintSummary(std::ostream& out) const;
virtual void PrintSummary(std::ostream& out, bool full = false) const;
/// Remove the data from the device memory (but preserve the data on the host).
VTKM_CONT
virtual void ReleaseResourcesExecution()
{