From d67d27e0fc98af8aead287311ffd5df70a14fcc1 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 9 Jul 2024 15:21:44 -0400 Subject: [PATCH] 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. --- docs/users-guide/advanced-types.rst | 5 +- docs/users-guide/basic-array-handles.rst | 104 +++++++++++++++++++++-- docs/users-guide/dataset.rst | 10 ++- vtkm/cont/ArrayHandle.h | 2 + vtkm/cont/CoordinateSystem.cxx | 4 +- vtkm/cont/CoordinateSystem.h | 4 +- vtkm/cont/Field.cxx | 4 +- vtkm/cont/Field.h | 23 ++++- 8 files changed, 137 insertions(+), 19 deletions(-) diff --git a/docs/users-guide/advanced-types.rst b/docs/users-guide/advanced-types.rst index c4468f0a2..666a1471f 100644 --- a/docs/users-guide/advanced-types.rst +++ b/docs/users-guide/advanced-types.rst @@ -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: diff --git a/docs/users-guide/basic-array-handles.rst b/docs/users-guide/basic-array-handles.rst index 35f6638b6..c164df35a 100644 --- a/docs/users-guide/basic-array-handles.rst +++ b/docs/users-guide/basic-array-handles.rst @@ -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 ---------------------------------------- diff --git a/docs/users-guide/dataset.rst b/docs/users-guide/dataset.rst index f767772fe..207d0f7bd 100644 --- a/docs/users-guide/dataset.rst +++ b/docs/users-guide/dataset.rst @@ -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`. ------------------------------ diff --git a/vtkm/cont/ArrayHandle.h b/vtkm/cont/ArrayHandle.h index 34f6d9a51..4277af5e4 100644 --- a/vtkm/cont/ArrayHandle.h +++ b/vtkm/cont/ArrayHandle.h @@ -308,7 +308,9 @@ public: using StorageTag = StorageTag_; using StorageType = vtkm::cont::internal::Storage; + /// 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. diff --git a/vtkm/cont/CoordinateSystem.cxx b/vtkm/cont/CoordinateSystem.cxx index b4128e296..9a32bc23e 100644 --- a/vtkm/cont/CoordinateSystem.cxx +++ b/vtkm/cont/CoordinateSystem.cxx @@ -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( diff --git a/vtkm/cont/CoordinateSystem.h b/vtkm/cont/CoordinateSystem.h index 16a41b26a..766145a1f 100644 --- a/vtkm/cont/CoordinateSystem.h +++ b/vtkm/cont/CoordinateSystem.h @@ -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(); diff --git a/vtkm/cont/Field.cxx b/vtkm/cont/Field.cxx index c54258d62..a509dff96 100644 --- a/vtkm/cont/Field.cxx +++ b/vtkm/cont/Field.cxx @@ -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 diff --git a/vtkm/cont/Field.h b/vtkm/cont/Field.h index 45990e4ba..f072d8470 100644 --- a/vtkm/cont/Field.h +++ b/vtkm/cont/Field.h @@ -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 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& 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() {