From e14ae4a4dd3a072805eacd6e407a33f65dc05dd1 Mon Sep 17 00:00:00 2001 From: Jacob Merson Date: Sun, 25 Oct 2026 18:16:40 +0000 Subject: [PATCH 1/3] Use indirection for access of DOF Data The FieldData class is intended to be a opaque type that only the field that creates it can unpack/work with. In some cases, like nimrod/fusion-io fields, we do not actually have access to any data/field coefficients explicitly, although we can evaluate those fields. This indirection provides us a way to model FieldData that we have the coefficients for, vs. those we don't. The side benefit of this approach is it gives us a convenient place to deal with packing data on device side, and for fixing the set/get full fields rather than subsets. --- src/pcms/capi/client.cpp | 19 ++-- src/pcms/coupler/CMakeLists.txt | 5 - src/pcms/coupler/coupler.hpp | 63 +++-------- src/pcms/coupler/field_communicator.hpp | 19 +--- .../coupler/field_layout_communicator.cpp | 5 + src/pcms/coupler/field_serializer.h | 10 +- src/pcms/coupler/serializer/xgc.h | 101 ------------------ src/pcms/field/data/mesh_fields.h | 27 ++--- src/pcms/field/data/mfem.cpp | 13 ++- src/pcms/field/data/mfem.hpp | 19 ++-- src/pcms/field/data/point_cloud.cpp | 21 +--- src/pcms/field/data/point_cloud.h | 17 +-- src/pcms/field/data/simple.h | 24 ++--- src/pcms/field/data/xgc.h | 57 +++++----- src/pcms/field/field.h | 74 +++++++++++-- src/pcms/field/field_data.h | 30 ++++-- src/pcms/field/function_space/lagrange.cpp | 2 +- src/pcms/field/function_space/mfem.hpp | 2 +- .../polynomial_reconstruction.cpp | 2 +- src/pcms/field/function_space/spline.cpp | 2 +- src/pcms/field/function_space/xgc.h | 2 +- test/field_test_utils.h | 28 ++--- test/test_field_evaluation.cpp | 2 +- test/test_field_serializer.cpp | 8 +- test/test_omega_h_field2_outofbounds.cpp | 12 +-- test/test_omega_h_lagrange_field.cpp | 26 +++-- test/test_point_evaluator.cpp | 48 +++++---- ...lynomial_reconstruction_function_space.cpp | 6 +- ...lynomial_reconstruction_mls_evaluation.cpp | 26 ++--- test/test_proxy_coupling.cpp | 39 ++++--- test/test_proxy_coupling_xgc_server.cpp | 11 +- ...test_proxy_coupling_xgc_server_overlap.cpp | 11 +- test/test_uniform_grid_field.cpp | 16 +-- test/xgc_n0_coupling_server.cpp | 4 +- 34 files changed, 323 insertions(+), 428 deletions(-) delete mode 100644 src/pcms/coupler/serializer/xgc.h diff --git a/src/pcms/capi/client.cpp b/src/pcms/capi/client.cpp index 5b1b6f99a..32dc52121 100644 --- a/src/pcms/capi/client.cpp +++ b/src/pcms/capi/client.cpp @@ -4,7 +4,6 @@ #include "pcms/field/function_space/xgc.h" #include "pcms/field/data/xgc.h" #include "pcms/field/layout/xgc.h" -#include "pcms/coupler/serializer/xgc.h" #include "pcms/discretization/discretization/xgc_reverse_classification.h" #include "pcms/coupler/coupler.hpp" #include "pcms/field/layout/empty.h" @@ -79,7 +78,7 @@ class EmptyFieldFactory : public FieldFactory throw pcms_error( "EmptyFieldFactory::CreateField: requires SimpleFieldData"); } - if (fd->GetDOFHolderDataHost().size() != + if (fd->GetDOFAccess()->GetOwnedValues().size() != detail::ExpectedFlatFieldDataSize(*layout_)) { throw pcms_error( "EmptyFieldFactory::CreateField: field data size does not match " @@ -130,14 +129,12 @@ ClientState::HandleVariant RegisterField( // not the arbitrary adapter name given at creation. registration.function_space.SetLayoutName(name); auto field = registration.function_space.template CreateField( - std::move(name), std::make_unique>( - registration.function_space.GetXGCLayout(), ValueBasis{}, - registration.data)); - std::unique_ptr> serializer = - std::make_unique>(registration.plane_comm, - participates); + std::move(name), + std::make_unique>( + registration.function_space.GetXGCLayout(), ValueBasis{}, + registration.data, registration.plane_comm)); return ClientState::HandleVariant{ - app.AddField(std::move(field), std::move(serializer), participates)}; + app.AddField(std::move(field), participates)}; } inline ClientState::HandleVariant RegisterField( @@ -152,10 +149,8 @@ inline ClientState::HandleVariant RegisterField( auto function_space = detail::EmptyFieldFactory{name, csys::CylindricalRZ::Create()}; auto field = function_space.CreateField(std::move(name)); - std::unique_ptr> serializer = - std::make_unique>(); return ClientState::HandleVariant{ - app.AddField(std::move(field), std::move(serializer), participates)}; + app.AddField(std::move(field), participates)}; } } // namespace pcms diff --git a/src/pcms/coupler/CMakeLists.txt b/src/pcms/coupler/CMakeLists.txt index 6a498c62b..397694120 100644 --- a/src/pcms/coupler/CMakeLists.txt +++ b/src/pcms/coupler/CMakeLists.txt @@ -17,11 +17,6 @@ set(PCMS_COUPLER_SOURCES field_exchange_planner.cpp ) -if(PCMS_ENABLE_XGC) - list(APPEND PCMS_COUPLER_HEADERS - serializer/xgc.h) -endif() - add_library(pcms_coupler ${PCMS_COUPLER_SOURCES}) set_target_properties(pcms_coupler PROPERTIES OUTPUT_NAME pcmscoupler diff --git a/src/pcms/coupler/coupler.hpp b/src/pcms/coupler/coupler.hpp index 454f0b48e..6bbad68b4 100644 --- a/src/pcms/coupler/coupler.hpp +++ b/src/pcms/coupler/coupler.hpp @@ -140,36 +140,24 @@ class ApplicationComm // Register a comm-only field, identified by its own name. The layout's // communicator is created on first use and shared by any later - // field/function whose layout has the same name. + // field/function whose layout has the same name. The field's data must + // provide DOFAccess: values are exchanged through it, and receiving writes + // them back through it on every rank, including ranks that do not + // participate, so backends that keep replicas can synchronize them. template FieldHandle AddField(Field&& field, bool participates = true); - - template - FieldHandle AddField(Field&& field, - std::unique_ptr> serializer, - bool participates = true); - - // Register a transferable field: like AddField but retains the function space - // (via the stored Function) and returns a FunctionHandle usable in transfers. - template - FieldHandle AddField(std::string name, Field&& field, - std::unique_ptr> serializer, - bool participates = true); // Registers a reference to user-owned data. The Application does not take // ownership; it uses the registered reference for subsequent send and receive // operations. template DataHandle AddData(std::string name, Rank1View data, MPI_Comm mpi_comm); + // Register a transferable field: like AddField but retains the function space + // (via the stored Function) and returns a FunctionHandle usable in transfers. template FunctionHandle AddFunction(Function&& function, bool participates = true); - template - FunctionHandle AddFunction(Function&& function, - std::unique_ptr> serializer, - bool participates = true); - void SendField(const std::string& name, redev::Mode mode = redev::Mode::Synchronous) { @@ -269,9 +257,7 @@ class ApplicationComm const FieldLayout& layout, bool participates); template - void RegisterFieldCommunicator(Field& field_obj, - std::unique_ptr> serializer, - bool participates); + void RegisterFieldCommunicator(Field& field_obj, bool participates); MPI_Comm mpi_comm_; redev::Redev& redev_; @@ -416,16 +402,19 @@ pcms::Field& pcms::ApplicationComm::GetField(const std::string& name) } template -void pcms::ApplicationComm::RegisterFieldCommunicator( - Field& field_obj, std::unique_ptr> serializer, - bool participates) +void pcms::ApplicationComm::RegisterFieldCommunicator(Field& field_obj, + bool participates) { const std::string& name = field_obj.GetName(); + if (field_obj.GetData().GetDOFAccess() == nullptr) { + throw pcms_error("Application: field '" + name + + "' cannot be exchanged: its values are not accessible " + "through PCMS (the field data provides no DOFAccess)"); + } FieldLayoutCommunicator& layout_communicator = GetOrCreateLayoutCommunicator(field_obj.GetLayout(), participates); FieldCommunicator2Ptr field_communicator = - std::make_unique>(name, layout_communicator, field_obj, - std::move(serializer)); + std::make_unique>(name, layout_communicator, field_obj); auto [it, inserted] = field_communicators_.emplace(name, std::move(field_communicator)); if (!inserted) { @@ -436,15 +425,6 @@ void pcms::ApplicationComm::RegisterFieldCommunicator( template pcms::FieldHandle pcms::ApplicationComm::AddField(Field&& field, bool participates) -{ - return AddField(std::move(field), std::make_unique>(), - participates); -} - -template -pcms::FieldHandle pcms::ApplicationComm::AddField( - Field&& field, std::unique_ptr> serializer, - bool participates) { PCMS_FUNCTION_TIMER; std::string name = field.GetName(); @@ -457,7 +437,7 @@ pcms::FieldHandle pcms::ApplicationComm::AddField( } try { RegisterFieldCommunicator(std::get>(field_it->second), - std::move(serializer), participates); + participates); } catch (...) { fields_.erase(field_it); throw; @@ -468,15 +448,6 @@ pcms::FieldHandle pcms::ApplicationComm::AddField( template pcms::FunctionHandle pcms::ApplicationComm::AddFunction( Function&& function, bool participates) -{ - return AddFunction(std::move(function), - std::make_unique>(), participates); -} - -template -pcms::FunctionHandle pcms::ApplicationComm::AddFunction( - Function&& function, std::unique_ptr> serializer, - bool participates) { PCMS_FUNCTION_TIMER; std::string name = function.GetName(); @@ -490,7 +461,7 @@ pcms::FunctionHandle pcms::ApplicationComm::AddFunction( } try { RegisterFieldCommunicator(std::get>(fn_it->second), - std::move(serializer), participates); + participates); } catch (...) { functions_.erase(fn_it); throw; diff --git a/src/pcms/coupler/field_communicator.hpp b/src/pcms/coupler/field_communicator.hpp index 2ff250b5e..8f95e4216 100644 --- a/src/pcms/coupler/field_communicator.hpp +++ b/src/pcms/coupler/field_communicator.hpp @@ -21,18 +21,7 @@ class FieldCommunicator public: FieldCommunicator(const std::string& name, FieldLayoutCommunicator& layout_comm, Field& field) - : FieldCommunicator(name, layout_comm, field, - std::make_unique>()) - { - } - - FieldCommunicator(const std::string& name, - FieldLayoutCommunicator& layout_comm, Field& field, - std::unique_ptr> serializer) - : comm_buffer_{}, - layout_comm_(layout_comm), - field_(field), - serializer_(std::move(serializer)) + : comm_buffer_{}, layout_comm_(layout_comm), field_(field) { PCMS_ALWAYS_ASSERT(layout_comm.GetLayout().GetName() == field.GetLayout().GetName()); @@ -51,7 +40,7 @@ class FieldCommunicator PCMS_FUNCTION_TIMER; PCMS_ALWAYS_ASSERT(layout_comm_.GetChannel().InSendCommunicationPhase()); auto buffer = make_array_view(comm_buffer_); - serializer_->Serialize(field_, buffer, layout_comm_.GetPermutationArray()); + serializer_.Serialize(field_, buffer, layout_comm_.GetPermutationArray()); comm_.Send(buffer.data_handle(), mode); } @@ -63,7 +52,7 @@ class FieldCommunicator // mode because we make an immediate call to deserialize after a call to // receive. auto data = comm_.Recv(redev::Mode::Synchronous); - serializer_->Deserialize(field_, make_const_array_view(data), + serializer_.Deserialize(field_, make_const_array_view(data), layout_comm_.GetPermutationArray()); } @@ -72,7 +61,7 @@ class FieldCommunicator redev::BidirectionalComm comm_; FieldLayoutCommunicator& layout_comm_; Field& field_; - std::unique_ptr> serializer_; + FieldSerializer serializer_; }; template diff --git a/src/pcms/coupler/field_layout_communicator.cpp b/src/pcms/coupler/field_layout_communicator.cpp index 00dabfb72..9bf9fe73f 100644 --- a/src/pcms/coupler/field_layout_communicator.cpp +++ b/src/pcms/coupler/field_layout_communicator.cpp @@ -1,4 +1,5 @@ #include "pcms/coupler/field_layout_communicator.h" +#include "pcms/field/gid_permutation.hpp" namespace pcms { @@ -108,6 +109,10 @@ void FieldLayoutCommunicator::UpdateLayout() void FieldLayoutCommunicator::UpdateLayoutNull() { PCMS_FUNCTION_TIMER; + // This rank exchanges nothing, but it still unpacks on receive (backends + // that keep replicas synchronize them there), so every holder is unmapped. + plan_.permutation = + MakeUnmappedPermutation(static_cast(layout_.GetNumOwnedDofHolder())); if (redev_.GetProcessType() == redev::ProcessType::Client) { channel_.BeginSendCommunicationPhase(); channel_.EndSendCommunicationPhase(); diff --git a/src/pcms/coupler/field_serializer.h b/src/pcms/coupler/field_serializer.h index 15636248b..27ef6dafc 100644 --- a/src/pcms/coupler/field_serializer.h +++ b/src/pcms/coupler/field_serializer.h @@ -10,11 +10,15 @@ namespace pcms { +// Packs a field's owned values into, and unpacks them from, an exchange +// buffer, through the field's DOFAccess. Any field with DOFAccess is +// exchanged this way. Backends that need extra work on receive (e.g., +// synchronizing replicas) do it in DOFAccess::SetOwnedValues. template class FieldSerializer { public: - virtual int Serialize(const Field& field, + int Serialize(const Field& field, Rank1View buffer, Rank1View permutation) const { @@ -33,7 +37,7 @@ class FieldSerializer return static_cast(buffer.size()); } - virtual void Deserialize( + void Deserialize( Field& field, Rank1View buffer, Rank1View permutation) const { @@ -62,8 +66,6 @@ class FieldSerializer field.SetDOFHolderDataUncheckedHost( Rank2View(sorted.data(), num_dof, num_comp)); } - - virtual ~FieldSerializer() noexcept = default; }; } // namespace pcms diff --git a/src/pcms/coupler/serializer/xgc.h b/src/pcms/coupler/serializer/xgc.h deleted file mode 100644 index 87bd3b63f..000000000 --- a/src/pcms/coupler/serializer/xgc.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef PCMS_XGC_FIELD_SERIALIZER_H -#define PCMS_XGC_FIELD_SERIALIZER_H - -#include "pcms/field/data/xgc.h" -#include "pcms/coupler/field_serializer.h" -#include "pcms/utility/assert.h" -#include "pcms/utility/mpi_type.h" -#include - -namespace pcms -{ - -template -class XGCFieldSerializer : public FieldSerializer -{ -public: - explicit XGCFieldSerializer(MPI_Comm plane_comm, - bool rank_participates = true) - : plane_comm_(plane_comm), rank_participates_(rank_participates) - { - } - - int Serialize(const Field& field, Rank1View buffer, - Rank1View permutation) const override - { - if (!rank_participates_) { - return 0; - } - - auto const* xgc_field = - dynamic_cast*>(&field.GetData()); - if (!xgc_field) { - throw pcms_error("XGCFieldSerializer::Serialize: incompatible FieldData"); - } - - auto data = xgc_field->GetDOFHolderDataHost(); - if (buffer.size() > 0) { - const LO num_dof = static_cast(data.extent(0)); - const LO num_comp = static_cast(data.extent(1)); - for (LO i = 0; i < num_dof; ++i) { - // A negative permutation entry marks a holder outside the exchange - // (non-owned, or owned but outside the overlap region); it has no slot. - if (permutation[i] >= 0) { - for (LO c = 0; c < num_comp; ++c) { - buffer[permutation[i] * num_comp + c] = data(i, c); - } - } - } - } - return static_cast(buffer.size()); - } - - void Deserialize( - Field& field, Rank1View buffer, - Rank1View permutation) const override - { - auto* xgc_field = dynamic_cast*>(&field.GetData()); - if (!xgc_field) { - throw pcms_error( - "XGCFieldSerializer::Deserialize: incompatible FieldData"); - } - - auto current = xgc_field->GetDOFHolderDataHost(); - const auto& layout = field.GetLayout(); - const LO num_dof = static_cast(current.extent(0)); - const LO num_comp = static_cast(current.extent(1)); - std::vector full_data(current.size()); - for (LO i = 0; i < num_dof; ++i) { - for (LO c = 0; c < num_comp; ++c) { - full_data[i * num_comp + c] = current(i, c); - } - } - if (rank_participates_) { - for (LO i = 0; i < num_dof; ++i) { - // A negative permutation entry marks a holder outside the exchange - // (owned but outside the overlap region); no data was received for it, - // so it keeps its current value (pre-filled above). - if (permutation[i] >= 0) { - for (LO c = 0; c < num_comp; ++c) { - full_data[i * num_comp + c] = buffer[permutation[i] * num_comp + c]; - } - } - } - } - - MPI_Bcast(full_data.data(), static_cast(full_data.size()), - pcms::GetMPIType(T{}), 0, plane_comm_); - - xgc_field->SetDOFHolderDataHost(Rank2View( - full_data.data(), layout.GetNumOwnedDofHolder(), - layout.GetNumComponents())); - } - -private: - MPI_Comm plane_comm_; - bool rank_participates_; -}; - -} // namespace pcms - -#endif // PCMS_XGC_FIELD_SERIALIZER_H diff --git a/src/pcms/field/data/mesh_fields.h b/src/pcms/field/data/mesh_fields.h index 3c7bfc2c3..0a60b59af 100644 --- a/src/pcms/field/data/mesh_fields.h +++ b/src/pcms/field/data/mesh_fields.h @@ -41,7 +41,9 @@ struct FlattenFunctor } // namespace template -class MeshFieldsFieldData : public FieldData +class MeshFieldsFieldData + : public FieldData + , public DOFAccess { public: MeshFieldsFieldData(std::shared_ptr layout, @@ -49,9 +51,6 @@ class MeshFieldsFieldData : public FieldData : layout_(std::move(layout)), basis_(std::move(basis)), mesh_field_(MakeMeshFieldBackend(*layout_)), - host_data_("meshfields_field_data", - static_cast(layout_->GetNumOwnedDofHolder()), - static_cast(layout_->GetNumComponents())), device_data_("meshfields_field_data_device", static_cast(layout_->GetNumOwnedDofHolder()), static_cast(layout_->GetNumComponents())) @@ -68,27 +67,18 @@ class MeshFieldsFieldData : public FieldData } const ValueBasis& GetValueBasis() const override { return basis_; } - Rank2View GetDOFHolderDataHost() const override - { - DeepCopyMismatchLayouts(host_data_, device_data_); - return MakeConstRank2View(host_data_); - } - - void SetDOFHolderDataHost(Rank2View values) override - { - CopyHostRank2ViewToDeviceView(device_data_, values); - SyncBackend(GetDOFHolderData()); - } + const DOFAccess* GetDOFAccess() const noexcept override { return this; } + DOFAccess* GetDOFAccess() noexcept override { return this; } - Rank2View GetDOFHolderData() const override + Rank2View GetOwnedValues() const override { return MakeConstRank2View(device_data_); } - void SetDOFHolderData(Rank2View values) override + void SetOwnedValues(Rank2View values) override { CopyDeviceRank2ViewToDeviceView(device_data_, values); - SyncBackend(GetDOFHolderData()); + SyncBackend(GetOwnedValues()); } std::shared_ptr> GetMeshFieldBackend() const @@ -130,7 +120,6 @@ class MeshFieldsFieldData : public FieldData std::shared_ptr layout_; ValueBasis basis_; std::shared_ptr> mesh_field_; - mutable Kokkos::View host_data_; Kokkos::View device_data_; }; diff --git a/src/pcms/field/data/mfem.cpp b/src/pcms/field/data/mfem.cpp index 86139e254..00405bad9 100644 --- a/src/pcms/field/data/mfem.cpp +++ b/src/pcms/field/data/mfem.cpp @@ -32,8 +32,7 @@ const ValueBasis& MFEMVertexFieldData::GetValueBasis() const return basis_; } -Rank2View -MFEMVertexFieldData::GetDOFHolderDataHost() const +Rank2View MFEMVertexFieldData::GatherHost() const { PCMS_FUNCTION_TIMER; @@ -54,7 +53,7 @@ MFEMVertexFieldData::GetDOFHolderDataHost() const return MakeConstRank2View(host_data_); } -void MFEMVertexFieldData::SetDOFHolderDataHost( +void MFEMVertexFieldData::ScatterHost( Rank2View data) { PCMS_FUNCTION_TIMER; @@ -79,20 +78,20 @@ void MFEMVertexFieldData::SetDOFHolderDataHost( gf_.SetFromTrueDofs(true_values); } -Rank2View MFEMVertexFieldData::GetDOFHolderData() +Rank2View MFEMVertexFieldData::GetOwnedValues() const { - GetDOFHolderDataHost(); + GatherHost(); DeepCopyMismatchLayouts(device_data_, host_data_); return MakeConstRank2View(device_data_); } -void MFEMVertexFieldData::SetDOFHolderData( +void MFEMVertexFieldData::SetOwnedValues( Rank2View data) { CopyDeviceRank2ViewToDeviceView(device_data_, data); DeepCopyMismatchLayouts(host_data_, device_data_); - SetDOFHolderDataHost(MakeConstRank2View(host_data_)); + ScatterHost(MakeConstRank2View(host_data_)); } } // namespace pcms diff --git a/src/pcms/field/data/mfem.hpp b/src/pcms/field/data/mfem.hpp index 8ff3e0294..392f038fc 100644 --- a/src/pcms/field/data/mfem.hpp +++ b/src/pcms/field/data/mfem.hpp @@ -11,7 +11,9 @@ namespace pcms { // FieldData backend for an MFEM order-1 H1 (vertex) scalar field. -class MFEMVertexFieldData : public FieldData +class MFEMVertexFieldData + : public FieldData + , public DOFAccess { public: MFEMVertexFieldData(mfem::ParFiniteElementSpace& pfes, @@ -20,14 +22,19 @@ class MFEMVertexFieldData : public FieldData FieldValueType GetValueType() const override; const ValueBasis& GetValueBasis() const override; - Rank2View GetDOFHolderDataHost() const override; - void SetDOFHolderDataHost( - Rank2View data) override; + const DOFAccess* GetDOFAccess() const noexcept override + { + return this; + } + DOFAccess* GetDOFAccess() noexcept override { return this; } - Rank2View GetDOFHolderData() const override; - void SetDOFHolderData(Rank2View data) override; + Rank2View GetOwnedValues() const override; + void SetOwnedValues(Rank2View data) override; private: + Rank2View GatherHost() const; + void ScatterHost(Rank2View data); + mfem::ParFiniteElementSpace& pfes_; mfem::ParGridFunction& gf_; ValueBasis basis_; diff --git a/src/pcms/field/data/point_cloud.cpp b/src/pcms/field/data/point_cloud.cpp index 0ebf3cc5e..d50e512d3 100644 --- a/src/pcms/field/data/point_cloud.cpp +++ b/src/pcms/field/data/point_cloud.cpp @@ -11,9 +11,7 @@ PointCloud::PointCloud(std::shared_ptr layout) : layout_(std::move(layout)), basis_{}, device_data_("", layout_->GetDOFHolderCoordinates().GetValues().extent(0), - static_cast(layout_->GetNumComponents())), - data_host_("", layout_->GetDOFHolderCoordinates().GetValues().extent(0), - static_cast(layout_->GetNumComponents())) + static_cast(layout_->GetNumComponents())) { } @@ -27,25 +25,12 @@ const ValueBasis& PointCloud::GetValueBasis() const return basis_; } -Rank2View PointCloud::GetDOFHolderDataHost() const -{ - DeepCopyMismatchLayouts(data_host_, device_data_); - return MakeConstRank2View(data_host_); -} - -void PointCloud::SetDOFHolderDataHost( - Rank2View data) -{ - PCMS_FUNCTION_TIMER; - CopyHostRank2ViewToDeviceView(device_data_, data); -} - -Rank2View PointCloud::GetDOFHolderData() const +Rank2View PointCloud::GetOwnedValues() const { return MakeConstRank2View(device_data_); } -void PointCloud::SetDOFHolderData(Rank2View data) +void PointCloud::SetOwnedValues(Rank2View data) { PCMS_FUNCTION_TIMER; CopyDeviceRank2ViewToDeviceView(device_data_, data); diff --git a/src/pcms/field/data/point_cloud.h b/src/pcms/field/data/point_cloud.h index c31e42234..93170b22d 100644 --- a/src/pcms/field/data/point_cloud.h +++ b/src/pcms/field/data/point_cloud.h @@ -9,7 +9,9 @@ namespace pcms { -class PointCloud : public FieldData +class PointCloud + : public FieldData + , public DOFAccess { public: PointCloud(std::shared_ptr layout); @@ -17,18 +19,19 @@ class PointCloud : public FieldData FieldValueType GetValueType() const override; const ValueBasis& GetValueBasis() const override; - Rank2View GetDOFHolderDataHost() const override; - void SetDOFHolderDataHost( - Rank2View data) override; + const DOFAccess* GetDOFAccess() const noexcept override + { + return this; + } + DOFAccess* GetDOFAccess() noexcept override { return this; } - Rank2View GetDOFHolderData() const override; - void SetDOFHolderData(Rank2View data) override; + Rank2View GetOwnedValues() const override; + void SetOwnedValues(Rank2View data) override; private: std::shared_ptr layout_; ValueBasis basis_; Kokkos::View device_data_; - mutable Kokkos::View data_host_; }; } // namespace pcms diff --git a/src/pcms/field/data/simple.h b/src/pcms/field/data/simple.h index 0ac8b0b2a..facfd032e 100644 --- a/src/pcms/field/data/simple.h +++ b/src/pcms/field/data/simple.h @@ -19,15 +19,14 @@ namespace pcms // Ownership of the layout is shared — the layout is typically held by the // factory that created this field data object. template -class SimpleFieldData : public FieldData +class SimpleFieldData + : public FieldData + , public DOFAccess { public: SimpleFieldData(std::shared_ptr layout, ValueBasis basis) : layout_(std::move(layout)), basis_(std::move(basis)), - host_data_("simple_field_data", - static_cast(layout_->GetNumOwnedDofHolder()), - static_cast(layout_->GetNumComponents())), device_data_("simple_field_data_device", static_cast(layout_->GetNumOwnedDofHolder()), static_cast(layout_->GetNumComponents())) @@ -40,23 +39,15 @@ class SimpleFieldData : public FieldData } const ValueBasis& GetValueBasis() const override { return basis_; } - Rank2View GetDOFHolderDataHost() const override - { - DeepCopyMismatchLayouts(host_data_, device_data_); - return MakeConstRank2View(host_data_); - } - - void SetDOFHolderDataHost(Rank2View values) override - { - CopyHostRank2ViewToDeviceView(device_data_, values); - } + const DOFAccess* GetDOFAccess() const noexcept override { return this; } + DOFAccess* GetDOFAccess() noexcept override { return this; } - Rank2View GetDOFHolderData() const override + Rank2View GetOwnedValues() const override { return MakeConstRank2View(device_data_); } - void SetDOFHolderData(Rank2View values) override + void SetOwnedValues(Rank2View values) override { CopyDeviceRank2ViewToDeviceView(device_data_, values); } @@ -64,7 +55,6 @@ class SimpleFieldData : public FieldData private: std::shared_ptr layout_; ValueBasis basis_; - mutable Kokkos::View host_data_; Kokkos::View device_data_; }; diff --git a/src/pcms/field/data/xgc.h b/src/pcms/field/data/xgc.h index dd3fc9861..9caf89d74 100644 --- a/src/pcms/field/data/xgc.h +++ b/src/pcms/field/data/xgc.h @@ -4,21 +4,34 @@ #include "pcms/field/layout/xgc.h" #include "pcms/field/field_data.h" #include "pcms/utility/assert.h" +#include "pcms/utility/mpi_type.h" #include +#include #include namespace pcms { template -class XGCFieldData : public FieldData +class XGCFieldData + : public FieldData + , public DOFAccess { public: // Externally-managed storage: the caller owns the underlying data buffer. // The view must remain valid for the lifetime of this object. + // + // XGC keeps a full copy of a plane's data on every rank of the plane, and + // only the plane's root rank (rank 0 of plane_comm) exchanges it. When + // plane_comm is given, SetOwnedValues broadcasts the root's values to the + // other ranks of the plane, so every rank of plane_comm must call it. XGCFieldData(std::shared_ptr layout, ValueBasis basis, - Rank1View data) - : layout_(std::move(layout)), basis_(std::move(basis)), data_(data) + Rank1View data, + MPI_Comm plane_comm = MPI_COMM_NULL) + : layout_(std::move(layout)), + basis_(std::move(basis)), + data_(data), + plane_comm_(plane_comm) { PCMS_ALWAYS_ASSERT(layout_ != nullptr); PCMS_ALWAYS_ASSERT(static_cast(data_.size()) == @@ -43,36 +56,21 @@ class XGCFieldData : public FieldData } const ValueBasis& GetValueBasis() const override { return basis_; } - Rank2View GetDOFHolderDataHost() const override - { - const auto nc = layout_->GetNumComponents(); - return Rank2View( - data_.data_handle(), static_cast(data_.size()) / nc, nc); - } + const DOFAccess* GetDOFAccess() const noexcept override { return this; } + DOFAccess* GetDOFAccess() noexcept override { return this; } - void SetDOFHolderDataHost(Rank2View values) override - { - if (values.size() != data_.size()) { - throw pcms_error("XGCFieldData::SetDOFHolderDataHost: size mismatch"); - } - const auto num_dof = values.extent(0); - const auto num_comp = values.extent(1); - // Explicit node-major serialization into the adapter buffer. - for (size_t i = 0; i < num_dof; ++i) { - for (size_t c = 0; c < num_comp; ++c) { - data_(i * num_comp + c) = values(i, c); - } - } - } - - Rank2View GetDOFHolderData() const override + Rank2View GetOwnedValues() const override { EnsureDeviceStaging(); - CopyHostRank2ViewToDeviceView(device_data_, GetDOFHolderDataHost()); + const auto nc = layout_->GetNumComponents(); + CopyHostRank2ViewToDeviceView( + device_data_, + Rank2View( + data_.data_handle(), static_cast(data_.size()) / nc, nc)); return MakeConstRank2View(device_data_); } - void SetDOFHolderData(Rank2View values) override + void SetOwnedValues(Rank2View values) override { EnsureDeviceStaging(); CopyDeviceRank2ViewToDeviceView(device_data_, values); @@ -86,6 +84,10 @@ class XGCFieldData : public FieldData data_(i * nc + c) = host_mirror(i, c); } } + if (plane_comm_ != MPI_COMM_NULL) { + MPI_Bcast(data_.data_handle(), static_cast(data_.size()), + GetMPIType(T{}), 0, plane_comm_); + } } private: @@ -108,6 +110,7 @@ class XGCFieldData : public FieldData Rank1View data_; // Shaped device staging, allocated on first device access. mutable Kokkos::View device_data_; + MPI_Comm plane_comm_ = MPI_COMM_NULL; }; } // namespace pcms diff --git a/src/pcms/field/field.h b/src/pcms/field/field.h index 659754a71..5c67f49f3 100644 --- a/src/pcms/field/field.h +++ b/src/pcms/field/field.h @@ -8,6 +8,7 @@ #include "pcms/utility/types.h" #include #include +#include #include #include @@ -40,44 +41,51 @@ class Field [[nodiscard]] const std::string& GetName() const noexcept { return name_; } + // The DOF-holder accessors below read and write the values of the DOF + // holders this rank owns through the data's DOFAccess, and throw if the data + // has none (e.g. a field of an evaluation-only source). Writes synchronize + // the backend's other copies and are collective; see DOFAccess. + + // Copies to the host unless device memory is host memory. The returned view + // is valid until the next call or until the field is written or destroyed. [[nodiscard]] ValueView GetDOFHolderDataHost() const { return ValueView(data_->GetValueBasis(), - data_->GetDOFHolderDataHost()); + OwnedValuesHost()); } [[nodiscard]] ValueView GetDOFHolderData() const { - return ValueView(data_->GetValueBasis(), - data_->GetDOFHolderData()); + return ValueView( + data_->GetValueBasis(), RequireDOFAccess().GetOwnedValues()); } void SetDOFHolderDataHost(ValueView v) { CheckWriteTag(v.GetBasis()); CheckWriteExtents(v.GetValues().extent(0), v.GetValues().extent(1)); - data_->SetDOFHolderDataHost(v.GetValues()); + SetOwnedValuesFromHost(v.GetValues()); } void SetDOFHolderData(ValueView v) { CheckWriteTag(v.GetBasis()); CheckWriteExtents(v.GetValues().extent(0), v.GetValues().extent(1)); - data_->SetDOFHolderData(v.GetValues()); + RequireDOFAccess().SetOwnedValues(v.GetValues()); } // unchecked is needed for optimization in some places, but should generally // not be used unless you know what you are doing void SetDOFHolderDataUncheckedHost(Rank2View v) { - data_->SetDOFHolderDataHost(v); + SetOwnedValuesFromHost(v); } // unchecked is needed for optimization in some places, but should generally // not be used unless you know what you are doing void SetDOFHolderDataUnchecked(Rank2View v) { - data_->SetDOFHolderData(v); + RequireDOFAccess().SetOwnedValues(v); } protected: @@ -112,6 +120,55 @@ class Field } } + const DOFAccess& RequireDOFAccess() const + { + const auto* access = data_->GetDOFAccess(); + if (access == nullptr) { + throw pcms_error("Field '" + name_ + + "': its values are not accessible through PCMS (the " + "field data provides no DOFAccess)"); + } + return *access; + } + + DOFAccess& RequireDOFAccess() + { + return const_cast&>(std::as_const(*this).RequireDOFAccess()); + } + + Rank2View OwnedValuesHost() const + { + const auto values = RequireDOFAccess().GetOwnedValues(); + if constexpr (std::is_same_v) { + return values; + } else { + // Stage in a device view of the default layout so the copy to the + // host handles whatever layout the backend's view carries. + Kokkos::View staged( + Kokkos::view_alloc(Kokkos::WithoutInitializing, "owned_values_staged"), + values.extent(0), values.extent(1)); + CopyDeviceRank2ViewToRank2View(MakeRank2View(staged), values); + host_values_ = Kokkos::View( + Kokkos::view_alloc(Kokkos::WithoutInitializing, "owned_values_host"), + values.extent(0), values.extent(1)); + DeepCopyMismatchLayouts(host_values_, staged); + return MakeConstRank2View(host_values_); + } + } + + void SetOwnedValuesFromHost(Rank2View values) + { + if constexpr (std::is_same_v) { + RequireDOFAccess().SetOwnedValues(values); + } else { + Kokkos::View staged( + Kokkos::view_alloc(Kokkos::WithoutInitializing, "owned_values_staged"), + values.extent(0), values.extent(1)); + CopyHostRank2ViewToDeviceView(staged, values); + RequireDOFAccess().SetOwnedValues(MakeConstRank2View(staged)); + } + } + // FieldFactory constructs Fields (via WrapField); FunctionSpace constructs // Fields the same way and moves a Field's name, layout and data out to build // a Function (via CreateFunction). Both stamp the field name. @@ -122,6 +179,9 @@ class Field std::string name_; std::shared_ptr layout_; std::unique_ptr> data_; + // Host copy backing GetDOFHolderDataHost when device memory is not host + // memory. + mutable Kokkos::View host_values_; }; // Function is a Field that additionally knows its FunctionSpace, so it can diff --git a/src/pcms/field/field_data.h b/src/pcms/field/field_data.h index 5281efe30..77b12984c 100644 --- a/src/pcms/field/field_data.h +++ b/src/pcms/field/field_data.h @@ -10,6 +10,27 @@ namespace pcms { +// DOFAccess is the optional capability of field data whose values PCMS can +// read and write directly: the values of the DOF holders this rank owns, shaped +// [owned DOF holder][component] in the layout's order. Coupler exchanges and +// transfer targets use it; evaluation never needs it. +template +class DOFAccess +{ +public: + // Valid until the field data is next written or destroyed. + virtual Rank2View GetOwnedValues() const = 0; + + // Writes the owned values, then brings any other copies the backend keeps + // (ghosts, replicas on other ranks) up to date. Collective over the ranks + // that share those copies. + virtual void SetOwnedValues(Rank2View values) = 0; + + virtual ~DOFAccess() noexcept = default; +}; + +// FieldData holds whatever a backend needs to represent a field. It is +// opaque. It should only be direcly used by the type that created it. template class FieldData { @@ -19,13 +40,8 @@ class FieldData virtual FieldValueType GetValueType() const = 0; virtual const ValueBasis& GetValueBasis() const = 0; - virtual Rank2View GetDOFHolderDataHost() const = 0; - virtual void SetDOFHolderDataHost( - Rank2View values) = 0; - - virtual Rank2View GetDOFHolderData() const = 0; - virtual void SetDOFHolderData( - Rank2View values) = 0; + virtual const DOFAccess* GetDOFAccess() const noexcept { return nullptr; } + virtual DOFAccess* GetDOFAccess() noexcept { return nullptr; } virtual ~FieldData() noexcept = default; }; diff --git a/src/pcms/field/function_space/lagrange.cpp b/src/pcms/field/function_space/lagrange.cpp index d3a4cec75..faeba79ef 100644 --- a/src/pcms/field/function_space/lagrange.cpp +++ b/src/pcms/field/function_space/lagrange.cpp @@ -43,7 +43,7 @@ void ValidateLagrangeWrappedFieldData(const FieldLayout& layout, } } - if (data.GetDOFHolderDataHost().size() != + if (data.GetDOFAccess()->GetOwnedValues().size() != detail::ExpectedFlatFieldDataSize(layout)) { throw pcms_error( "LagrangeFunctionSpace::CreateField: field data size does not match " diff --git a/src/pcms/field/function_space/mfem.hpp b/src/pcms/field/function_space/mfem.hpp index bd31a98f8..a3b8820d9 100644 --- a/src/pcms/field/function_space/mfem.hpp +++ b/src/pcms/field/function_space/mfem.hpp @@ -74,7 +74,7 @@ class MFEMFieldFactory : public FieldFactory throw pcms_error( "MFEMFieldFactory::CreateField: requires MFEMVertexFieldData"); } - if ((*real)->GetDOFHolderDataHost().size() != + if ((*real)->GetDOFAccess()->GetOwnedValues().size() != detail::ExpectedFlatFieldDataSize(*layout_)) { throw pcms_error("MFEMFieldFactory::CreateField: field data size does " "not match layout"); diff --git a/src/pcms/field/function_space/polynomial_reconstruction.cpp b/src/pcms/field/function_space/polynomial_reconstruction.cpp index 9d6fa7bd8..2097ead77 100644 --- a/src/pcms/field/function_space/polynomial_reconstruction.cpp +++ b/src/pcms/field/function_space/polynomial_reconstruction.cpp @@ -111,7 +111,7 @@ FieldVariant PolynomialReconstructionFunctionSpace::CreateFieldImpl( "PolynomialReconstructionFunctionSpace::CreateField: requires " "SimpleFieldData"); } - if (fd->GetDOFHolderDataHost().size() != + if (fd->GetDOFAccess()->GetOwnedValues().size() != detail::ExpectedFlatFieldDataSize(*layout_)) { throw pcms_error( "PolynomialReconstructionFunctionSpace::CreateField: field data size " diff --git a/src/pcms/field/function_space/spline.cpp b/src/pcms/field/function_space/spline.cpp index 437150f86..f169a9f40 100644 --- a/src/pcms/field/function_space/spline.cpp +++ b/src/pcms/field/function_space/spline.cpp @@ -61,7 +61,7 @@ FieldVariant SplineFunctionSpace::CreateFieldImpl(FieldDataVariant data) const throw pcms_error( "SplineFunctionSpace::CreateField: requires SimpleFieldData"); } - if (fd->GetDOFHolderDataHost().size() != + if (fd->GetDOFAccess()->GetOwnedValues().size() != detail::ExpectedFlatFieldDataSize(*layout_)) { throw pcms_error( "SplineFunctionSpace::CreateField: field data size does not match " diff --git a/src/pcms/field/function_space/xgc.h b/src/pcms/field/function_space/xgc.h index ff5dca83c..29555da69 100644 --- a/src/pcms/field/function_space/xgc.h +++ b/src/pcms/field/function_space/xgc.h @@ -65,7 +65,7 @@ class XGCFieldFactory : public FieldFactory throw pcms_error( "XGCFieldFactory::CreateField: requires XGCFieldData"); } - if (fd->GetDOFHolderDataHost().size() != + if (fd->GetDOFAccess()->GetOwnedValues().size() != static_cast(layout_->GetFullDataSize())) { throw pcms_error( "XGCFieldFactory::CreateField: field data size does not match " diff --git a/test/field_test_utils.h b/test/field_test_utils.h index f7ef3c0de..0077cbf91 100644 --- a/test/field_test_utils.h +++ b/test/field_test_utils.h @@ -340,8 +340,7 @@ struct SetFieldFunctor // arity of func selects the spatial dimension: func(x, y) for 2D layouts, // func(x, y, z) for 3D. template -inline void SetField(const FieldLayout& layout, FieldData& field, - Func func) +inline void SetField(Field& field, Func func) { using MemorySpace = typename ExecutionSpace::memory_space; @@ -349,7 +348,7 @@ inline void SetField(const FieldLayout& layout, FieldData& field, std::is_invocable_v, "SetField requires func(x, y) or func(x, y, z)"); - auto dof_coords = layout.GetDOFHolderCoordinates().GetValues(); + auto dof_coords = field.GetLayout().GetDOFHolderCoordinates().GetValues(); int n = static_cast(dof_coords.extent(0)); Kokkos::View data_device("data_device", n); @@ -360,13 +359,9 @@ inline void SetField(const FieldLayout& layout, FieldData& field, auto data_host = Kokkos::create_mirror_view_and_copy(HostMemorySpace(), data_device); field.SetDOFHolderDataHost( - Rank2View(data_host.data(), n, 1)); -} - -template -inline void SetField(Field& field, Func func) -{ - SetField(field.GetLayout(), field.GetData(), func); + pcms::ValueView( + pcms::ValueBasis{}, + Rank2View(data_host.data(), n, 1))); } // Set multi-component DOF data by sampling func at each DOF-holder @@ -396,15 +391,10 @@ inline void SetFieldComponents(Field& field, Func func) func(coords_host(i, 0), coords_host(i, 1), out); } } - field.SetDOFHolderDataUncheckedHost( - Rank2View(data.data(), n, num_components)); -} - -template -inline void SetField(FieldData& field, const FieldLayout& layout, - Func func) -{ - SetField(layout, field, func); + field.SetDOFHolderDataHost( + pcms::ValueView( + field.GetData().GetValueBasis(), + Rank2View(data.data(), n, num_components))); } // Helper structure to hold device coordinates with proper lifetime management diff --git a/test/test_field_evaluation.cpp b/test/test_field_evaluation.cpp index 6414cb93f..85a05a96e 100644 --- a/test/test_field_evaluation.cpp +++ b/test/test_field_evaluation.cpp @@ -36,7 +36,7 @@ TEST_CASE("evaluate linear 2d omega_h_field") auto field = factory->CreateFunction(); pcms::test::SetField( - field.GetData(), *factory->GetLayout(), + field, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); pcms::test::CheckEvaluation( factory, field, pcms::test::StandardEvalCoords2D(), diff --git a/test/test_field_serializer.cpp b/test/test_field_serializer.cpp index 1d8bd15cf..8af516501 100644 --- a/test/test_field_serializer.cpp +++ b/test/test_field_serializer.cpp @@ -19,7 +19,6 @@ #endif #ifdef PCMS_ENABLE_XGC -#include "pcms/coupler/serializer/xgc.h" #include "pcms/field/data/xgc.h" #include "pcms/field/function_space/xgc.h" #endif @@ -89,7 +88,7 @@ bool InXGCOverlap(int, int id) } // namespace #ifdef PCMS_ENABLE_XGC -TEST_CASE("XGCFieldSerializer preserves inactive field entries") +TEST_CASE("Serializing an XGC field preserves inactive field entries") { static constexpr int data_size = 16; auto rc = CreateDummyReverseClassification(data_size); @@ -101,8 +100,9 @@ TEST_CASE("XGCFieldSerializer preserves inactive field entries") auto field = factory.CreateField( "", std::make_unique>( - factory.GetXGCLayout(), pcms::ValueBasis{}, pcms::make_array_view(data))); - pcms::XGCFieldSerializer serializer(MPI_COMM_SELF); + factory.GetXGCLayout(), pcms::ValueBasis{}, pcms::make_array_view(data), + MPI_COMM_SELF)); + pcms::FieldSerializer serializer; auto owned = field.GetLayout().GetOwnedHost(); std::vector permutation(data_size, -1); diff --git a/test/test_omega_h_field2_outofbounds.cpp b/test/test_omega_h_field2_outofbounds.cpp index 4a00e9a99..5a909e527 100644 --- a/test/test_omega_h_field2_outofbounds.cpp +++ b/test/test_omega_h_field2_outofbounds.cpp @@ -23,9 +23,7 @@ TEST_CASE("omega_h_field2 out of bounds FILL mode") auto factory = pcms::LagrangeFunctionSpace::FromMesh( mesh, 1, 1, pcms::csys::Cartesian::Deferred()); auto field = factory->CreateFunction(); - pcms::test::SetField( - field.GetData(), *factory->GetLayout(), - OMEGA_H_LAMBDA(Real x, Real y) { return x + y; }); + pcms::test::SetField(field, OMEGA_H_LAMBDA(Real x, Real y) { return x + y; }); Real fill_value = -999.0; @@ -55,9 +53,7 @@ TEST_CASE("uniform_grid_field out of bounds FILL mode") auto factory = pcms::LagrangeFunctionSpace::FromUniformGrid( grid, 1, pcms::csys::Cartesian::Deferred(), 1); auto field = factory->CreateFunction(); - pcms::test::SetField( - field.GetData(), *factory->GetLayout(), - OMEGA_H_LAMBDA(Real x, Real y) { return x + y; }); + pcms::test::SetField(field, OMEGA_H_LAMBDA(Real x, Real y) { return x + y; }); Real fill_value = -999.0; @@ -87,9 +83,7 @@ TEST_CASE("spline_field out of bounds FILL mode") auto factory = pcms::SplineFunctionSpace::FromUniformGrid( grid, pcms::csys::Cartesian::Deferred()); auto field = factory->CreateFunction(); - pcms::test::SetField( - field.GetData(), *factory->GetLayout(), - OMEGA_H_LAMBDA(Real x, Real y) { return x + y; }); + pcms::test::SetField(field, OMEGA_H_LAMBDA(Real x, Real y) { return x + y; }); Real fill_value = -999.0; diff --git a/test/test_omega_h_lagrange_field.cpp b/test/test_omega_h_lagrange_field.cpp index 205e8e807..76b65d0c6 100644 --- a/test/test_omega_h_lagrange_field.cpp +++ b/test/test_omega_h_lagrange_field.cpp @@ -136,9 +136,11 @@ TEST_CASE("OmegaHLagrangeField order-1: set/get DOF data round-trip") data[i] = static_cast(i); pcms::Rank2View view(data.data(), n, 1); - field.GetData().SetDOFHolderDataHost(view); + field.SetDOFHolderDataHost( + pcms::ValueView(pcms::ValueBasis{}, + view)); - auto got = pcms::FlattenToRank1View(field.GetData().GetDOFHolderDataHost()); + auto got = pcms::FlattenToRank1View(field.GetDOFHolderDataHost().GetValues()); REQUIRE(static_cast(got.size()) == n); for (int i = 0; i < n; ++i) REQUIRE(got[i] == Catch::Approx(data[i])); @@ -179,7 +181,7 @@ TEST_CASE("OmegaHLagrangeField order-1: linear function evaluation") auto field = factory->CreateFunction(); pcms::test::SetField( - field.GetData(), *factory->GetLayout(), + field, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); pcms::test::CheckEvaluation( factory, field, pcms::test::StandardEvalCoords2D(), @@ -197,7 +199,7 @@ TEST_CASE("MeshFieldsAdapter order-1: linear function evaluation (shared util)") auto field = factory->CreateFunction(); pcms::test::SetField( - field.GetData(), *factory->GetLayout(), + field, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); pcms::test::CheckEvaluation( factory, field, pcms::test::StandardEvalCoords2D(), @@ -213,7 +215,7 @@ TEST_CASE("OmegaHLagrangeField order-1: out-of-bounds FILL mode") auto field = factory->CreateFunction(); pcms::test::SetField( - field.GetData(), *factory->GetLayout(), + field, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); Real fill_value = -999.0; @@ -234,9 +236,11 @@ TEST_CASE("OmegaHLagrangeField order-0: set/get DOF data round-trip") int n = factory->GetLayout()->GetNumOwnedDofHolder(); std::vector data(n, 3.14); pcms::Rank2View view(data.data(), n, 1); - field.GetData().SetDOFHolderDataHost(view); + field.SetDOFHolderDataHost( + pcms::ValueView(pcms::ValueBasis{}, + view)); - auto got = pcms::FlattenToRank1View(field.GetData().GetDOFHolderDataHost()); + auto got = pcms::FlattenToRank1View(field.GetDOFHolderDataHost().GetValues()); REQUIRE(static_cast(got.size()) == n); for (int i = 0; i < n; ++i) REQUIRE(got[i] == Catch::Approx(3.14)); @@ -255,7 +259,9 @@ TEST_CASE("OmegaHLagrangeField order-0: constant field evaluation") std::vector data(nelems, kValue); pcms::Rank2View view(data.data(), nelems, 1); - field.GetData().SetDOFHolderDataHost(view); + field.SetDOFHolderDataHost( + pcms::ValueView(pcms::ValueBasis{}, + view)); pcms::test::CheckEvaluation( factory, field, pcms::test::StandardEvalCoords2D(), @@ -274,7 +280,9 @@ TEST_CASE("OmegaHLagrangeField order-0: out-of-bounds FILL mode") std::vector data(nelems, 1.0); pcms::Rank2View view(data.data(), nelems, 1); - field.GetData().SetDOFHolderDataHost(view); + field.SetDOFHolderDataHost( + pcms::ValueView(pcms::ValueBasis{}, + view)); Real fill_value = -1.0; std::vector outside{-0.5, 0.5, 1.5, 0.5}; diff --git a/test/test_point_evaluator.cpp b/test/test_point_evaluator.cpp index 347a3e24a..12c6442b3 100644 --- a/test/test_point_evaluator.cpp +++ b/test/test_point_evaluator.cpp @@ -34,7 +34,7 @@ TEST_CASE("PointEvaluator: OmegaH order-1 linear evaluation") auto field_data = factory->CreateFunction(); pcms::test::SetField( - field_data.GetData(), *factory->GetLayout(), + field_data, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); auto pts = pcms::test::StandardEvalCoords2D(); @@ -67,11 +67,10 @@ TEST_CASE("PointEvaluator: same evaluator reused for two FieldData objects") // field_a: linear_f; field_b: constant 42 pcms::test::SetField( - field_a.GetData(), *factory->GetLayout(), + field_a, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); pcms::test::SetField( - field_b.GetData(), *factory->GetLayout(), - OMEGA_H_LAMBDA(Real, Real) { return Real(42); }); + field_b, OMEGA_H_LAMBDA(Real, Real) { return Real(42); }); auto pts = pcms::test::StandardEvalCoords2D(); auto device_coords = pcms::test::CreateDeviceCoordinateView( @@ -104,7 +103,7 @@ TEST_CASE("PointEvaluator: OmegaH order-1 out-of-bounds fill") auto field_data = factory->CreateFunction(); pcms::test::SetField( - field_data.GetData(), *factory->GetLayout(), + field_data, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); // Points clearly outside [0,1]^2 @@ -136,7 +135,7 @@ TEST_CASE("PointEvaluator: UniformGrid order-1 linear evaluation") auto field_data = factory->CreateFunction(); pcms::test::SetField( - field_data.GetData(), *factory->GetLayout(), + field_data, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); auto pts = pcms::test::StandardEvalCoords2D(); int n = static_cast(pts.size()) / 2; @@ -163,7 +162,7 @@ TEST_CASE("PointEvaluator: SplineFunctionSpace uniform-grid evaluation") auto field_data = factory->CreateFunction(); pcms::test::SetField( - field_data.GetData(), *factory->GetLayout(), + field_data, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); auto pts = pcms::test::StandardEvalCoords2D(); int n = static_cast(pts.size()) / 2; @@ -195,7 +194,7 @@ TEST_CASE("PointEvaluator: uniform grids report the points they fill") auto factory = pcms::LagrangeFunctionSpace::FromUniformGrid( grid, 1, pcms::csys::Cartesian::Deferred(), 1); auto field = factory->CreateFunction(); - pcms::test::SetField(field.GetData(), *factory->GetLayout(), linear); + pcms::test::SetField(field, linear); pcms::test::CheckEvaluationWithFill(factory, field, pts, is_inside, linear, -999.0, 1e-8); } @@ -204,7 +203,7 @@ TEST_CASE("PointEvaluator: uniform grids report the points they fill") auto factory = pcms::SplineFunctionSpace::FromUniformGrid( grid, pcms::csys::Cartesian::Deferred()); auto field = factory->CreateFunction(); - pcms::test::SetField(field.GetData(), *factory->GetLayout(), linear); + pcms::test::SetField(field, linear); pcms::test::CheckEvaluationWithFill(factory, field, pts, is_inside, linear, -999.0, 1e-8); } @@ -290,11 +289,13 @@ TEST_CASE("SimpleFieldData: set and get DOF holder data round-trip") for (int i = 0; i < n; ++i) data_in[i] = static_cast(i) * 0.5; - field_data.GetData().SetDOFHolderDataHost( - pcms::Rank2View(data_in.data(), n, 1)); + field_data.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, pcms::Rank2View( + data_in.data(), n, 1))); auto data_out = - pcms::FlattenToRank1View(field_data.GetData().GetDOFHolderDataHost()); + pcms::FlattenToRank1View(field_data.GetDOFHolderDataHost().GetValues()); REQUIRE(static_cast(data_out.size()) == n); for (int i = 0; i < n; ++i) { REQUIRE(data_out[i] == Catch::Approx(data_in[i])); @@ -336,7 +337,7 @@ TEST_CASE("PointEvaluator: MeshFields order-1 linear evaluation") auto field_data = factory->CreateFunction(); pcms::test::SetField( - field_data.GetData(), *factory->GetLayout(), + field_data, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); auto pts = pcms::test::StandardEvalCoords2D(); @@ -362,7 +363,7 @@ TEST_CASE("PointEvaluator: MeshFields out-of-bounds fill") auto field_data = factory->CreateFunction(); pcms::test::SetField( - field_data.GetData(), *factory->GetLayout(), + field_data, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); const auto outside_pts = pcms::test::StandardOutsideCoords2D(); @@ -389,11 +390,10 @@ TEST_CASE( auto field_a = factory->CreateFunction(); auto field_b = factory->CreateFunction(); pcms::test::SetField( - field_a.GetData(), *factory->GetLayout(), + field_a, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); pcms::test::SetField( - field_b.GetData(), *factory->GetLayout(), - OMEGA_H_LAMBDA(Real, Real) { return Real(42); }); + field_b, OMEGA_H_LAMBDA(Real, Real) { return Real(42); }); auto pts = pcms::test::StandardEvalCoords2D(); auto device_coords = pcms::test::CreateDeviceCoordinateView( @@ -439,9 +439,10 @@ TEST_CASE("PointEvaluator: MeshFields order-1 multi-component (2) evaluation") host_data[static_cast(i) * 2 + 0] = x + y; host_data[static_cast(i) * 2 + 1] = 2.0 * x - y; } - field.GetData().SetDOFHolderDataHost( - pcms::Rank2View(host_data.data(), - num_dof, 2)); + field.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, pcms::Rank2View( + host_data.data(), num_dof, 2))); auto pts = pcms::test::StandardEvalCoords2D(); int n = static_cast(pts.size()) / 2; @@ -494,9 +495,10 @@ TEST_CASE("PointEvaluator: MeshFields order-1 multi-component (3) evaluation") host_data[static_cast(i) * 3 + 1] = y; host_data[static_cast(i) * 3 + 2] = x * y; } - field.GetData().SetDOFHolderDataHost( - pcms::Rank2View(host_data.data(), - num_dof, 3)); + field.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, pcms::Rank2View( + host_data.data(), num_dof, 3))); auto pts = pcms::test::StandardEvalCoords2D(); int n = static_cast(pts.size()) / 2; diff --git a/test/test_polynomial_reconstruction_function_space.cpp b/test/test_polynomial_reconstruction_function_space.cpp index cecf68050..c62865292 100644 --- a/test/test_polynomial_reconstruction_function_space.cpp +++ b/test/test_polynomial_reconstruction_function_space.cpp @@ -87,9 +87,11 @@ TEST_CASE("PolynomialReconstructionFunctionSpace point-cloud field set/get DOF " std::vector data{1.0, 2.0, 3.0, 4.0}; Rank2View data_view( data.data(), static_cast(data.size()), 1); - field.GetData().SetDOFHolderDataHost(data_view); + field.SetDOFHolderDataHost( + pcms::ValueView(pcms::ValueBasis{}, + data_view)); - auto got = pcms::FlattenToRank1View(field.GetData().GetDOFHolderDataHost()); + auto got = pcms::FlattenToRank1View(field.GetDOFHolderDataHost().GetValues()); REQUIRE(got.size() == data.size()); for (LO i = 0; i < static_cast(data.size()); ++i) { REQUIRE(got[i] == Catch::Approx(data[i])); diff --git a/test/test_polynomial_reconstruction_mls_evaluation.cpp b/test/test_polynomial_reconstruction_mls_evaluation.cpp index e656b7b8b..9f8784d66 100644 --- a/test/test_polynomial_reconstruction_mls_evaluation.cpp +++ b/test/test_polynomial_reconstruction_mls_evaluation.cpp @@ -118,7 +118,7 @@ void CheckPolynomialReproduction(unsigned degree, coords_view), SweepTestOptions(degree, basis)); auto field = fs->CreateFunction(); - pcms::test::SetField(field.GetData(), *fs->GetLayout(), func); + pcms::test::SetField(field, func); auto pts = pcms::test::StandardEvalCoords2D(); auto device_coords = pcms::test::CreateDeviceCoordinateView( @@ -176,12 +176,10 @@ TEST_CASE("PolynomialReconstructionFunctionSpace MLS: same PointEvaluator " auto field_b = fs->CreateFunction(); pcms::test::SetField( - field_a.GetData(), *fs->GetLayout(), + field_a, OMEGA_H_LAMBDA(Real x, Real y) { return pcms::test::linear_f(x, y); }); const Real cval = 7.0; - pcms::test::SetField( - field_b.GetData(), *fs->GetLayout(), - OMEGA_H_LAMBDA(Real, Real) { return cval; }); + pcms::test::SetField(field_b, OMEGA_H_LAMBDA(Real, Real) { return cval; }); auto pts = pcms::test::StandardEvalCoords2D(); auto device_coords = pcms::test::CreateDeviceCoordinateView( @@ -242,9 +240,7 @@ TEST_CASE("PolynomialReconstructionFunctionSpace MLS: default MLSOptions — " pcms::CoordinateView(pcms::csys::Cartesian::Deferred(), coords_view)); auto field = fs->CreateFunction(); - pcms::test::SetField( - field.GetData(), *fs->GetLayout(), - OMEGA_H_LAMBDA(Real, Real) { return Real(1.0); }); + pcms::test::SetField(field, OMEGA_H_LAMBDA(Real, Real) { return Real(1.0); }); auto pts = pcms::test::StandardEvalCoords2D(); int n = static_cast(pts.size()) / 2; @@ -305,8 +301,11 @@ TEST_CASE("PolynomialReconstructionFunctionSpace MLS: accepts the (R, Z) " for (size_t i = 0; i < src.size() / 2; ++i) { dof_values.push_back(linear_rz(src[2 * i], src[2 * i + 1])); } - field.GetData().SetDOFHolderDataHost(Rank2View( - dof_values.data(), static_cast(dof_values.size()), 1)); + field.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, + Rank2View( + dof_values.data(), static_cast(dof_values.size()), 1))); std::vector pts{0.3, 0.4, 0.6, 0.7}; auto device_coords = pcms::test::CreateDeviceCoordinateView( @@ -362,8 +361,11 @@ TEST_CASE("PolynomialReconstructionFunctionSpace MLS: radius option is " opts); auto field = fs->CreateFunction(); std::vector dof_values{1.0, 5.0}; - field.GetData().SetDOFHolderDataHost(Rank2View( - dof_values.data(), static_cast(dof_values.size()), 1)); + field.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, + Rank2View( + dof_values.data(), static_cast(dof_values.size()), 1))); std::vector pts{0.0, 0.0}; auto device_coords = pcms::test::CreateDeviceCoordinateView( diff --git a/test/test_proxy_coupling.cpp b/test/test_proxy_coupling.cpp index 8a0f4f354..0e8c04d47 100644 --- a/test/test_proxy_coupling.cpp +++ b/test/test_proxy_coupling.cpp @@ -23,7 +23,7 @@ static constexpr int COMM_ROUNDS = 4; namespace ts = test_support; void initializeFieldWithGids(const pcms::FieldLayout& layout, - pcms::FieldData* field, + pcms::Field& field, pcms::Real multiplier = 1.0) { auto gids = layout.GetGidsHost(); @@ -36,17 +36,21 @@ void initializeFieldWithGids(const pcms::FieldLayout& layout, Kokkos::RangePolicy(0, n), [=](int i) { ids[i] = gids[i] * multiplier; }); - field->SetDOFHolderDataHost( - pcms::Rank2View(ids.data(), n, 1)); + field.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, + pcms::Rank2View(ids.data(), n, + 1))); } bool validateField(const pcms::FieldLayout& layout, - pcms::FieldData* field, + const pcms::Field* field, const std::string& field_name, int rank, pcms::Real multiplier = 1.0) { auto gids = layout.GetGidsHost(); - auto copied_array = pcms::FlattenToRank1View(field->GetDOFHolderDataHost()); + auto copied_array = + pcms::FlattenToRank1View(field->GetDOFHolderDataHost().GetValues()); auto owned = layout.GetOwnedHost(); const auto n = layout.GetNumOwnedDofHolder(); @@ -98,13 +102,10 @@ void xgc_delta_f(MPI_Comm comm, Omega_h::Mesh& mesh) auto gids_field = factory->CreateFunction("gids"); auto gids2_field = factory->CreateFunction("gids2"); - auto* gids_ptr = &gids_field.GetData(); - auto* gids2_ptr = &gids2_field.GetData(); + initializeFieldWithGids(*factory->GetLayout(), gids_field, 1.0); + initializeFieldWithGids(*factory->GetLayout(), gids2_field, 2.0); - initializeFieldWithGids(*factory->GetLayout(), gids_ptr, 1.0); - initializeFieldWithGids(*factory->GetLayout(), gids2_ptr, 2.0); - - app->AddField(std::move(gids_field)); + const auto* gids_ptr = &app->AddField(std::move(gids_field)).GetField(); app->AddField(std::move(gids2_field)); do { @@ -139,11 +140,9 @@ void xgc_total_f(MPI_Comm comm, Omega_h::Mesh& mesh) auto gids_field = factory->CreateFunction("gids"); - auto* gids_ptr = &gids_field.GetData(); - - initializeFieldWithGids(*factory->GetLayout(), gids_ptr, 10.0); + initializeFieldWithGids(*factory->GetLayout(), gids_field, 10.0); - app->AddField(std::move(gids_field)); + const auto* gids_ptr = &app->AddField(std::move(gids_field)).GetField(); do { for (int i = 0; i < COMM_ROUNDS; ++i) { @@ -188,12 +187,10 @@ void xgc_coupler(MPI_Comm comm, Omega_h::Mesh& mesh, std::string_view cpn_file) auto delta_gids_field = factory_delta->CreateFunction("gids"); auto delta_gids2_field = factory_delta->CreateFunction("gids2"); - auto* total_gids_ptr = &total_gids_field.GetData(); - auto* delta_gids_ptr = &delta_gids_field.GetData(); - auto* delta_gids2_ptr = &delta_gids2_field.GetData(); - - total_f->AddField(std::move(total_gids_field)); - delta_f->AddField(std::move(delta_gids_field)); + const auto* total_gids_ptr = + &total_f->AddField(std::move(total_gids_field)).GetField(); + const auto* delta_gids_ptr = + &delta_f->AddField(std::move(delta_gids_field)).GetField(); delta_f->AddField(std::move(delta_gids2_field)); do { diff --git a/test/test_proxy_coupling_xgc_server.cpp b/test/test_proxy_coupling_xgc_server.cpp index f890ab636..351d5f81a 100644 --- a/test/test_proxy_coupling_xgc_server.cpp +++ b/test/test_proxy_coupling_xgc_server.cpp @@ -8,7 +8,6 @@ #include "test_support.h" #include "pcms/field/function_space/xgc.h" #include "pcms/field/layout/xgc.h" -#include "pcms/coupler/serializer/xgc.h" #include "pcms/coupler/coupler.hpp" #include "pcms/field/value_view.hpp" #include "pcms/field/function_space/lagrange.h" @@ -59,11 +58,9 @@ void xgc_coupler(MPI_Comm comm, Omega_h::Mesh& mesh, std::string_view cpn_file) auto field = function_space.CreateField( ss.str(), std::make_unique>( function_space.GetXGCLayout(), pcms::ValueBasis{}, - make_array_view(data[i]))); - std::unique_ptr> serializer = - std::make_unique>(comm); + make_array_view(data[i]), comm)); fields.push_back( - application->AddField(std::move(field), std::move(serializer))); + application->AddField(std::move(field))); } do { @@ -179,10 +176,8 @@ void omegah_coupler(MPI_Comm comm, Omega_h::Mesh& mesh, auto field = factory->CreateFunction( ss.str(), std::make_unique>( factory->GetLayout(), pcms::ValueBasis{})); - std::unique_ptr> serializer = - std::make_unique>(); fields.push_back( - application->AddField(std::move(field), std::move(serializer))); + application->AddField(std::move(field))); } do { application->ReceivePhase([&]() { diff --git a/test/test_proxy_coupling_xgc_server_overlap.cpp b/test/test_proxy_coupling_xgc_server_overlap.cpp index 3773e25c7..39d4a1cd3 100644 --- a/test/test_proxy_coupling_xgc_server_overlap.cpp +++ b/test/test_proxy_coupling_xgc_server_overlap.cpp @@ -8,7 +8,6 @@ #include "test_support.h" #include "pcms/field/function_space/xgc.h" #include "pcms/field/layout/xgc.h" -#include "pcms/coupler/serializer/xgc.h" #include "pcms/coupler/coupler.hpp" #include "pcms/coupler/overlap_mask.h" #include "pcms/field/value_view.hpp" @@ -63,13 +62,11 @@ void xgc_coupler_with_overlap(MPI_Comm comm, Omega_h::Mesh& mesh, auto field = function_space.CreateField( ss.str(), std::make_unique>( function_space.GetXGCLayout(), pcms::ValueBasis{}, - make_array_view(data[i]))); + make_array_view(data[i]), comm)); - std::unique_ptr> serializer = - std::make_unique>(comm); fields.push_back( - application->AddField(std::move(field), std::move(serializer))); + application->AddField(std::move(field))); } do { @@ -183,11 +180,9 @@ void omegah_coupler_with_overlap(MPI_Comm comm, Omega_h::Mesh& mesh, ss.str(), std::make_unique>( factory->GetLayout(), pcms::ValueBasis{})); - std::unique_ptr> serializer = - std::make_unique>(); fields.push_back( - application->AddField(std::move(field), std::move(serializer))); + application->AddField(std::move(field))); } do { diff --git a/test/test_uniform_grid_field.cpp b/test/test_uniform_grid_field.cpp index a83539207..e4e44ec69 100644 --- a/test/test_uniform_grid_field.cpp +++ b/test/test_uniform_grid_field.cpp @@ -693,9 +693,11 @@ TEST_CASE("UniformGrid Order-1 multi-component field evaluation") // Reshape data to [num_vertices][num_components] pcms::LO num_vertices = (grid.divisions[0] + 1) * (grid.divisions[1] + 1); - field.GetData().SetDOFHolderDataHost( - pcms::Rank2View( - data.data(), num_vertices, num_components)); + field.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, + pcms::Rank2View( + data.data(), num_vertices, num_components))); // Mix of cell centers and off-center points. const int num_points = 4; @@ -757,9 +759,11 @@ TEST_CASE("UniformGrid Order-0 multi-component field (regression)") 4.0, 40.0 // Cell 3 }; - field.GetData().SetDOFHolderDataHost( - pcms::Rank2View(data.data(), 4, - num_components)); + field.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, + pcms::Rank2View( + data.data(), 4, num_components))); // Evaluate at cell centers std::vector eval_coords = { diff --git a/test/xgc_n0_coupling_server.cpp b/test/xgc_n0_coupling_server.cpp index f8a5a55f6..417e965f5 100644 --- a/test/xgc_n0_coupling_server.cpp +++ b/test/xgc_n0_coupling_server.cpp @@ -49,9 +49,7 @@ struct RegisteredField PCMS_ALWAYS_ASSERT(application != nullptr); auto field_name = MakeFieldName(name, plane); auto field = function_space->CreateFunction(path + field_name); - std::unique_ptr> serializer = - std::make_unique>(); - auto handle = application->AddField(std::move(field), std::move(serializer)); + auto handle = application->AddField(std::move(field)); return {std::move(handle)}; } From 8fc002b96442e2928f84d7978b7d813d38b32b02 Mon Sep 17 00:00:00 2001 From: Jacob Merson Date: Sun, 25 Oct 2026 21:01:51 +0000 Subject: [PATCH 2/3] Make overlap masking generic and drop XGC-specific overlap handling --- .gitattributes | 3 + src/pcms/capi/client.cpp | 46 +- src/pcms/capi/client.h | 13 +- src/pcms/coupler/coupler.hpp | 5 +- src/pcms/coupler/field_communicator.hpp | 2 +- .../coupler/field_layout_communicator.cpp | 4 +- src/pcms/coupler/field_serializer.h | 10 +- src/pcms/coupler/overlap_mask.h | 34 +- src/pcms/field/data/mfem.hpp | 5 +- src/pcms/field/data/point_cloud.h | 5 +- src/pcms/field/field_data.h | 2 +- src/pcms/field/function_space/xgc.h | 6 +- src/pcms/field/layout/xgc.cpp | 98 +- src/pcms/field/layout/xgc.h | 2 - src/pcms/fortranapi/client.i | 11 +- src/pcms/fortranapi/client_wrap.c | 79 +- src/pcms/fortranapi/pcms.f90 | 1051 +++++++++-------- test/test_field_serializer.cpp | 28 +- test/test_mfem_adapter.cpp | 14 +- test/test_proxy_coupling_xgc_client.c | 3 +- ...test_proxy_coupling_xgc_client_fortran.f90 | 3 +- test/test_proxy_coupling_xgc_server.cpp | 11 +- ...test_proxy_coupling_xgc_server_overlap.cpp | 19 +- test/test_xgc_field_data.cpp | 22 +- 24 files changed, 781 insertions(+), 695 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..48415451a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# SWIG output regenerated from client.i; collapsed in GitHub diffs +src/pcms/fortranapi/client_wrap.c linguist-generated +src/pcms/fortranapi/pcms.f90 linguist-generated diff --git a/src/pcms/capi/client.cpp b/src/pcms/capi/client.cpp index 32dc52121..414579bf3 100644 --- a/src/pcms/capi/client.cpp +++ b/src/pcms/capi/client.cpp @@ -129,10 +129,9 @@ ClientState::HandleVariant RegisterField( // not the arbitrary adapter name given at creation. registration.function_space.SetLayoutName(name); auto field = registration.function_space.template CreateField( - std::move(name), - std::make_unique>( - registration.function_space.GetXGCLayout(), ValueBasis{}, - registration.data, registration.plane_comm)); + std::move(name), std::make_unique>( + registration.function_space.GetXGCLayout(), ValueBasis{}, + registration.data, registration.plane_comm)); return ClientState::HandleVariant{ app.AddField(std::move(field), participates)}; } @@ -184,6 +183,18 @@ void pcms_destroy_reverse_classification(PcmsReverseClassificationHandle rc) delete reinterpret_cast(rc.pointer); } +void pcms_set_overlap_mask(PcmsClientHandle client_handle, const char* name, + in_overlap_function in_overlap) +{ + auto* app = + reinterpret_cast(client_handle.appPointer); + PCMS_ALWAYS_ASSERT(app != nullptr); + PCMS_ALWAYS_ASSERT(in_overlap != nullptr); + // Fields registered through the C API use their name as the layout name. + app->SetLayoutOverlapMask(name, + std::make_unique(in_overlap)); +} + PcmsFieldHandle pcms_add_field(PcmsClientHandle client_handle, const char* name, PcmsFieldAdapterHandle adapter_handle, int participates) @@ -248,11 +259,10 @@ template void pcms_create_xgc_field_adapter_t( const char* /* name */, MPI_Comm comm, void* data, int size, const pcms::ReverseClassificationVertex& reverse_classification, - in_overlap_function in_overlap, pcms::FieldAdapterVariant& field_adapter) + pcms::FieldAdapterVariant& field_adapter) { PCMS_ALWAYS_ASSERT((size > 0) ? (data != nullptr) : true); - auto function_space = - pcms::XGCFieldFactory(reverse_classification, in_overlap, size); + auto function_space = pcms::XGCFieldFactory(reverse_classification, size); pcms::Rank1View data_view( reinterpret_cast(data), size); field_adapter.emplace>( @@ -261,7 +271,7 @@ void pcms_create_xgc_field_adapter_t( PcmsFieldAdapterHandle pcms_create_xgc_field_adapter( const char* name, MPI_Comm comm, void* data, int size, PcmsType data_type, - const PcmsReverseClassificationHandle rc, in_overlap_function in_overlap) + const PcmsReverseClassificationHandle rc) { auto* field_adapter = new pcms::FieldAdapterVariant{}; PCMS_ALWAYS_ASSERT(rc.pointer != nullptr); @@ -270,24 +280,20 @@ PcmsFieldAdapterHandle pcms_create_xgc_field_adapter( PCMS_ALWAYS_ASSERT(reverse_classification != nullptr); switch (data_type) { case PCMS_DOUBLE: - pcms_create_xgc_field_adapter_t(name, comm, data, size, - *reverse_classification, - in_overlap, *field_adapter); + pcms_create_xgc_field_adapter_t( + name, comm, data, size, *reverse_classification, *field_adapter); break; case PCMS_FLOAT: - pcms_create_xgc_field_adapter_t(name, comm, data, size, - *reverse_classification, - in_overlap, *field_adapter); + pcms_create_xgc_field_adapter_t( + name, comm, data, size, *reverse_classification, *field_adapter); break; case PCMS_INT: - pcms_create_xgc_field_adapter_t(name, comm, data, size, - *reverse_classification, in_overlap, - *field_adapter); + pcms_create_xgc_field_adapter_t( + name, comm, data, size, *reverse_classification, *field_adapter); break; case PCMS_LONG_INT: - pcms_create_xgc_field_adapter_t(name, comm, data, size, - *reverse_classification, - in_overlap, *field_adapter); + pcms_create_xgc_field_adapter_t( + name, comm, data, size, *reverse_classification, *field_adapter); break; default: PCMS_ALWAYS_ASSERT(false, MPI_COMM_WORLD, diff --git a/src/pcms/capi/client.h b/src/pcms/capi/client.h index 5aa8eec64..b17a664f1 100644 --- a/src/pcms/capi/client.h +++ b/src/pcms/capi/client.h @@ -65,13 +65,18 @@ void pcms_destroy_reverse_classification(PcmsReverseClassificationHandle); // likely to be needed for production cases int pcms_reverse_classification_count_verts(PcmsReverseClassificationHandle); +PcmsFieldAdapterHandle pcms_create_xgc_field_adapter( + const char* name, MPI_Comm plane_comm, void* data, int size, + PcmsType data_type, const PcmsReverseClassificationHandle rc); + // takes in overlap function takes a geometric dimension and a geometric id // C doesn't have a builtin bool type, so we use int for compatability with C++ typedef int8_t (*in_overlap_function)(int, int); -PcmsFieldAdapterHandle pcms_create_xgc_field_adapter( - const char* name, MPI_Comm plane_comm, void* data, int size, - PcmsType data_type, const PcmsReverseClassificationHandle rc, - in_overlap_function in_overlap); +// Restricts the field registered under `name` to the DOF holders whose +// classification satisfies in_overlap (unclassified holders are passed as +// (-1, -1)). +void pcms_set_overlap_mask(PcmsClientHandle client_handle, const char* name, + in_overlap_function in_overlap); PcmsFieldAdapterHandle pcms_create_dummy_field_adapter(); diff --git a/src/pcms/coupler/coupler.hpp b/src/pcms/coupler/coupler.hpp index 6bbad68b4..7bdf23118 100644 --- a/src/pcms/coupler/coupler.hpp +++ b/src/pcms/coupler/coupler.hpp @@ -403,7 +403,7 @@ pcms::Field& pcms::ApplicationComm::GetField(const std::string& name) template void pcms::ApplicationComm::RegisterFieldCommunicator(Field& field_obj, - bool participates) + bool participates) { const std::string& name = field_obj.GetName(); if (field_obj.GetData().GetDOFAccess() == nullptr) { @@ -414,7 +414,8 @@ void pcms::ApplicationComm::RegisterFieldCommunicator(Field& field_obj, FieldLayoutCommunicator& layout_communicator = GetOrCreateLayoutCommunicator(field_obj.GetLayout(), participates); FieldCommunicator2Ptr field_communicator = - std::make_unique>(name, layout_communicator, field_obj); + std::make_unique>(name, layout_communicator, + field_obj); auto [it, inserted] = field_communicators_.emplace(name, std::move(field_communicator)); if (!inserted) { diff --git a/src/pcms/coupler/field_communicator.hpp b/src/pcms/coupler/field_communicator.hpp index 8f95e4216..8dc8de534 100644 --- a/src/pcms/coupler/field_communicator.hpp +++ b/src/pcms/coupler/field_communicator.hpp @@ -53,7 +53,7 @@ class FieldCommunicator // receive. auto data = comm_.Recv(redev::Mode::Synchronous); serializer_.Deserialize(field_, make_const_array_view(data), - layout_comm_.GetPermutationArray()); + layout_comm_.GetPermutationArray()); } private: diff --git a/src/pcms/coupler/field_layout_communicator.cpp b/src/pcms/coupler/field_layout_communicator.cpp index 9bf9fe73f..f20b53945 100644 --- a/src/pcms/coupler/field_layout_communicator.cpp +++ b/src/pcms/coupler/field_layout_communicator.cpp @@ -111,8 +111,8 @@ void FieldLayoutCommunicator::UpdateLayoutNull() PCMS_FUNCTION_TIMER; // This rank exchanges nothing, but it still unpacks on receive (backends // that keep replicas synchronize them there), so every holder is unmapped. - plan_.permutation = - MakeUnmappedPermutation(static_cast(layout_.GetNumOwnedDofHolder())); + plan_.permutation = MakeUnmappedPermutation( + static_cast(layout_.GetNumOwnedDofHolder())); if (redev_.GetProcessType() == redev::ProcessType::Client) { channel_.BeginSendCommunicationPhase(); channel_.EndSendCommunicationPhase(); diff --git a/src/pcms/coupler/field_serializer.h b/src/pcms/coupler/field_serializer.h index 27ef6dafc..883b61418 100644 --- a/src/pcms/coupler/field_serializer.h +++ b/src/pcms/coupler/field_serializer.h @@ -18,9 +18,8 @@ template class FieldSerializer { public: - int Serialize(const Field& field, - Rank1View buffer, - Rank1View permutation) const + int Serialize(const Field& field, Rank1View buffer, + Rank1View permutation) const { const auto data = field.GetDOFHolderDataHost().GetValues(); if (buffer.size() > 0) { @@ -37,9 +36,8 @@ class FieldSerializer return static_cast(buffer.size()); } - void Deserialize( - Field& field, Rank1View buffer, - Rank1View permutation) const + void Deserialize(Field& field, Rank1View buffer, + Rank1View permutation) const { const auto& layout = field.GetLayout(); const LO num_dof = layout.GetNumOwnedDofHolder(); diff --git a/src/pcms/coupler/overlap_mask.h b/src/pcms/coupler/overlap_mask.h index fef2d7537..1a917abb4 100644 --- a/src/pcms/coupler/overlap_mask.h +++ b/src/pcms/coupler/overlap_mask.h @@ -15,17 +15,18 @@ struct OverlapMask Kokkos::View is_overlap_; std::function in_overlap_func_; - // Default constructor: all DOFs are in overlap + // Default constructor: all DOF holders are in overlap OverlapMask(size_t size) : is_overlap_("overlap_info", size) { Kokkos::deep_copy(is_overlap_, true); } - // Construct from a function that determines overlap based on classification - OverlapMask(size_t size, std::function in_overlap) - : is_overlap_("overlap_info", size), in_overlap_func_(std::move(in_overlap)) + // Construct from a function that determines overlap from a DOF holder's + // classification (dimension, id). Holders without classification are passed + // as (-1, -1). + explicit OverlapMask(std::function in_overlap) + : in_overlap_func_(std::move(in_overlap)) { - Kokkos::deep_copy(is_overlap_, true); } // Construct from a precomputed per-DOF-holder host mask (e.g. an MFEM @@ -56,19 +57,22 @@ struct OverlapMask } } - // Get the mask, evaluating the function if needed - Rank1View GetMask( + // Get the mask, evaluating the function against the layout if one was given + Kokkos::View GetMask( const FieldLayout& layout) const { - if (in_overlap_func_) { - auto class_dims = layout.GetDOFHolderClassificationDimensionsHost(); - auto class_ids = layout.GetDOFHolderClassificationIdsHost(); - for (size_t i = 0; i < is_overlap_.extent(0); ++i) { - is_overlap_[i] = - static_cast(in_overlap_func_(class_dims[i], class_ids[i])); - } + if (!in_overlap_func_) { + return is_overlap_; + } + auto class_dims = layout.GetDOFHolderClassificationDimensionsHost(); + auto class_ids = layout.GetDOFHolderClassificationIdsHost(); + Kokkos::View is_overlap("overlap_info", + class_dims.size()); + for (size_t i = 0; i < is_overlap.extent(0); ++i) { + is_overlap[i] = + static_cast(in_overlap_func_(class_dims[i], class_ids[i])); } - return make_const_array_view(is_overlap_); + return is_overlap; } }; diff --git a/src/pcms/field/data/mfem.hpp b/src/pcms/field/data/mfem.hpp index 392f038fc..be7837a13 100644 --- a/src/pcms/field/data/mfem.hpp +++ b/src/pcms/field/data/mfem.hpp @@ -22,10 +22,7 @@ class MFEMVertexFieldData FieldValueType GetValueType() const override; const ValueBasis& GetValueBasis() const override; - const DOFAccess* GetDOFAccess() const noexcept override - { - return this; - } + const DOFAccess* GetDOFAccess() const noexcept override { return this; } DOFAccess* GetDOFAccess() noexcept override { return this; } Rank2View GetOwnedValues() const override; diff --git a/src/pcms/field/data/point_cloud.h b/src/pcms/field/data/point_cloud.h index 93170b22d..a7006b9e0 100644 --- a/src/pcms/field/data/point_cloud.h +++ b/src/pcms/field/data/point_cloud.h @@ -19,10 +19,7 @@ class PointCloud FieldValueType GetValueType() const override; const ValueBasis& GetValueBasis() const override; - const DOFAccess* GetDOFAccess() const noexcept override - { - return this; - } + const DOFAccess* GetDOFAccess() const noexcept override { return this; } DOFAccess* GetDOFAccess() noexcept override { return this; } Rank2View GetOwnedValues() const override; diff --git a/src/pcms/field/field_data.h b/src/pcms/field/field_data.h index 77b12984c..bed91f930 100644 --- a/src/pcms/field/field_data.h +++ b/src/pcms/field/field_data.h @@ -30,7 +30,7 @@ class DOFAccess }; // FieldData holds whatever a backend needs to represent a field. It is -// opaque. It should only be direcly used by the type that created it. +// opaque. It should only be directly used by the type that created it. template class FieldData { diff --git a/src/pcms/field/function_space/xgc.h b/src/pcms/field/function_space/xgc.h index 29555da69..ee0b63b2a 100644 --- a/src/pcms/field/function_space/xgc.h +++ b/src/pcms/field/function_space/xgc.h @@ -6,7 +6,6 @@ #include "pcms/field/field_factory.h" #include "pcms/field/value_view.hpp" #include "pcms/utility/common.h" -#include #include namespace pcms @@ -17,10 +16,9 @@ class XGCFieldFactory : public FieldFactory { public: XGCFieldFactory(const ReverseClassificationVertex& reverse_classification, - std::function in_overlap, LO num_plane_nodes, std::string layout_name = "") - : layout_(std::make_shared( - reverse_classification, std::move(in_overlap), num_plane_nodes)) + : layout_(std::make_shared(reverse_classification, + num_plane_nodes)) { layout_->SetName(std::move(layout_name)); } diff --git a/src/pcms/field/layout/xgc.cpp b/src/pcms/field/layout/xgc.cpp index 3dae952be..54d9eecfd 100644 --- a/src/pcms/field/layout/xgc.cpp +++ b/src/pcms/field/layout/xgc.cpp @@ -1,5 +1,6 @@ #include "pcms/field/layout/xgc.h" #include "pcms/utility/assert.h" +#include "pcms/utility/entity_types.h" #include "pcms/field/coordinate_systems/cylindrical.hpp" namespace pcms @@ -11,60 +12,39 @@ struct InitilizeXGCMembersFunctor Kokkos::View gids_; Kokkos::View class_dims_; Kokkos::View class_ids_; - - InitilizeXGCMembersFunctor(Kokkos::View owned, - Kokkos::View gids, - Kokkos::View class_dims, - Kokkos::View class_ids) - : owned_(owned), gids_(gids), class_dims_(class_dims), class_ids_(class_ids) + Rank1View vert_class_dims_; + Rank1View vert_class_ids_; + + InitilizeXGCMembersFunctor( + Kokkos::View owned, + Kokkos::View gids, + Kokkos::View class_dims, + Kokkos::View class_ids, + Rank1View vert_class_dims, + Rank1View vert_class_ids) + : owned_(owned), + gids_(gids), + class_dims_(class_dims), + class_ids_(class_ids), + vert_class_dims_(vert_class_dims), + vert_class_ids_(vert_class_ids) { } KOKKOS_INLINE_FUNCTION void operator()(LO i) const { - owned_(i) = false; + owned_(i) = true; gids_(i) = static_cast(i) + 1; - class_dims_(i) = -1; - class_ids_(i) = -1; + // ClassificationDimension is int8_t, a small integer rather than a char + // NOLINTNEXTLINE(bugprone-signed-char-misuse) + class_dims_(i) = vert_class_dims_(i); + class_ids_(i) = vert_class_ids_(i); } }; -struct ClassifyVertsAndOwnedFunctor -{ - pcms::DimID geom; - Kokkos::View verts; - Kokkos::View owned_; - Kokkos::View class_dims_; - Kokkos::View class_ids_; - - ClassifyVertsAndOwnedFunctor(pcms::DimID geom, - Kokkos::View verts, - Kokkos::View owned, - Kokkos::View class_dims, - Kokkos::View class_ids) - : geom(geom), - verts(verts), - owned_(owned), - class_dims_(class_dims), - class_ids_(class_ids) - { - } - - KOKKOS_INLINE_FUNCTION void operator()(const int i) const - { - LO vert = verts(i); - if (vert >= 0 && vert < owned_.extent(0)) { - owned_(vert) = true; - class_dims_(vert) = geom.dim; - class_ids_(vert) = geom.id; - } - }; -}; - XGCFieldLayout::XGCFieldLayout( - const ReverseClassificationVertex& reverse_classification, - std::function in_overlap, LO num_plane_nodes) + const ReverseClassificationVertex& reverse_classification, LO num_plane_nodes) : owned_("xgc_owned", num_plane_nodes), gids_("xgc_gids", num_plane_nodes), class_dims_("xgc_class_dims", num_plane_nodes), @@ -78,33 +58,16 @@ XGCFieldLayout::XGCFieldLayout( { // XGC poloidal planes are in the RZ plane of a cylindrical coordinate system SetCoordinateSystem(csys::CylindricalRZ::Create()); - PCMS_ALWAYS_ASSERT(static_cast(in_overlap)); + auto discretization = std::make_shared( + reverse_classification, num_plane_nodes_); Kokkos::parallel_for( "InitXGCMembers", Kokkos::RangePolicy(0, num_plane_nodes_), - InitilizeXGCMembersFunctor(owned_, gids_, class_dims_, class_ids_)); - - for (const auto& [geom, verts] : reverse_classification) { - if (!in_overlap(geom.dim, geom.id)) - continue; - auto verts_host = - Kokkos::View("verts_host", verts.size()); - int idx = 0; - for (LO vert : verts) - verts_host(idx++) = vert; - auto verts_device = - Kokkos::View("verts_device", verts.size()); - Kokkos::deep_copy(verts_device, verts_host); - Kokkos::parallel_for( - "ClassifyVerts", Kokkos::RangePolicy<>(0, verts.size()), - ClassifyVertsAndOwnedFunctor(geom, verts_device, owned_, class_dims_, - class_ids_)); - Kokkos::fence(); // Wait for kernel to complete before verts_device is - // destroyed, better would be to optimize the - // reverse_classification data structure to avoid this copy - // and synchronization, but this is simpler for now - } + InitilizeXGCMembersFunctor( + owned_, gids_, class_dims_, class_ids_, + discretization->GetEntityClassificationDimensions(Vertex), + discretization->GetEntityClassificationIds(Vertex))); Kokkos::deep_copy(owned_host_, owned_); Kokkos::deep_copy(gids_host_, gids_); @@ -112,8 +75,7 @@ XGCFieldLayout::XGCFieldLayout( Kokkos::deep_copy(classification_ids_host_, class_ids_); // Copy coordinates to device Kokkos::deep_copy(coords_, 0.0); - discretization_ = std::make_shared(reverse_classification, - num_plane_nodes_); + discretization_ = std::move(discretization); } std::shared_ptr XGCFieldLayout::GetDiscretization() diff --git a/src/pcms/field/layout/xgc.h b/src/pcms/field/layout/xgc.h index 69e62b5c2..8d3e3ff43 100644 --- a/src/pcms/field/layout/xgc.h +++ b/src/pcms/field/layout/xgc.h @@ -4,7 +4,6 @@ #include "pcms/discretization/discretization/xgc.hpp" #include "pcms/discretization/discretization/xgc_reverse_classification.h" #include "pcms/field/field_layout.h" -#include namespace pcms { @@ -13,7 +12,6 @@ class XGCFieldLayout : public FieldLayout { public: XGCFieldLayout(const ReverseClassificationVertex& reverse_classification, - std::function in_overlap, LO num_plane_nodes); std::shared_ptr GetDiscretization() diff --git a/src/pcms/fortranapi/client.i b/src/pcms/fortranapi/client.i index f86dc731e..39ebe3ff3 100644 --- a/src/pcms/fortranapi/client.i +++ b/src/pcms/fortranapi/client.i @@ -30,7 +30,8 @@ enum PcmsType struct PcmsClientHandle { - void* pointer; + void* couplerPointer; + void* appPointer; }; typedef struct PcmsClientHandle PcmsClientHandle; struct PcmsOmegaHMeshHandle @@ -63,11 +64,13 @@ void pcms_destroy_reverse_classification(PcmsReverseClassificationHandle); int pcms_reverse_classification_count_verts(PcmsReverseClassificationHandle); -typedef int8_t (*in_overlap_function)(int, int); PcmsFieldAdapterHandle pcms_create_xgc_field_adapter( const char* name, MPI_Comm plane_comm, void* data, int size, - PcmsType data_type, const PcmsReverseClassificationHandle rc, - in_overlap_function in_overlap); + PcmsType data_type, const PcmsReverseClassificationHandle rc); + +typedef int8_t (*in_overlap_function)(int, int); +void pcms_set_overlap_mask(PcmsClientHandle client_handle, const char* name, + in_overlap_function in_overlap); PcmsFieldAdapterHandle pcms_create_dummy_field_adapter(); diff --git a/src/pcms/fortranapi/client_wrap.c b/src/pcms/fortranapi/client_wrap.c index 5c65d3ef9..7162d3d0e 100644 --- a/src/pcms/fortranapi/client_wrap.c +++ b/src/pcms/fortranapi/client_wrap.c @@ -277,28 +277,61 @@ SWIGINTERN SwigArrayWrapper SwigArrayWrapper_uninitialized() return result; } -SWIGEXPORT void _wrap_PcmsClientHandle_pointer_set(SwigClassWrapper* farg1, - void const** farg2) +SWIGEXPORT void _wrap_PcmsClientHandle_couplerPointer_set( + SwigClassWrapper* farg1, void const** farg2) +{ + struct PcmsClientHandle* arg1 = (struct PcmsClientHandle*)0; + void* arg2 = (void*)0; + + SWIG_check_nonnull(farg1->cptr, "struct PcmsClientHandle *", + "PcmsClientHandle", "PcmsClientHandle::couplerPointer", + return); + arg1 = (struct PcmsClientHandle*)farg1->cptr; + arg2 = (void*)(*farg2); + if (arg1) + (arg1)->couplerPointer = arg2; +} + +SWIGEXPORT void* _wrap_PcmsClientHandle_couplerPointer_get( + SwigClassWrapper* farg1) +{ + void* fresult; + struct PcmsClientHandle* arg1 = (struct PcmsClientHandle*)0; + void* result = 0; + + SWIG_check_nonnull(farg1->cptr, "struct PcmsClientHandle *", + "PcmsClientHandle", "PcmsClientHandle::couplerPointer", + return 0); + arg1 = (struct PcmsClientHandle*)farg1->cptr; + result = (void*)((arg1)->couplerPointer); + fresult = (void*)(result); + return fresult; +} + +SWIGEXPORT void _wrap_PcmsClientHandle_appPointer_set(SwigClassWrapper* farg1, + void const** farg2) { struct PcmsClientHandle* arg1 = (struct PcmsClientHandle*)0; void* arg2 = (void*)0; SWIG_check_nonnull(farg1->cptr, "struct PcmsClientHandle *", - "PcmsClientHandle", "PcmsClientHandle::pointer", return); + "PcmsClientHandle", "PcmsClientHandle::appPointer", + return); arg1 = (struct PcmsClientHandle*)farg1->cptr; arg2 = (void*)(*farg2); if (arg1) (arg1)->appPointer = arg2; } -SWIGEXPORT void* _wrap_PcmsClientHandle_pointer_get(SwigClassWrapper* farg1) +SWIGEXPORT void* _wrap_PcmsClientHandle_appPointer_get(SwigClassWrapper* farg1) { void* fresult; struct PcmsClientHandle* arg1 = (struct PcmsClientHandle*)0; void* result = 0; SWIG_check_nonnull(farg1->cptr, "struct PcmsClientHandle *", - "PcmsClientHandle", "PcmsClientHandle::pointer", return 0); + "PcmsClientHandle", "PcmsClientHandle::appPointer", + return 0); arg1 = (struct PcmsClientHandle*)farg1->cptr; result = (void*)((arg1)->appPointer); fresult = (void*)(result); @@ -672,8 +705,7 @@ SWIGEXPORT int _wrap_pcms_reverse_classification_count_verts( SWIGEXPORT SwigClassWrapper _wrap_pcms_create_xgc_field_adapter( SwigArrayWrapper* farg1, int const* farg2, void const** farg3, - int const* farg4, int const* farg5, SwigClassWrapper* farg6, - in_overlap_function farg7) + int const* farg4, int const* farg5, SwigClassWrapper* farg6) { SwigClassWrapper fresult; char* arg1 = (char*)0; @@ -682,7 +714,6 @@ SWIGEXPORT SwigClassWrapper _wrap_pcms_create_xgc_field_adapter( int arg4; PcmsType arg5; PcmsReverseClassificationHandle arg6; - in_overlap_function arg7 = (in_overlap_function)0; PcmsFieldAdapterHandle result; arg1 = (char*)(farg1->data); @@ -694,16 +725,14 @@ SWIGEXPORT SwigClassWrapper _wrap_pcms_create_xgc_field_adapter( arg3 = (void*)(*farg3); arg4 = (int)(*farg4); arg5 = (PcmsType)(*farg5); - SWIG_check_nonnull( - farg6->cptr, "PcmsReverseClassificationHandle", - "PcmsReverseClassificationHandle", - "pcms_create_xgc_field_adapter(char const *,MPI_Comm,void " - "*,int,PcmsType,PcmsReverseClassificationHandle const,in_overlap_function)", - return SwigClassWrapper_uninitialized()); + SWIG_check_nonnull(farg6->cptr, "PcmsReverseClassificationHandle", + "PcmsReverseClassificationHandle", + "pcms_create_xgc_field_adapter(char const *,MPI_Comm,void " + "*,int,PcmsType,PcmsReverseClassificationHandle const)", + return SwigClassWrapper_uninitialized()); arg6 = *((PcmsReverseClassificationHandle*)(farg6->cptr)); - arg7 = (in_overlap_function)(*farg7); result = pcms_create_xgc_field_adapter((char const*)arg1, arg2, arg3, arg4, - arg5, arg6, arg7); + arg5, arg6); fresult.cptr = (PcmsFieldAdapterHandle*)memcpy( (PcmsFieldAdapterHandle*)calloc(1, sizeof(PcmsFieldAdapterHandle)), &result, sizeof(PcmsFieldAdapterHandle)); @@ -711,6 +740,24 @@ SWIGEXPORT SwigClassWrapper _wrap_pcms_create_xgc_field_adapter( return fresult; } +SWIGEXPORT void _wrap_pcms_set_overlap_mask(SwigClassWrapper* farg1, + SwigArrayWrapper* farg2, + in_overlap_function farg3) +{ + PcmsClientHandle arg1; + char* arg2 = (char*)0; + in_overlap_function arg3 = (in_overlap_function)0; + + SWIG_check_nonnull( + farg1->cptr, "PcmsClientHandle", "PcmsClientHandle", + "pcms_set_overlap_mask(PcmsClientHandle,char const *,in_overlap_function)", + return); + arg1 = *((PcmsClientHandle*)(farg1->cptr)); + arg2 = (char*)(farg2->data); + arg3 = (in_overlap_function)(*farg3); + pcms_set_overlap_mask(arg1, (char const*)arg2, arg3); +} + SWIGEXPORT SwigClassWrapper _wrap_pcms_create_dummy_field_adapter() { SwigClassWrapper fresult; diff --git a/src/pcms/fortranapi/pcms.f90 b/src/pcms/fortranapi/pcms.f90 index 8d431d805..ec4852708 100644 --- a/src/pcms/fortranapi/pcms.f90 +++ b/src/pcms/fortranapi/pcms.f90 @@ -39,8 +39,10 @@ module pcms type, public :: PcmsClientHandle type(SwigClassWrapper), public :: swigdata contains - procedure :: set_pointer => swigf_PcmsClientHandle_pointer_set - procedure :: get_pointer => swigf_PcmsClientHandle_pointer_get + procedure :: set_couplerPointer => swigf_PcmsClientHandle_couplerPointer_set + procedure :: get_couplerPointer => swigf_PcmsClientHandle_couplerPointer_get + procedure :: set_appPointer => swigf_PcmsClientHandle_appPointer_set + procedure :: get_appPointer => swigf_PcmsClientHandle_appPointer_get procedure :: release => swigf_PcmsClientHandle_release procedure, private :: swigf_PcmsClientHandle_op_assign__ generic :: assignment(=) => swigf_PcmsClientHandle_op_assign__ @@ -95,6 +97,7 @@ module pcms public :: pcms_destroy_reverse_classification public :: pcms_reverse_classification_count_verts public :: pcms_create_xgc_field_adapter + public :: pcms_set_overlap_mask public :: pcms_create_dummy_field_adapter public :: pcms_destroy_field_adapter public :: pcms_add_field @@ -140,205 +143,222 @@ function in_overlap_func(dimension, id) & ! WRAPPER DECLARATIONS interface - subroutine swigc_PcmsClientHandle_pointer_set(farg1, farg2) & - bind(C, name = "_wrap_PcmsClientHandle_pointer_set") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR), intent(in) :: farg2 - end subroutine - - function swigc_PcmsClientHandle_pointer_get(farg1) & - bind(C, name = "_wrap_PcmsClientHandle_pointer_get") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR) :: fresult - end function - - function swigc_new_PcmsClientHandle() & - bind(C, name = "_wrap_new_PcmsClientHandle") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper) :: fresult - end function +subroutine swigc_PcmsClientHandle_couplerPointer_set(farg1, farg2) & +bind(C, name="_wrap_PcmsClientHandle_couplerPointer_set") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR), intent(in) :: farg2 +end subroutine - subroutine swigc_delete_PcmsClientHandle(farg1) & - bind(C, name = "_wrap_delete_PcmsClientHandle") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - end subroutine +function swigc_PcmsClientHandle_couplerPointer_get(farg1) & +bind(C, name="_wrap_PcmsClientHandle_couplerPointer_get") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR) :: fresult +end function - subroutine swigc_PcmsClientHandle_op_assign__(farg1, farg2) & - bind(C, name = "_wrap_PcmsClientHandle_op_assign__") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - type(SwigClassWrapper), intent(in) :: farg2 - end subroutine +subroutine swigc_PcmsClientHandle_appPointer_set(farg1, farg2) & +bind(C, name="_wrap_PcmsClientHandle_appPointer_set") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR), intent(in) :: farg2 +end subroutine - subroutine swigc_PcmsOmegaHMeshHandle_pointer_set(farg1, farg2) & - bind(C, name = "_wrap_PcmsOmegaHMeshHandle_pointer_set") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR), intent(in) :: farg2 - end subroutine - - function swigc_PcmsOmegaHMeshHandle_pointer_get(farg1) & - bind(C, name = "_wrap_PcmsOmegaHMeshHandle_pointer_get") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR) :: fresult - end function - - function swigc_new_PcmsOmegaHMeshHandle() & - bind(C, name = "_wrap_new_PcmsOmegaHMeshHandle") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper) :: fresult - end function +function swigc_PcmsClientHandle_appPointer_get(farg1) & +bind(C, name="_wrap_PcmsClientHandle_appPointer_get") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR) :: fresult +end function - subroutine swigc_delete_PcmsOmegaHMeshHandle(farg1) & - bind(C, name = "_wrap_delete_PcmsOmegaHMeshHandle") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - end subroutine +function swigc_new_PcmsClientHandle() & +bind(C, name="_wrap_new_PcmsClientHandle") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper) :: fresult +end function - subroutine swigc_PcmsOmegaHMeshHandle_op_assign__(farg1, farg2) & - bind(C, name = "_wrap_PcmsOmegaHMeshHandle_op_assign__") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - type(SwigClassWrapper), intent(in) :: farg2 - end subroutine +subroutine swigc_delete_PcmsClientHandle(farg1) & +bind(C, name="_wrap_delete_PcmsClientHandle") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +end subroutine - subroutine swigc_PcmsReverseClassificationHandle_pointer_set(farg1, farg2) & - bind(C, name = "_wrap_PcmsReverseClassificationHandle_pointer_set") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR), intent(in) :: farg2 - end subroutine - - function swigc_PcmsReverseClassificationHandle_pointer_get(farg1) & - bind(C, name = "_wrap_PcmsReverseClassificationHandle_pointer_get") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR) :: fresult - end function - - function swigc_new_PcmsReverseClassificationHandle() & - bind(C, name = "_wrap_new_PcmsReverseClassificationHandle") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper) :: fresult - end function +subroutine swigc_PcmsClientHandle_op_assign__(farg1, farg2) & +bind(C, name="_wrap_PcmsClientHandle_op_assign__") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +type(SwigClassWrapper), intent(in) :: farg2 +end subroutine - subroutine swigc_delete_PcmsReverseClassificationHandle(farg1) & - bind(C, name = "_wrap_delete_PcmsReverseClassificationHandle") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - end subroutine +subroutine swigc_PcmsOmegaHMeshHandle_pointer_set(farg1, farg2) & +bind(C, name="_wrap_PcmsOmegaHMeshHandle_pointer_set") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR), intent(in) :: farg2 +end subroutine - subroutine swigc_PcmsReverseClassificationHandle_op_assign__(farg1, farg2) & - bind(C, name = "_wrap_PcmsReverseClassificationHandle_op_assign__") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - type(SwigClassWrapper), intent(in) :: farg2 - end subroutine +function swigc_PcmsOmegaHMeshHandle_pointer_get(farg1) & +bind(C, name="_wrap_PcmsOmegaHMeshHandle_pointer_get") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR) :: fresult +end function - subroutine swigc_PcmsFieldAdapterHandle_pointer_set(farg1, farg2) & - bind(C, name = "_wrap_PcmsFieldAdapterHandle_pointer_set") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR), intent(in) :: farg2 - end subroutine - - function swigc_PcmsFieldAdapterHandle_pointer_get(farg1) & - bind(C, name = "_wrap_PcmsFieldAdapterHandle_pointer_get") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR) :: fresult - end function - - function swigc_new_PcmsFieldAdapterHandle() & - bind(C, name = "_wrap_new_PcmsFieldAdapterHandle") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper) :: fresult - end function +function swigc_new_PcmsOmegaHMeshHandle() & +bind(C, name="_wrap_new_PcmsOmegaHMeshHandle") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper) :: fresult +end function - subroutine swigc_delete_PcmsFieldAdapterHandle(farg1) & - bind(C, name = "_wrap_delete_PcmsFieldAdapterHandle") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - end subroutine +subroutine swigc_delete_PcmsOmegaHMeshHandle(farg1) & +bind(C, name="_wrap_delete_PcmsOmegaHMeshHandle") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +end subroutine - subroutine swigc_PcmsFieldAdapterHandle_op_assign__(farg1, farg2) & - bind(C, name = "_wrap_PcmsFieldAdapterHandle_op_assign__") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - type(SwigClassWrapper), intent(in) :: farg2 - end subroutine +subroutine swigc_PcmsOmegaHMeshHandle_op_assign__(farg1, farg2) & +bind(C, name="_wrap_PcmsOmegaHMeshHandle_op_assign__") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +type(SwigClassWrapper), intent(in) :: farg2 +end subroutine - subroutine swigc_PcmsFieldHandle_pointer_set(farg1, farg2) & - bind(C, name = "_wrap_PcmsFieldHandle_pointer_set") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR), intent(in) :: farg2 - end subroutine - - function swigc_PcmsFieldHandle_pointer_get(farg1) & - bind(C, name = "_wrap_PcmsFieldHandle_pointer_get") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(in) :: farg1 - type(C_PTR) :: fresult - end function - - function swigc_new_PcmsFieldHandle() & - bind(C, name = "_wrap_new_PcmsFieldHandle") & - result(fresult) - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper) :: fresult - end function +subroutine swigc_PcmsReverseClassificationHandle_pointer_set(farg1, farg2) & +bind(C, name="_wrap_PcmsReverseClassificationHandle_pointer_set") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR), intent(in) :: farg2 +end subroutine - subroutine swigc_delete_PcmsFieldHandle(farg1) & - bind(C, name = "_wrap_delete_PcmsFieldHandle") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - end subroutine +function swigc_PcmsReverseClassificationHandle_pointer_get(farg1) & +bind(C, name="_wrap_PcmsReverseClassificationHandle_pointer_get") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR) :: fresult +end function - subroutine swigc_PcmsFieldHandle_op_assign__(farg1, farg2) & - bind(C, name = "_wrap_PcmsFieldHandle_op_assign__") - use, intrinsic :: ISO_C_BINDING - import :: swigclasswrapper - type(SwigClassWrapper), intent(inout) :: farg1 - type(SwigClassWrapper), intent(in) :: farg2 - end subroutine +function swigc_new_PcmsReverseClassificationHandle() & +bind(C, name="_wrap_new_PcmsReverseClassificationHandle") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper) :: fresult +end function + +subroutine swigc_delete_PcmsReverseClassificationHandle(farg1) & +bind(C, name="_wrap_delete_PcmsReverseClassificationHandle") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +end subroutine + +subroutine swigc_PcmsReverseClassificationHandle_op_assign__(farg1, farg2) & +bind(C, name="_wrap_PcmsReverseClassificationHandle_op_assign__") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +type(SwigClassWrapper), intent(in) :: farg2 +end subroutine + +subroutine swigc_PcmsFieldAdapterHandle_pointer_set(farg1, farg2) & +bind(C, name="_wrap_PcmsFieldAdapterHandle_pointer_set") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR), intent(in) :: farg2 +end subroutine + +function swigc_PcmsFieldAdapterHandle_pointer_get(farg1) & +bind(C, name="_wrap_PcmsFieldAdapterHandle_pointer_get") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR) :: fresult +end function + +function swigc_new_PcmsFieldAdapterHandle() & +bind(C, name="_wrap_new_PcmsFieldAdapterHandle") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper) :: fresult +end function + +subroutine swigc_delete_PcmsFieldAdapterHandle(farg1) & +bind(C, name="_wrap_delete_PcmsFieldAdapterHandle") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +end subroutine + +subroutine swigc_PcmsFieldAdapterHandle_op_assign__(farg1, farg2) & +bind(C, name="_wrap_PcmsFieldAdapterHandle_op_assign__") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +type(SwigClassWrapper), intent(in) :: farg2 +end subroutine + +subroutine swigc_PcmsFieldHandle_pointer_set(farg1, farg2) & +bind(C, name="_wrap_PcmsFieldHandle_pointer_set") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR), intent(in) :: farg2 +end subroutine + +function swigc_PcmsFieldHandle_pointer_get(farg1) & +bind(C, name="_wrap_PcmsFieldHandle_pointer_get") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(C_PTR) :: fresult +end function + +function swigc_new_PcmsFieldHandle() & +bind(C, name="_wrap_new_PcmsFieldHandle") & +result(fresult) +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper) :: fresult +end function + +subroutine swigc_delete_PcmsFieldHandle(farg1) & +bind(C, name="_wrap_delete_PcmsFieldHandle") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +end subroutine + +subroutine swigc_PcmsFieldHandle_op_assign__(farg1, farg2) & +bind(C, name="_wrap_PcmsFieldHandle_op_assign__") +use, intrinsic :: ISO_C_BINDING +import :: swigclasswrapper +type(SwigClassWrapper), intent(inout) :: farg1 +type(SwigClassWrapper), intent(in) :: farg2 +end subroutine function swigc_pcms_create_client(farg1, farg2) & bind(C, name="_wrap_pcms_create_client") & @@ -385,7 +405,7 @@ function swigc_pcms_reverse_classification_count_verts(farg1) & integer(C_INT) :: fresult end function -function swigc_pcms_create_xgc_field_adapter(farg1, farg2, farg3, farg4, farg5, farg6, farg7) & +function swigc_pcms_create_xgc_field_adapter(farg1, farg2, farg3, farg4, farg5, farg6) & bind(C, name="_wrap_pcms_create_xgc_field_adapter") & result(fresult) use, intrinsic :: ISO_C_BINDING @@ -397,10 +417,19 @@ function swigc_pcms_create_xgc_field_adapter(farg1, farg2, farg3, farg4, farg5, integer(C_INT), intent(in) :: farg4 integer(C_INT), intent(in) :: farg5 type(SwigClassWrapper), intent(in) :: farg6 -type(C_FUNPTR), value :: farg7 type(SwigClassWrapper) :: fresult end function +subroutine swigc_pcms_set_overlap_mask(farg1, farg2, farg3) & +bind(C, name="_wrap_pcms_set_overlap_mask") +use, intrinsic :: ISO_C_BINDING +import :: swigarraywrapper +import :: swigclasswrapper +type(SwigClassWrapper), intent(in) :: farg1 +type(SwigArrayWrapper) :: farg2 +type(C_FUNPTR), value :: farg3 +end subroutine + function swigc_pcms_create_dummy_field_adapter() & bind(C, name="_wrap_pcms_create_dummy_field_adapter") & result(fresult) @@ -504,315 +533,340 @@ subroutine swigc_pcms_kokkos_finalize() & contains ! MODULE SUBPROGRAMS - subroutine swigf_PcmsClientHandle_pointer_set(self, pointer) - use, intrinsic :: ISO_C_BINDING - class(PcmsClientHandle), intent(in) :: self - type(C_PTR), intent(in) :: pointer - type(SwigClassWrapper) :: farg1 - type(C_PTR) :: farg2 - - farg1 = self%swigdata - farg2 = pointer - call swigc_PcmsClientHandle_pointer_set(farg1, farg2) - end subroutine - - function swigf_PcmsClientHandle_pointer_get(self) & - result(swig_result) - use, intrinsic :: ISO_C_BINDING - type(C_PTR) :: swig_result - class(PcmsClientHandle), intent(in) :: self - type(C_PTR) :: fresult - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - fresult = swigc_PcmsClientHandle_pointer_get(farg1) - swig_result = fresult - end function - - function swigf_new_PcmsClientHandle() & - result(self) - use, intrinsic :: ISO_C_BINDING - type(PcmsClientHandle) :: self - type(SwigClassWrapper) :: fresult +subroutine swigf_PcmsClientHandle_couplerPointer_set(self, couplerpointer) +use, intrinsic :: ISO_C_BINDING +class(PcmsClientHandle), intent(in) :: self +type(C_PTR), intent(in) :: couplerpointer +type(SwigClassWrapper) :: farg1 +type(C_PTR) :: farg2 - fresult = swigc_new_PcmsClientHandle() - self%swigdata = fresult - end function +farg1 = self%swigdata +farg2 = couplerpointer +call swigc_PcmsClientHandle_couplerPointer_set(farg1, farg2) +end subroutine - subroutine swigf_PcmsClientHandle_release(self) - use, intrinsic :: ISO_C_BINDING - class(PcmsClientHandle), intent(inout) :: self - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - if (btest(farg1%cmemflags, swig_cmem_own_bit)) then - call swigc_delete_PcmsClientHandle(farg1) - endif - farg1%cptr = C_NULL_PTR - farg1%cmemflags = 0 - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsClientHandle_op_assign__(self, other) - use, intrinsic :: ISO_C_BINDING - class(PcmsClientHandle), intent(inout) :: self - type(PcmsClientHandle), intent(in) :: other - type(SwigClassWrapper) :: farg1 - type(SwigClassWrapper) :: farg2 - - farg1 = self%swigdata - farg2 = other%swigdata - call swigc_PcmsClientHandle_op_assign__(farg1, farg2) - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsOmegaHMeshHandle_pointer_set(self, pointer) - use, intrinsic :: ISO_C_BINDING - class(PcmsOmegaHMeshHandle), intent(in) :: self - type(C_PTR), intent(in) :: pointer - type(SwigClassWrapper) :: farg1 - type(C_PTR) :: farg2 - - farg1 = self%swigdata - farg2 = pointer - call swigc_PcmsOmegaHMeshHandle_pointer_set(farg1, farg2) - end subroutine - - function swigf_PcmsOmegaHMeshHandle_pointer_get(self) & - result(swig_result) - use, intrinsic :: ISO_C_BINDING - type(C_PTR) :: swig_result - class(PcmsOmegaHMeshHandle), intent(in) :: self - type(C_PTR) :: fresult - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - fresult = swigc_PcmsOmegaHMeshHandle_pointer_get(farg1) - swig_result = fresult - end function - - function swigf_new_PcmsOmegaHMeshHandle() & - result(self) - use, intrinsic :: ISO_C_BINDING - type(PcmsOmegaHMeshHandle) :: self - type(SwigClassWrapper) :: fresult +function swigf_PcmsClientHandle_couplerPointer_get(self) & +result(swig_result) +use, intrinsic :: ISO_C_BINDING +type(C_PTR) :: swig_result +class(PcmsClientHandle), intent(in) :: self +type(C_PTR) :: fresult +type(SwigClassWrapper) :: farg1 - fresult = swigc_new_PcmsOmegaHMeshHandle() - self%swigdata = fresult - end function +farg1 = self%swigdata +fresult = swigc_PcmsClientHandle_couplerPointer_get(farg1) +swig_result = fresult +end function - subroutine swigf_PcmsOmegaHMeshHandle_release(self) - use, intrinsic :: ISO_C_BINDING - class(PcmsOmegaHMeshHandle), intent(inout) :: self - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - if (btest(farg1%cmemflags, swig_cmem_own_bit)) then - call swigc_delete_PcmsOmegaHMeshHandle(farg1) - endif - farg1%cptr = C_NULL_PTR - farg1%cmemflags = 0 - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsOmegaHMeshHandle_op_assign__(self, other) - use, intrinsic :: ISO_C_BINDING - class(PcmsOmegaHMeshHandle), intent(inout) :: self - type(PcmsOmegaHMeshHandle), intent(in) :: other - type(SwigClassWrapper) :: farg1 - type(SwigClassWrapper) :: farg2 - - farg1 = self%swigdata - farg2 = other%swigdata - call swigc_PcmsOmegaHMeshHandle_op_assign__(farg1, farg2) - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsReverseClassificationHandle_pointer_set(self, pointer) - use, intrinsic :: ISO_C_BINDING - class(PcmsReverseClassificationHandle), intent(in) :: self - type(C_PTR), intent(in) :: pointer - type(SwigClassWrapper) :: farg1 - type(C_PTR) :: farg2 - - farg1 = self%swigdata - farg2 = pointer - call swigc_PcmsReverseClassificationHandle_pointer_set(farg1, farg2) - end subroutine - - function swigf_PcmsReverseClassificationHandle_pointer_get(self) & - result(swig_result) - use, intrinsic :: ISO_C_BINDING - type(C_PTR) :: swig_result - class(PcmsReverseClassificationHandle), intent(in) :: self - type(C_PTR) :: fresult - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - fresult = swigc_PcmsReverseClassificationHandle_pointer_get(farg1) - swig_result = fresult - end function - - function swigf_new_PcmsReverseClassificationHandle() & - result(self) - use, intrinsic :: ISO_C_BINDING - type(PcmsReverseClassificationHandle) :: self - type(SwigClassWrapper) :: fresult +subroutine swigf_PcmsClientHandle_appPointer_set(self, apppointer) +use, intrinsic :: ISO_C_BINDING +class(PcmsClientHandle), intent(in) :: self +type(C_PTR), intent(in) :: apppointer +type(SwigClassWrapper) :: farg1 +type(C_PTR) :: farg2 - fresult = swigc_new_PcmsReverseClassificationHandle() - self%swigdata = fresult - end function +farg1 = self%swigdata +farg2 = apppointer +call swigc_PcmsClientHandle_appPointer_set(farg1, farg2) +end subroutine - subroutine swigf_PcmsReverseClassificationHandle_release(self) - use, intrinsic :: ISO_C_BINDING - class(PcmsReverseClassificationHandle), intent(inout) :: self - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - if (btest(farg1%cmemflags, swig_cmem_own_bit)) then - call swigc_delete_PcmsReverseClassificationHandle(farg1) - endif - farg1%cptr = C_NULL_PTR - farg1%cmemflags = 0 - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsReverseClassificationHandle_op_assign__(self, other) - use, intrinsic :: ISO_C_BINDING - class(PcmsReverseClassificationHandle), intent(inout) :: self - type(PcmsReverseClassificationHandle), intent(in) :: other - type(SwigClassWrapper) :: farg1 - type(SwigClassWrapper) :: farg2 - - farg1 = self%swigdata - farg2 = other%swigdata - call swigc_PcmsReverseClassificationHandle_op_assign__(farg1, farg2) - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsFieldAdapterHandle_pointer_set(self, pointer) - use, intrinsic :: ISO_C_BINDING - class(PcmsFieldAdapterHandle), intent(in) :: self - type(C_PTR), intent(in) :: pointer - type(SwigClassWrapper) :: farg1 - type(C_PTR) :: farg2 - - farg1 = self%swigdata - farg2 = pointer - call swigc_PcmsFieldAdapterHandle_pointer_set(farg1, farg2) - end subroutine - - function swigf_PcmsFieldAdapterHandle_pointer_get(self) & - result(swig_result) - use, intrinsic :: ISO_C_BINDING - type(C_PTR) :: swig_result - class(PcmsFieldAdapterHandle), intent(in) :: self - type(C_PTR) :: fresult - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - fresult = swigc_PcmsFieldAdapterHandle_pointer_get(farg1) - swig_result = fresult - end function - - function swigf_new_PcmsFieldAdapterHandle() & - result(self) - use, intrinsic :: ISO_C_BINDING - type(PcmsFieldAdapterHandle) :: self - type(SwigClassWrapper) :: fresult +function swigf_PcmsClientHandle_appPointer_get(self) & +result(swig_result) +use, intrinsic :: ISO_C_BINDING +type(C_PTR) :: swig_result +class(PcmsClientHandle), intent(in) :: self +type(C_PTR) :: fresult +type(SwigClassWrapper) :: farg1 - fresult = swigc_new_PcmsFieldAdapterHandle() - self%swigdata = fresult - end function +farg1 = self%swigdata +fresult = swigc_PcmsClientHandle_appPointer_get(farg1) +swig_result = fresult +end function - subroutine swigf_PcmsFieldAdapterHandle_release(self) - use, intrinsic :: ISO_C_BINDING - class(PcmsFieldAdapterHandle), intent(inout) :: self - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - if (btest(farg1%cmemflags, swig_cmem_own_bit)) then - call swigc_delete_PcmsFieldAdapterHandle(farg1) - endif - farg1%cptr = C_NULL_PTR - farg1%cmemflags = 0 - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsFieldAdapterHandle_op_assign__(self, other) - use, intrinsic :: ISO_C_BINDING - class(PcmsFieldAdapterHandle), intent(inout) :: self - type(PcmsFieldAdapterHandle), intent(in) :: other - type(SwigClassWrapper) :: farg1 - type(SwigClassWrapper) :: farg2 - - farg1 = self%swigdata - farg2 = other%swigdata - call swigc_PcmsFieldAdapterHandle_op_assign__(farg1, farg2) - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsFieldHandle_pointer_set(self, pointer) - use, intrinsic :: ISO_C_BINDING - class(PcmsFieldHandle), intent(in) :: self - type(C_PTR), intent(in) :: pointer - type(SwigClassWrapper) :: farg1 - type(C_PTR) :: farg2 - - farg1 = self%swigdata - farg2 = pointer - call swigc_PcmsFieldHandle_pointer_set(farg1, farg2) - end subroutine - - function swigf_PcmsFieldHandle_pointer_get(self) & - result(swig_result) - use, intrinsic :: ISO_C_BINDING - type(C_PTR) :: swig_result - class(PcmsFieldHandle), intent(in) :: self - type(C_PTR) :: fresult - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - fresult = swigc_PcmsFieldHandle_pointer_get(farg1) - swig_result = fresult - end function - - function swigf_new_PcmsFieldHandle() & - result(self) - use, intrinsic :: ISO_C_BINDING - type(PcmsFieldHandle) :: self - type(SwigClassWrapper) :: fresult +function swigf_new_PcmsClientHandle() & +result(self) +use, intrinsic :: ISO_C_BINDING +type(PcmsClientHandle) :: self +type(SwigClassWrapper) :: fresult - fresult = swigc_new_PcmsFieldHandle() - self%swigdata = fresult - end function +fresult = swigc_new_PcmsClientHandle() +self%swigdata = fresult +end function - subroutine swigf_PcmsFieldHandle_release(self) - use, intrinsic :: ISO_C_BINDING - class(PcmsFieldHandle), intent(inout) :: self - type(SwigClassWrapper) :: farg1 - - farg1 = self%swigdata - if (btest(farg1%cmemflags, swig_cmem_own_bit)) then - call swigc_delete_PcmsFieldHandle(farg1) - endif - farg1%cptr = C_NULL_PTR - farg1%cmemflags = 0 - self%swigdata = farg1 - end subroutine - - subroutine swigf_PcmsFieldHandle_op_assign__(self, other) - use, intrinsic :: ISO_C_BINDING - class(PcmsFieldHandle), intent(inout) :: self - type(PcmsFieldHandle), intent(in) :: other - type(SwigClassWrapper) :: farg1 - type(SwigClassWrapper) :: farg2 +subroutine swigf_PcmsClientHandle_release(self) +use, intrinsic :: ISO_C_BINDING +class(PcmsClientHandle), intent(inout) :: self +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +if (btest(farg1%cmemflags, swig_cmem_own_bit)) then +call swigc_delete_PcmsClientHandle(farg1) +endif +farg1%cptr = C_NULL_PTR +farg1%cmemflags = 0 +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsClientHandle_op_assign__(self, other) +use, intrinsic :: ISO_C_BINDING +class(PcmsClientHandle), intent(inout) :: self +type(PcmsClientHandle), intent(in) :: other +type(SwigClassWrapper) :: farg1 +type(SwigClassWrapper) :: farg2 - farg1 = self%swigdata - farg2 = other%swigdata - call swigc_PcmsFieldHandle_op_assign__(farg1, farg2) - self%swigdata = farg1 - end subroutine +farg1 = self%swigdata +farg2 = other%swigdata +call swigc_PcmsClientHandle_op_assign__(farg1, farg2) +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsOmegaHMeshHandle_pointer_set(self, pointer) +use, intrinsic :: ISO_C_BINDING +class(PcmsOmegaHMeshHandle), intent(in) :: self +type(C_PTR), intent(in) :: pointer +type(SwigClassWrapper) :: farg1 +type(C_PTR) :: farg2 + +farg1 = self%swigdata +farg2 = pointer +call swigc_PcmsOmegaHMeshHandle_pointer_set(farg1, farg2) +end subroutine + +function swigf_PcmsOmegaHMeshHandle_pointer_get(self) & +result(swig_result) +use, intrinsic :: ISO_C_BINDING +type(C_PTR) :: swig_result +class(PcmsOmegaHMeshHandle), intent(in) :: self +type(C_PTR) :: fresult +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +fresult = swigc_PcmsOmegaHMeshHandle_pointer_get(farg1) +swig_result = fresult +end function + +function swigf_new_PcmsOmegaHMeshHandle() & +result(self) +use, intrinsic :: ISO_C_BINDING +type(PcmsOmegaHMeshHandle) :: self +type(SwigClassWrapper) :: fresult + +fresult = swigc_new_PcmsOmegaHMeshHandle() +self%swigdata = fresult +end function + +subroutine swigf_PcmsOmegaHMeshHandle_release(self) +use, intrinsic :: ISO_C_BINDING +class(PcmsOmegaHMeshHandle), intent(inout) :: self +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +if (btest(farg1%cmemflags, swig_cmem_own_bit)) then +call swigc_delete_PcmsOmegaHMeshHandle(farg1) +endif +farg1%cptr = C_NULL_PTR +farg1%cmemflags = 0 +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsOmegaHMeshHandle_op_assign__(self, other) +use, intrinsic :: ISO_C_BINDING +class(PcmsOmegaHMeshHandle), intent(inout) :: self +type(PcmsOmegaHMeshHandle), intent(in) :: other +type(SwigClassWrapper) :: farg1 +type(SwigClassWrapper) :: farg2 + +farg1 = self%swigdata +farg2 = other%swigdata +call swigc_PcmsOmegaHMeshHandle_op_assign__(farg1, farg2) +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsReverseClassificationHandle_pointer_set(self, pointer) +use, intrinsic :: ISO_C_BINDING +class(PcmsReverseClassificationHandle), intent(in) :: self +type(C_PTR), intent(in) :: pointer +type(SwigClassWrapper) :: farg1 +type(C_PTR) :: farg2 + +farg1 = self%swigdata +farg2 = pointer +call swigc_PcmsReverseClassificationHandle_pointer_set(farg1, farg2) +end subroutine + +function swigf_PcmsReverseClassificationHandle_pointer_get(self) & +result(swig_result) +use, intrinsic :: ISO_C_BINDING +type(C_PTR) :: swig_result +class(PcmsReverseClassificationHandle), intent(in) :: self +type(C_PTR) :: fresult +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +fresult = swigc_PcmsReverseClassificationHandle_pointer_get(farg1) +swig_result = fresult +end function + +function swigf_new_PcmsReverseClassificationHandle() & +result(self) +use, intrinsic :: ISO_C_BINDING +type(PcmsReverseClassificationHandle) :: self +type(SwigClassWrapper) :: fresult + +fresult = swigc_new_PcmsReverseClassificationHandle() +self%swigdata = fresult +end function + +subroutine swigf_PcmsReverseClassificationHandle_release(self) +use, intrinsic :: ISO_C_BINDING +class(PcmsReverseClassificationHandle), intent(inout) :: self +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +if (btest(farg1%cmemflags, swig_cmem_own_bit)) then +call swigc_delete_PcmsReverseClassificationHandle(farg1) +endif +farg1%cptr = C_NULL_PTR +farg1%cmemflags = 0 +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsReverseClassificationHandle_op_assign__(self, other) +use, intrinsic :: ISO_C_BINDING +class(PcmsReverseClassificationHandle), intent(inout) :: self +type(PcmsReverseClassificationHandle), intent(in) :: other +type(SwigClassWrapper) :: farg1 +type(SwigClassWrapper) :: farg2 + +farg1 = self%swigdata +farg2 = other%swigdata +call swigc_PcmsReverseClassificationHandle_op_assign__(farg1, farg2) +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsFieldAdapterHandle_pointer_set(self, pointer) +use, intrinsic :: ISO_C_BINDING +class(PcmsFieldAdapterHandle), intent(in) :: self +type(C_PTR), intent(in) :: pointer +type(SwigClassWrapper) :: farg1 +type(C_PTR) :: farg2 + +farg1 = self%swigdata +farg2 = pointer +call swigc_PcmsFieldAdapterHandle_pointer_set(farg1, farg2) +end subroutine + +function swigf_PcmsFieldAdapterHandle_pointer_get(self) & +result(swig_result) +use, intrinsic :: ISO_C_BINDING +type(C_PTR) :: swig_result +class(PcmsFieldAdapterHandle), intent(in) :: self +type(C_PTR) :: fresult +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +fresult = swigc_PcmsFieldAdapterHandle_pointer_get(farg1) +swig_result = fresult +end function + +function swigf_new_PcmsFieldAdapterHandle() & +result(self) +use, intrinsic :: ISO_C_BINDING +type(PcmsFieldAdapterHandle) :: self +type(SwigClassWrapper) :: fresult + +fresult = swigc_new_PcmsFieldAdapterHandle() +self%swigdata = fresult +end function + +subroutine swigf_PcmsFieldAdapterHandle_release(self) +use, intrinsic :: ISO_C_BINDING +class(PcmsFieldAdapterHandle), intent(inout) :: self +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +if (btest(farg1%cmemflags, swig_cmem_own_bit)) then +call swigc_delete_PcmsFieldAdapterHandle(farg1) +endif +farg1%cptr = C_NULL_PTR +farg1%cmemflags = 0 +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsFieldAdapterHandle_op_assign__(self, other) +use, intrinsic :: ISO_C_BINDING +class(PcmsFieldAdapterHandle), intent(inout) :: self +type(PcmsFieldAdapterHandle), intent(in) :: other +type(SwigClassWrapper) :: farg1 +type(SwigClassWrapper) :: farg2 + +farg1 = self%swigdata +farg2 = other%swigdata +call swigc_PcmsFieldAdapterHandle_op_assign__(farg1, farg2) +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsFieldHandle_pointer_set(self, pointer) +use, intrinsic :: ISO_C_BINDING +class(PcmsFieldHandle), intent(in) :: self +type(C_PTR), intent(in) :: pointer +type(SwigClassWrapper) :: farg1 +type(C_PTR) :: farg2 + +farg1 = self%swigdata +farg2 = pointer +call swigc_PcmsFieldHandle_pointer_set(farg1, farg2) +end subroutine + +function swigf_PcmsFieldHandle_pointer_get(self) & +result(swig_result) +use, intrinsic :: ISO_C_BINDING +type(C_PTR) :: swig_result +class(PcmsFieldHandle), intent(in) :: self +type(C_PTR) :: fresult +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +fresult = swigc_PcmsFieldHandle_pointer_get(farg1) +swig_result = fresult +end function + +function swigf_new_PcmsFieldHandle() & +result(self) +use, intrinsic :: ISO_C_BINDING +type(PcmsFieldHandle) :: self +type(SwigClassWrapper) :: fresult + +fresult = swigc_new_PcmsFieldHandle() +self%swigdata = fresult +end function + +subroutine swigf_PcmsFieldHandle_release(self) +use, intrinsic :: ISO_C_BINDING +class(PcmsFieldHandle), intent(inout) :: self +type(SwigClassWrapper) :: farg1 + +farg1 = self%swigdata +if (btest(farg1%cmemflags, swig_cmem_own_bit)) then +call swigc_delete_PcmsFieldHandle(farg1) +endif +farg1%cptr = C_NULL_PTR +farg1%cmemflags = 0 +self%swigdata = farg1 +end subroutine + +subroutine swigf_PcmsFieldHandle_op_assign__(self, other) +use, intrinsic :: ISO_C_BINDING +class(PcmsFieldHandle), intent(inout) :: self +type(PcmsFieldHandle), intent(in) :: other +type(SwigClassWrapper) :: farg1 +type(SwigClassWrapper) :: farg2 + +farg1 = self%swigdata +farg2 = other%swigdata +call swigc_PcmsFieldHandle_op_assign__(farg1, farg2) +self%swigdata = farg1 +end subroutine subroutine SWIGTM_fin_char_Sm_(finp, iminp, temp) @@ -897,7 +951,7 @@ function pcms_reverse_classification_count_verts(arg0) & swig_result = fresult end function -function pcms_create_xgc_field_adapter(name, plane_comm, data, size, data_type, rc, in_overlap) & +function pcms_create_xgc_field_adapter(name, plane_comm, data, size, data_type, rc) & result(swig_result) use, intrinsic :: ISO_C_BINDING type(PcmsFieldAdapterHandle) :: swig_result @@ -907,7 +961,6 @@ function pcms_create_xgc_field_adapter(name, plane_comm, data, size, data_type, integer(C_INT), intent(in) :: size integer(PcmsType), intent(in) :: data_type type(PcmsReverseClassificationHandle), intent(in) :: rc -procedure(in_overlap_func) :: in_overlap type(SwigClassWrapper) :: fresult character(kind=C_CHAR), dimension(:), allocatable, target :: farg1_temp type(SwigArrayWrapper) :: farg1 @@ -916,7 +969,6 @@ function pcms_create_xgc_field_adapter(name, plane_comm, data, size, data_type, integer(C_INT) :: farg4 integer(C_INT) :: farg5 type(SwigClassWrapper) :: farg6 -type(C_FUNPTR) :: farg7 call SWIGTM_fin_char_Sm_(name, farg1, farg1_temp) farg2 = int(plane_comm, C_INT) @@ -924,11 +976,26 @@ function pcms_create_xgc_field_adapter(name, plane_comm, data, size, data_type, farg4 = size farg5 = data_type farg6 = rc%swigdata -farg7 = c_funloc(in_overlap) -fresult = swigc_pcms_create_xgc_field_adapter(farg1, farg2, farg3, farg4, farg5, farg6, farg7) +fresult = swigc_pcms_create_xgc_field_adapter(farg1, farg2, farg3, farg4, farg5, farg6) swig_result%swigdata = fresult end function +subroutine pcms_set_overlap_mask(client_handle, name, in_overlap) +use, intrinsic :: ISO_C_BINDING +type(PcmsClientHandle), intent(in) :: client_handle +character(len=*), target :: name +procedure(in_overlap_func) :: in_overlap +type(SwigClassWrapper) :: farg1 +character(kind=C_CHAR), dimension(:), allocatable, target :: farg2_temp +type(SwigArrayWrapper) :: farg2 +type(C_FUNPTR) :: farg3 + +farg1 = client_handle%swigdata +call SWIGTM_fin_char_Sm_(name, farg2, farg2_temp) +farg3 = c_funloc(in_overlap) +call swigc_pcms_set_overlap_mask(farg1, farg2, farg3) +end subroutine + function pcms_create_dummy_field_adapter() & result(swig_result) use, intrinsic :: ISO_C_BINDING diff --git a/test/test_field_serializer.cpp b/test/test_field_serializer.cpp index 8af516501..267564922 100644 --- a/test/test_field_serializer.cpp +++ b/test/test_field_serializer.cpp @@ -19,6 +19,7 @@ #endif #ifdef PCMS_ENABLE_XGC +#include "pcms/coupler/overlap_mask.h" #include "pcms/field/data/xgc.h" #include "pcms/field/function_space/xgc.h" #endif @@ -92,36 +93,37 @@ TEST_CASE("Serializing an XGC field preserves inactive field entries") { static constexpr int data_size = 16; auto rc = CreateDummyReverseClassification(data_size); - pcms::XGCFieldFactory factory(rc, InXGCOverlap, data_size); + pcms::XGCFieldFactory factory(rc, data_size); std::vector data(data_size); std::iota(data.begin(), data.end(), 0.0); const auto original = data; auto field = factory.CreateField( - "", - std::make_unique>( - factory.GetXGCLayout(), pcms::ValueBasis{}, pcms::make_array_view(data), - MPI_COMM_SELF)); + "", std::make_unique>( + factory.GetXGCLayout(), pcms::ValueBasis{}, + pcms::make_array_view(data), MPI_COMM_SELF)); pcms::FieldSerializer serializer; - auto owned = field.GetLayout().GetOwnedHost(); + // Entries outside the overlap region are not exchanged. + const pcms::OverlapMask overlap_mask(InXGCOverlap); + auto active = overlap_mask.GetMask(field.GetLayout()); std::vector permutation(data_size, -1); int entry = 0; for (int i = 0; i < data_size; ++i) { - if (owned[i]) { + if (active[i]) { permutation[i] = entry++; } } - const int num_owned = entry; - REQUIRE(num_owned == 4); + const int num_active = entry; + REQUIRE(num_active == 4); - std::vector buffer(num_owned, -1.0); + std::vector buffer(num_active, -1.0); REQUIRE(serializer.Serialize(field, pcms::make_array_view(buffer), pcms::make_const_array_view(permutation)) == - num_owned); + num_active); for (int i = 0; i < data_size; ++i) { - if (owned[i]) { + if (active[i]) { REQUIRE(buffer[permutation[i]] == data[i]); buffer[permutation[i]] += 100.0; } else { @@ -135,7 +137,7 @@ TEST_CASE("Serializing an XGC field preserves inactive field entries") auto after = pcms::FlattenToRank1View(field.GetDOFHolderDataHost().GetValues()); for (int i = 0; i < data_size; ++i) { - if (owned[i]) { + if (active[i]) { REQUIRE(after[i] == Catch::Approx(original[i] + 100.0)); } else { REQUIRE(after[i] == Catch::Approx(original[i])); diff --git a/test/test_mfem_adapter.cpp b/test/test_mfem_adapter.cpp index ba4da3d15..ba5b869a0 100644 --- a/test/test_mfem_adapter.cpp +++ b/test/test_mfem_adapter.cpp @@ -63,10 +63,12 @@ TEST_CASE("MFEM vertex-scalar field adapter", "[mfem]") mfem::FunctionCoefficient coeff(LinearField); gf.ProjectCoefficient(coeff); - pcms::MFEMLayout layout(pmesh, pfes, pcms::csys::Cartesian::Deferred()); - pcms::MFEMVertexFieldData data(pfes, gf); + pcms::MFEMFieldFactory factory(pmesh, pfes, gf, + pcms::csys::Cartesian::Deferred(), "mfem"); + auto field = factory.CreateField("data"); + const auto& layout = field.GetLayout(); - auto host = data.GetDOFHolderDataHost(); + auto host = field.GetDOFHolderDataHost().GetValues(); REQUIRE(static_cast(host.extent(0)) == pmesh.GetNV()); REQUIRE(host.extent(1) == 1); @@ -82,9 +84,11 @@ TEST_CASE("MFEM vertex-scalar field adapter", "[mfem]") for (size_t v = 0; v < captured.size(); ++v) { in(v, 0) = captured[v]; } - data.SetDOFHolderDataHost(pcms::MakeConstRank2View(in)); + field.SetDOFHolderDataHost( + pcms::ValueView( + pcms::ValueBasis{}, pcms::MakeConstRank2View(in))); - auto host2 = data.GetDOFHolderDataHost(); + auto host2 = field.GetDOFHolderDataHost().GetValues(); auto owned = layout.GetOwnedHost(); for (size_t v = 0; v < host2.extent(0); ++v) { if (owned[v]) { diff --git a/test/test_proxy_coupling_xgc_client.c b/test/test_proxy_coupling_xgc_client.c index 43546b2d3..91898ab57 100644 --- a/test/test_proxy_coupling_xgc_client.c +++ b/test/test_proxy_coupling_xgc_client.c @@ -67,10 +67,11 @@ int main(int argc, char** argv) int communicating_rank = (i == plane) && (plane_rank == 0); if (plane == i) { field_adapters[i] = pcms_create_xgc_field_adapter( - "adapter1", plane_comm, data, nverts, PCMS_LONG_INT, rc, &in_overlap); + "adapter1", plane_comm, data, nverts, PCMS_LONG_INT, rc); } else { field_adapters[i] = pcms_create_dummy_field_adapter(); } + pcms_set_overlap_mask(client, field_name, &in_overlap); field[i] = pcms_add_field(client, field_name, field_adapters[i], communicating_rank); } diff --git a/test/test_proxy_coupling_xgc_client_fortran.f90 b/test/test_proxy_coupling_xgc_client_fortran.f90 index ab4341410..594a377db 100644 --- a/test/test_proxy_coupling_xgc_client_fortran.f90 +++ b/test/test_proxy_coupling_xgc_client_fortran.f90 @@ -89,10 +89,11 @@ Program main name = 'xgc_gids_plane_' // trim(plane_str) if (plane .eq. ix) then adapters(ix+1) = pcms_create_xgc_field_adapter(trim(name), MPI_COMM_SELF, c_loc(data_pointer), & - size(data_pointer), PCMS_LONG_INT, reverse_classification, in_overlap) + size(data_pointer), PCMS_LONG_INT, reverse_classification) else adapters(ix+1) = pcms_create_dummy_field_adapter() end if + call pcms_set_overlap_mask(client, trim(name), in_overlap) if ((plane_rank .eq. 0) .and. (ix .eq. plane)) then ! field participates in the communication fields(ix+1) = pcms_add_field(client, trim(name), adapters(ix+1), 1) diff --git a/test/test_proxy_coupling_xgc_server.cpp b/test/test_proxy_coupling_xgc_server.cpp index 351d5f81a..b7d067547 100644 --- a/test/test_proxy_coupling_xgc_server.cpp +++ b/test/test_proxy_coupling_xgc_server.cpp @@ -49,18 +49,18 @@ void xgc_coupler(MPI_Comm comm, Omega_h::Mesh& mesh, std::string_view cpn_file) data[i].resize(mesh.nverts()); std::stringstream ss; ss << "xgc_gids_plane_" << i; + application->SetLayoutOverlapMask( + ss.str(), std::make_unique(ts::IsModelEntInOverlap{})); // FIXME: The current C/Fortran proxy API couples layout registration to // field registration, so each XGC plane is registered as a separate layout // communicator even though the layouts are geometrically identical. auto function_space = - pcms::XGCFieldFactory(rc, ts::IsModelEntInOverlap{}, - static_cast(mesh.nverts()), ss.str()); + pcms::XGCFieldFactory(rc, static_cast(mesh.nverts()), ss.str()); auto field = function_space.CreateField( ss.str(), std::make_unique>( function_space.GetXGCLayout(), pcms::ValueBasis{}, make_array_view(data[i]), comm)); - fields.push_back( - application->AddField(std::move(field))); + fields.push_back(application->AddField(std::move(field))); } do { @@ -176,8 +176,7 @@ void omegah_coupler(MPI_Comm comm, Omega_h::Mesh& mesh, auto field = factory->CreateFunction( ss.str(), std::make_unique>( factory->GetLayout(), pcms::ValueBasis{})); - fields.push_back( - application->AddField(std::move(field))); + fields.push_back(application->AddField(std::move(field))); } do { application->ReceivePhase([&]() { diff --git a/test/test_proxy_coupling_xgc_server_overlap.cpp b/test/test_proxy_coupling_xgc_server_overlap.cpp index 39d4a1cd3..4fd3eba1c 100644 --- a/test/test_proxy_coupling_xgc_server_overlap.cpp +++ b/test/test_proxy_coupling_xgc_server_overlap.cpp @@ -48,25 +48,22 @@ void xgc_coupler_with_overlap(MPI_Comm comm, Omega_h::Mesh& mesh, std::stringstream ss; ss << "xgc_gids_plane_" << i; - auto overlap_mask = std::make_unique( - mesh.nverts(), [](int dim, int id) -> int8_t { + auto overlap_mask = + std::make_unique([](int dim, int id) -> int8_t { return ts::IsModelEntInOverlap{}(dim, id); }); application->SetLayoutOverlapMask(ss.str(), std::move(overlap_mask)); auto function_space = - pcms::XGCFieldFactory(rc, ts::IsModelEntInOverlap{}, - static_cast(mesh.nverts()), ss.str()); + pcms::XGCFieldFactory(rc, static_cast(mesh.nverts()), ss.str()); auto field = function_space.CreateField( ss.str(), std::make_unique>( function_space.GetXGCLayout(), pcms::ValueBasis{}, make_array_view(data[i]), comm)); - - fields.push_back( - application->AddField(std::move(field))); + fields.push_back(application->AddField(std::move(field))); } do { @@ -165,8 +162,8 @@ void omegah_coupler_with_overlap(MPI_Comm comm, Omega_h::Mesh& mesh, std::stringstream ss; ss << "xgc_gids_plane_" << i; - auto overlap_mask = std::make_unique( - mesh.nverts(), [](int dim, int id) -> int8_t { + auto overlap_mask = + std::make_unique([](int dim, int id) -> int8_t { return ts::IsModelEntInOverlap{}(dim, id); }); @@ -180,9 +177,7 @@ void omegah_coupler_with_overlap(MPI_Comm comm, Omega_h::Mesh& mesh, ss.str(), std::make_unique>( factory->GetLayout(), pcms::ValueBasis{})); - - fields.push_back( - application->AddField(std::move(field))); + fields.push_back(application->AddField(std::move(field))); } do { diff --git a/test/test_xgc_field_data.cpp b/test/test_xgc_field_data.cpp index b694c3b99..55a89d593 100644 --- a/test/test_xgc_field_data.cpp +++ b/test/test_xgc_field_data.cpp @@ -1,5 +1,6 @@ #include #include "pcms/field/data/xgc.h" +#include "pcms/coupler/overlap_mask.h" #include "pcms/field/function_space/xgc.h" #include @@ -26,28 +27,25 @@ bool in_overlap(int, int id) } // namespace -TEST_CASE("XGC FieldLayout marks overlap entries and gids") +TEST_CASE("XGC FieldLayout owns and classifies every entry") { static constexpr int data_size = 16; auto rc = create_dummy_rc(data_size); - pcms::XGCFieldLayout layout(rc, in_overlap, data_size); + pcms::XGCFieldLayout layout(rc, data_size); auto owned = layout.GetOwnedHost(); auto gids = layout.GetGidsHost(); auto class_dims = layout.GetDOFHolderClassificationDimensionsHost(); auto class_ids = layout.GetDOFHolderClassificationIdsHost(); + const pcms::OverlapMask overlap_mask(in_overlap); + auto overlap = overlap_mask.GetMask(layout); for (int i = 0; i < data_size; ++i) { REQUIRE(gids[i] == i + 1); - if (i % 4 == 0) { - REQUIRE(owned[i]); - REQUIRE(class_dims[i] == 0); - REQUIRE(class_ids[i] == 0); - } else { - REQUIRE(!owned[i]); - REQUIRE(class_dims[i] == -1); - REQUIRE(class_ids[i] == -1); - } + REQUIRE(owned[i]); + REQUIRE(class_dims[i] == 0); + REQUIRE(class_ids[i] == (i % 4 == 0 ? 0 : 1)); + REQUIRE(overlap[i] == (i % 4 == 0)); } } @@ -55,7 +53,7 @@ TEST_CASE("XGCFieldFactory creates fields and rejects evaluator access") { static constexpr int data_size = 16; auto rc = create_dummy_rc(data_size); - pcms::XGCFieldFactory function_space(rc, in_overlap, data_size); + pcms::XGCFieldFactory function_space(rc, data_size); std::vector data(data_size); std::iota(data.begin(), data.end(), 0.0); From 8421904d60f7673276f1e5e8c293d6043d07bb52 Mon Sep 17 00:00:00 2001 From: Jacob Merson Date: Sun, 25 Oct 2026 22:08:07 +0000 Subject: [PATCH 3/3] add support for poloidal planes and empty layout This is laying the groundwork for evaluation only types so we can support FusionIO and NIMROD sources. --- src/pcms/field/coordinate_map.cpp | 34 +++++ src/pcms/field/coordinate_map.hpp | 20 +++ src/pcms/field/layout/empty.cpp | 10 +- src/pcms/field/layout/empty.h | 7 +- test/CMakeLists.txt | 3 +- test/test_evaluation_only_space.cpp | 189 ++++++++++++++++++++++++++++ 6 files changed, 258 insertions(+), 5 deletions(-) create mode 100644 test/test_evaluation_only_space.cpp diff --git a/src/pcms/field/coordinate_map.cpp b/src/pcms/field/coordinate_map.cpp index c4778fe48..512b91ceb 100644 --- a/src/pcms/field/coordinate_map.cpp +++ b/src/pcms/field/coordinate_map.cpp @@ -185,6 +185,22 @@ class BoundCylindricalRotation final : public BoundCoordinateMap Real sign_; }; +// (R, Z) -> (R, theta0, Z). A free function: CUDA does not allow device +// lambdas in protected members such as BindImpl. +void EmbedPoloidalPoints(Rank2View in, + Kokkos::View out, + Real theta0) +{ + Kokkos::parallel_for( + "embed_poloidal_points", + Kokkos::RangePolicy(0, static_cast(in.extent(0))), + KOKKOS_LAMBDA(const LO i) { + out(i, 0) = in(i, 0); + out(i, 1) = theta0; + out(i, 2) = in(i, 1); + }); +} + /// Values run opposite to points: a map into cylindrical coordinates rotates /// cylindrical components into Cartesian ones, and vice versa. std::unique_ptr BindCylindrical( @@ -307,4 +323,22 @@ std::unique_ptr CylindricalToCartesian::BindImpl( PointDirection::ToCartesian); } +PoloidalPlaneEmbedding::PoloidalPlaneEmbedding(Real theta0) + : CoordinateMap(csys::CylindricalRZ::Create(), + csys::CylindricalRThetaZ::Create()), + theta0_(theta0) +{ +} + +std::unique_ptr PoloidalPlaneEmbedding::BindImpl( + const CoordinateView& query_points) const +{ + const auto in = query_points.GetValues(); + auto mapped = AllocateCoordinates(in.extent(0), 3); + EmbedPoloidalPoints(in, mapped, theta0_); + // The base bound map re-expresses no vector values; scalars never reach it. + return std::make_unique(GetTargetCoordinateSystem(), + std::move(mapped)); +} + } // namespace pcms diff --git a/src/pcms/field/coordinate_map.hpp b/src/pcms/field/coordinate_map.hpp index 389c1a880..529460bed 100644 --- a/src/pcms/field/coordinate_map.hpp +++ b/src/pcms/field/coordinate_map.hpp @@ -118,6 +118,26 @@ class CylindricalToCartesian final : public CoordinateMap const CoordinateView& query_points) const override; }; +// Places points of a poloidal (R, Z) plane at a fixed toroidal angle: +// (R, Z) -> (R, theta0, Z). This lets a 2D CylindricalRZ target query a 3D +// CylindricalRThetaZ source through a TransformedSource. +// +// Only scalar values (including multi-component scalars) are currently +// supported. +class PoloidalPlaneEmbedding final : public CoordinateMap +{ +public: + /// @param theta0 toroidal angle of the plane, in radians + explicit PoloidalPlaneEmbedding(Real theta0); + +protected: + [[nodiscard]] std::unique_ptr BindImpl( + const CoordinateView& query_points) const override; + +private: + Real theta0_; +}; + } // namespace pcms #endif // PCMS_FIELD_COORDINATE_MAP_HPP diff --git a/src/pcms/field/layout/empty.cpp b/src/pcms/field/layout/empty.cpp index e7d201540..cdd6dc0b4 100644 --- a/src/pcms/field/layout/empty.cpp +++ b/src/pcms/field/layout/empty.cpp @@ -26,7 +26,7 @@ int RequireDimension(const std::shared_ptr& system) } // namespace EmptyFieldLayout::EmptyFieldLayout( - std::shared_ptr system) + std::shared_ptr system, int num_components) : owned_("null_owned", 0), gids_("null_gids", 0), class_dims_("null_class_dims", 0), @@ -35,8 +35,12 @@ EmptyFieldLayout::EmptyFieldLayout( gids_host_("null_gids_host", 0), classification_dims_host_("null_classification_dims_host", 0), classification_ids_host_("null_classification_ids_host", 0), - coords_("null_coords", 0, RequireDimension(system)) + coords_("null_coords", 0, RequireDimension(system)), + num_components_(num_components) { + if (num_components_ < 1) { + throw pcms_error("EmptyFieldLayout: num_components must be positive"); + } SetCoordinateSystem(std::move(system)); discretization_ = std::make_shared(); } @@ -49,7 +53,7 @@ std::shared_ptr EmptyFieldLayout::GetDiscretization() int EmptyFieldLayout::GetNumComponents() const { - return 1; + return num_components_; } LO EmptyFieldLayout::GetNumOwnedDofHolder() const diff --git a/src/pcms/field/layout/empty.h b/src/pcms/field/layout/empty.h index 77d00c35b..c3e129701 100644 --- a/src/pcms/field/layout/empty.h +++ b/src/pcms/field/layout/empty.h @@ -11,7 +11,11 @@ namespace pcms class EmptyFieldLayout : public FieldLayout { public: - explicit EmptyFieldLayout(std::shared_ptr system); + /// @param system coordinate system of the (absent) DOF holders + /// @param num_components components per value of fields on this layout; + /// fields whose values live outside PCMS still declare their width + explicit EmptyFieldLayout(std::shared_ptr system, + int num_components = 1); std::shared_ptr GetDiscretization() const noexcept override; @@ -41,6 +45,7 @@ class EmptyFieldLayout : public FieldLayout Kokkos::View classification_ids_host_; Kokkos::View coords_; std::shared_ptr discretization_; + int num_components_; }; } // namespace pcms diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 217b577fa..97eee9023 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -434,7 +434,8 @@ if(Catch2_FOUND) test_omega_h_lagrange_field.cpp test_point_evaluator.cpp test_tagged_outputs.cpp - test_transformed_source.cpp) + test_transformed_source.cpp + test_evaluation_only_space.cpp) endif() if(PCMS_ENABLE_MFEM) diff --git a/test/test_evaluation_only_space.cpp b/test/test_evaluation_only_space.cpp new file mode 100644 index 000000000..5440ee5da --- /dev/null +++ b/test/test_evaluation_only_space.cpp @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "field_test_utils.h" + +using Catch::Matchers::ContainsSubstring; +using Catch::Matchers::WithinAbs; +using pcms::DeviceMemorySpace; +using pcms::LO; +using pcms::Real; +using pcms::ValueBasis; + +// An evaluation-only source, the pattern external codes (NIMROD, Fusion-IO) +// follow: a FunctionSpace on an EmptyFieldLayout whose field data names a +// quantity but provides no DOFAccess. Here the quantity is analytic: +// component c of f at (R, theta, Z) is 2R + 3 theta - Z + c. +namespace +{ + +KOKKOS_INLINE_FUNCTION Real MockValue(Real r, Real theta, Real z, int c) +{ + return 2.0 * r + 3.0 * theta - z + c; +} + +class MockData final : public pcms::FieldData +{ +public: + pcms::FieldValueType GetValueType() const override + { + return pcms::FieldValueType::Scalar; + } + const ValueBasis& GetValueBasis() const override { return basis_; } + +private: + ValueBasis basis_; +}; + +class MockEvaluator final : public pcms::PointEvaluator +{ +public: + explicit MockEvaluator(const pcms::EvaluationRequest& request) + : points_("mock_points", request.coords.GetValues().extent(0), + request.coords.GetValues().extent(1)) + { + pcms::CopyDeviceRank2ViewToRank2View(pcms::MakeRank2View(points_), + request.coords.GetValues()); + } + + void Evaluate(const pcms::Field& field, + pcms::ValueView values) const override + { + CheckEvaluateWriteTag("MockEvaluator", field, values); + if (dynamic_cast(&field.GetData()) == nullptr) { + throw pcms::pcms_error("MockEvaluator: not a field of this source"); + } + const auto out = values.GetValues(); + const auto points = points_; + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, static_cast(points.extent(0))), + KOKKOS_LAMBDA(const LO i) { + for (size_t c = 0; c < out.extent(1); ++c) { + out(i, c) = + MockValue(points(i, 0), points(i, 1), points(i, 2), int(c)); + } + }); + } + + Kokkos::View FilledPoints() const override + { + return {}; + } + +private: + Kokkos::View points_; +}; + +class MockSpace final : public pcms::FunctionSpace +{ +public: + static std::shared_ptr Create(int num_components) + { + return std::shared_ptr(new MockSpace(num_components)); + } + + std::shared_ptr GetLayout() const noexcept override + { + return layout_; + } + +protected: + pcms::FieldVariant CreateFieldImpl(pcms::Type, ValueBasis) const override + { + throw pcms::pcms_error("MockSpace: pass the data naming the quantity"); + } + + pcms::FieldVariant CreateFieldImpl(pcms::FieldDataVariant data) const override + { + auto& real = std::get>>(data); + if (dynamic_cast(real.get()) == nullptr) { + throw pcms::pcms_error("MockSpace: requires MockData"); + } + return WrapField(layout_, std::move(real)); + } + + pcms::PointEvaluatorVariant CreatePointEvaluatorImpl( + pcms::Type, const pcms::EvaluationRequest& request) const override + { + return std::unique_ptr>( + std::make_unique(request)); + } + +private: + explicit MockSpace(int num_components) + : layout_(std::make_shared( + pcms::csys::CylindricalRThetaZ::Create(), num_components)) + { + } + + std::shared_ptr layout_; +}; + +std::shared_ptr MakePoloidalTarget( + Omega_h::Mesh& mesh, int num_components) +{ + return pcms::LagrangeFunctionSpace::FromMesh( + mesh, 1, num_components, pcms::csys::CylindricalRZ::Create(), "global", + pcms::LagrangeFunctionSpace::Backend::OmegaH); +} + +} // namespace + +TEST_CASE("Evaluation-only space interpolates onto a poloidal plane") +{ + auto lib = Omega_h::Library{}; + auto mesh = pcms::test::BuildUnitSquare(lib, 1); + const int num_components = GENERATE(1, 2); + auto source = MockSpace::Create(num_components); + auto target = MakePoloidalTarget(mesh, num_components); + const Real theta0 = -0.3; + pcms::TransformedSource on_plane( + source, std::make_shared(theta0)); + pcms::Interpolator interpolator(on_plane, *target); + + auto f = source->CreateFunction("f", std::make_unique()); + auto g = target->CreateFunction("g"); + interpolator.Apply(f, g); + + const auto coords = pcms::test::CopyCoordinatesToHost( + target->GetLayout()->GetDOFHolderCoordinates().GetValues()); + const auto result = g.GetDOFHolderDataHost().GetValues(); + for (size_t i = 0; i < coords.extent(0); ++i) { + for (int c = 0; c < num_components; ++c) { + CAPTURE(i, c); + REQUIRE_THAT(result(i, c), + WithinAbs(MockValue(coords(i, 0), theta0, coords(i, 1), c), + pcms::test::ExactTol)); + } + } + + // The embedding maps points only, so vector values cannot pass through it. + auto evaluator = on_plane.CreatePointEvaluator( + pcms::EvaluationRequest::FromFunctionSpace(*target)); + REQUIRE_THROWS_WITH( + evaluator->OutputBasis(ValueBasis{pcms::csys::CylindricalRThetaZ::Create(), + pcms::ComponentScaling::Physical, + pcms::values::Vector}), + ContainsSubstring("cannot re-express")); +} + +TEST_CASE("Functions of an evaluation-only space hold no values") +{ + auto source = MockSpace::Create(1); + auto f = source->CreateFunction("f", std::make_unique()); + REQUIRE(f.GetLayout().GetNumOwnedDofHolder() == 0); + REQUIRE_THROWS_WITH(f.GetDOFHolderData(), ContainsSubstring("no DOFAccess")); + REQUIRE_THROWS_WITH(source->CreateFunction("default"), + ContainsSubstring("naming the quantity")); +}