Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 91 additions & 1 deletion src/pcms/coupler/coupler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
#include "pcms/utility/assert.h"
#include "pcms/utility/common.h"
#include "pcms/utility/profile.h"
#include "pcms/transfer/transfer_operator.hpp"
#include <cstddef>
#include <memory>
#include <vector>

namespace pcms
{

class Application;
class Coupler;

template <typename T>
class FieldHandle
Expand All @@ -40,7 +44,7 @@ class FieldHandle

// FunctionHandle is a FieldHandle that additionally carries the function space,
// so it can be evaluated / used to build transfer operators. Only AddFunction
// produces one; CreateTransfer accepts FunctionHandle (not FieldHandle), which
// produces one; AddTransfer accepts FunctionHandle (not FieldHandle), which
// makes passing a comm-only field to a transfer a compile error.
template <typename T>
class FunctionHandle : public FieldHandle<T>
Expand All @@ -66,6 +70,62 @@ class FunctionHandle : public FieldHandle<T>
std::shared_ptr<const FunctionSpace> space_;
};

// Transfer is the non-templated base for a runnable transfer, owned by the
// Coupler. Run() takes no typed arguments (the operator's source/target fields
// are bound in the concrete BoundTransfer), so it erases the value type T and
// lets the coupler hold a heterogeneous set of transfers uniformly.
class Transfer
{
public:
virtual void Run() const = 0;
virtual ~Transfer() noexcept = default;
};

// BoundTransfer binds a built transfer operator to the source/target functions
// it connects. Owned by the Coupler (as a Transfer) and referenced through a
// TransferHandle; the expensive build happened in AddTransfer, Run() is the
// cheap per-step Apply.
template <typename T>
class BoundTransfer : public Transfer
{
public:
BoundTransfer(FunctionHandle<T> source, FunctionHandle<T> target,
std::unique_ptr<TransferOperator<T>> op)
: source_(std::move(source)),
target_(std::move(target)),
operator_(std::move(op))
{
}

void Run() const override
{
PCMS_FUNCTION_TIMER;
operator_->Apply(source_.GetField(), target_.GetField());
}

private:
FunctionHandle<T> source_;
FunctionHandle<T> target_;
std::unique_ptr<TransferOperator<T>> operator_;
};

// Lightweight, copyable reference to a coupler-owned transfer (mirrors
// FieldHandle). Run() dispatches to the owning Coupler.
class TransferHandle
{
public:
void Run() const;

private:
TransferHandle(Coupler* coupler, std::size_t id) : coupler_(coupler), id_(id)
{
}
friend class Coupler;

Coupler* coupler_;
std::size_t id_;
};

class Application
{
public:
Expand Down Expand Up @@ -249,12 +309,22 @@ class Coupler
return redev_.GetPartition();
}

// Build and register a transfer from source to target using the given method
// recipe (see pcms/transfer/transfer_method.hpp). The coupler owns the built
// operator; the returned handle's Run() applies it in the coupling loop.
template <typename T, typename Method>
TransferHandle AddTransfer(FunctionHandle<T> source, FunctionHandle<T> target,
const Method& method);

void RunTransfer(std::size_t id) { transfers_.at(id)->Run(); }

private:
std::string name_;
MPI_Comm mpi_comm_;
redev::Redev redev_;
// gather and scatter operations have reference to internal fields
std::map<std::string, Application> applications_;
std::vector<std::unique_ptr<Transfer>> transfers_;
};

} // namespace pcms
Expand Down Expand Up @@ -385,4 +455,24 @@ pcms::FunctionHandle<T> pcms::Application::AddFunction(
return FunctionHandle<T>{this, std::move(name), std::move(space)};
}

inline void pcms::TransferHandle::Run() const
{
PCMS_ALWAYS_ASSERT(coupler_ != nullptr);
coupler_->RunTransfer(id_);
}

template <typename T, typename Method>
pcms::TransferHandle pcms::Coupler::AddTransfer(FunctionHandle<T> source,
FunctionHandle<T> target,
const Method& method)
{
PCMS_FUNCTION_TIMER;
std::unique_ptr<TransferOperator<T>> op =
method.Build(source.GetSpace(), target.GetSpace());
const std::size_t id = transfers_.size();
transfers_.push_back(std::make_unique<BoundTransfer<T>>(
std::move(source), std::move(target), std::move(op)));
return TransferHandle{this, id};
}

#endif // COUPLER2_H_
97 changes: 97 additions & 0 deletions src/pcms/transfer/transfer_method.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#ifndef PCMS_TRANSFER_METHOD_H
#define PCMS_TRANSFER_METHOD_H

#include "pcms/transfer/transfer_operator.hpp"
#include "pcms/transfer/copy.h"
#include "pcms/transfer/interpolator.h"
#include "pcms/transfer/omega_h_conservative_projection.hpp"
#include "pcms/transfer/omega_h_control_variate_projection.hpp"
#include "pcms/transfer/monte_carlo_sampling.hpp"
#include "pcms/field/out_of_bounds_policy.h"
#include "pcms/utility/types.h"
#include <cstdint>
#include <memory>

namespace pcms
{

class FunctionSpace;

// A "transfer method" is a parameter-carrying recipe: it owns the
// method-specific parameters, and its
//
// std::unique_ptr<TransferOperator<T>> Build(const FunctionSpace& source,
// const FunctionSpace& target)
// const
//
// member turns a space pair into a transfer operator. Selecting or comparing
// methods is then a one-line change at the AddTransfer call site, e.g.
// AddTransfer(src, tgt,
// method::ConservativeMonteCarlo{.samples_per_element = 64});
//
// Methods are aggregates (no polymorphic base) so their parameters can be set
// with designated initializers; they are matched by AddTransfer as a
// duck-typed concept rather than a base class. The value type T is enforced
// through Build's return type: a Real-only method will not compile against
// FunctionHandle<float>, and templated methods work for any supported T.
//
// The recipes live in `pcms::method` so their short names (e.g. `Copy`) do not
// collide with the transfer-operator types they wrap.
namespace method
{

// Identity copy; source and target must share a layout. Any supported T.
template <typename T>
struct Copy
{
std::unique_ptr<TransferOperator<T>> Build(const FunctionSpace& source,
const FunctionSpace& target) const
{
return std::make_unique<pcms::Copy<T>>(source, target);
}
};

// Point interpolation: evaluate the source field at the target DOF sites. Any
// supported T.
template <typename T>
struct Interpolation
{
OutOfBoundsPolicy policy{};

std::unique_ptr<TransferOperator<T>> Build(const FunctionSpace& source,
const FunctionSpace& target) const
{
return std::make_unique<Interpolator<T>>(source, target, policy);
}
};

// Conservative Galerkin (L2) projection via mesh intersection. Real only.
struct ConservativeIntersection
{
std::unique_ptr<TransferOperator<Real>> Build(
const FunctionSpace& source, const FunctionSpace& target) const
{
return std::make_unique<OmegaHConservativeProjection>(source, target);
}
};

// Conservative projection with a Monte-Carlo / control-variate RHS. Real only.
struct ConservativeMonteCarlo
{
int samples_per_element = 32;
MonteCarloSampling sampling = MonteCarloSampling::UniformRandom;
std::uint64_t seed = 8675309;

std::unique_ptr<TransferOperator<Real>> Build(
const FunctionSpace& source, const FunctionSpace& target) const
{
return std::make_unique<OmegaHControlVariateProjection>(
source, target, samples_per_element, sampling, seed);
}
};

} // namespace method

} // namespace pcms

#endif // PCMS_TRANSFER_METHOD_H
41 changes: 41 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,47 @@ if(PCMS_ENABLE_OMEGA_H)
${d3d1p}
ignored)
endif()
if(PCMS_ENABLE_PETSC AND PCMS_ENABLE_MESHFIELDS)
add_executable(coupler_transfer test_coupler_transfer.cpp)
target_link_libraries(coupler_transfer PUBLIC pcms::core test_support)
if(HOST_NPROC GREATER_EQUAL 4)
tri_mpi_test(
TESTNAME
test_coupler_transfer_4p
TIMEOUT
60
NAME1
rdv
EXE1
./coupler_transfer
PROCS1
2
ARGS1
-1
${d3d1p}
${d3d2p_cpn}
NAME2
source
EXE2
./coupler_transfer
PROCS2
1
ARGS2
0
${d3d1p}
ignored
NAME3
target
EXE3
./coupler_transfer
PROCS3
1
ARGS3
1
${d3d1p}
ignored)
endif()
endif()
if(HOST_NPROC GREATER_EQUAL 26)
tri_mpi_test(
TESTNAME
Expand Down
Loading