Skip to content

Commit 3e64a1f

Browse files
committed
Fix the CreateFunction / CreateField split
With Function<T> being the object that handles evaluatable fields and Field<T> being the non-evaluatable object that can be used for sending over the network. This commit makes the function naming/usage consisent.
1 parent b1c2edd commit 3e64a1f

36 files changed

Lines changed: 473 additions & 455 deletions

src/pcms/capi/client.cpp

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "client.h"
22
#include "pcms.h"
3+
#include "pcms/field/field_factory.h"
34
#include "pcms/field/function_space/xgc.h"
45
#include "pcms/field/data/xgc.h"
56
#include "pcms/field/layout/xgc.h"
@@ -23,11 +24,11 @@ namespace detail
2324
template <typename T>
2425
struct XGCFieldRegistration
2526
{
26-
XGCFunctionSpace function_space;
27+
XGCFieldFactory function_space;
2728
MPI_Comm plane_comm;
2829
Rank1View<T, HostMemorySpace> data;
2930

30-
XGCFieldRegistration(XGCFunctionSpace fs, MPI_Comm comm,
31+
XGCFieldRegistration(XGCFieldFactory fs, MPI_Comm comm,
3132
Rank1View<T, HostMemorySpace> d)
3233
: function_space(std::move(fs)), plane_comm(comm), data(d)
3334
{
@@ -37,10 +38,10 @@ struct XGCFieldRegistration
3738
struct DummyFieldRegistration
3839
{};
3940

40-
class EmptyFunctionSpace : public FunctionSpace
41+
class EmptyFieldFactory : public FieldFactory
4142
{
4243
public:
43-
explicit EmptyFunctionSpace(std::string layout_name = "")
44+
explicit EmptyFieldFactory(std::string layout_name = "")
4445
{
4546
auto layout = std::make_shared<EmptyFieldLayout>();
4647
layout->SetName(std::move(layout_name));
@@ -53,11 +54,6 @@ class EmptyFunctionSpace : public FunctionSpace
5354
return layout_;
5455
}
5556

56-
[[nodiscard]] CoordinateSystem GetCoordinateSystem() const noexcept override
57-
{
58-
return CoordinateSystem::Cartesian;
59-
}
60-
6157
protected:
6258
[[nodiscard]] FieldVariant CreateFieldImpl(
6359
Type value_type, FieldMetadata metadata) const override
@@ -80,25 +76,19 @@ class EmptyFunctionSpace : public FunctionSpace
8076
PCMS_ALWAYS_ASSERT(fd != nullptr);
8177
if (dynamic_cast<const SimpleFieldData<T>*>(fd.get()) == nullptr) {
8278
throw pcms_error(
83-
"EmptyFunctionSpace::CreateField: requires SimpleFieldData");
79+
"EmptyFieldFactory::CreateField: requires SimpleFieldData");
8480
}
8581
if (fd->GetDOFHolderDataHost().size() !=
8682
detail::ExpectedFlatFieldDataSize(*layout_)) {
8783
throw pcms_error(
88-
"EmptyFunctionSpace::CreateField: field data size does not match "
84+
"EmptyFieldFactory::CreateField: field data size does not match "
8985
"layout");
9086
}
9187
return WrapField<T>(layout_, std::forward<decltype(fd)>(fd));
9288
},
9389
std::move(data));
9490
}
9591

96-
[[nodiscard]] PointEvaluatorVariant CreatePointEvaluatorImpl(
97-
Type /*value_type*/, const EvaluationRequest& /*request*/) const override
98-
{
99-
throw pcms_error("EmptyFunctionSpace does not support point evaluation");
100-
}
101-
10292
private:
10393
std::shared_ptr<const EmptyFieldLayout> layout_;
10494
};
@@ -153,7 +143,7 @@ inline ClientState::HandleVariant RegisterField(
153143
Application& app, std::string name, const detail::DummyFieldRegistration&,
154144
bool participates)
155145
{
156-
auto function_space = detail::EmptyFunctionSpace{name};
146+
auto function_space = detail::EmptyFieldFactory{name};
157147
auto field =
158148
function_space.CreateField<int>(std::move(name), FieldMetadata{});
159149
std::unique_ptr<FieldSerializer<int>> serializer =
@@ -258,7 +248,7 @@ void pcms_create_xgc_field_adapter_t(
258248
{
259249
PCMS_ALWAYS_ASSERT((size > 0) ? (data != nullptr) : true);
260250
auto function_space =
261-
pcms::XGCFunctionSpace(reverse_classification, in_overlap, size);
251+
pcms::XGCFieldFactory(reverse_classification, in_overlap, size);
262252
pcms::Rank1View<T, pcms::HostMemorySpace> data_view(
263253
reinterpret_cast<T*>(data), size);
264254
field_adapter.emplace<pcms::detail::XGCFieldRegistration<T>>(

src/pcms/field/eqdsk_field.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ namespace pcms
1212

1313
struct EQDSKField
1414
{
15-
SplineFunctionSpace space;
15+
std::shared_ptr<SplineFunctionSpace> space;
1616
Field<Real> field;
1717
};
1818

1919
// TODD: @jacobmerson replace with Function.
2020
struct EQDSKFieldWithData
2121
{
2222
EQDSKData data;
23-
SplineFunctionSpace space;
23+
std::shared_ptr<SplineFunctionSpace> space;
2424
Field<Real> field;
2525
};
2626

@@ -43,7 +43,7 @@ inline EQDSKField MakeEQDSKField(const EQDSKData& eqdsk_data)
4343
{
4444
auto space = SplineFunctionSpace::FromUniformGrid(
4545
eqdsk_data.grid, CoordinateSystem::Cartesian);
46-
auto field = space.CreateField<Real>();
46+
Field<Real> field = space->CreateFunction<Real>();
4747
field.GetData().SetDOFHolderData(Rank2View<const Real, DeviceMemorySpace>(
4848
eqdsk_data.PSIZR.data(), eqdsk_data.PSIZR.extent(0), 1));
4949
return {std::move(space), std::move(field)};

src/pcms/field/field.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ class Field
6868
{
6969
}
7070

71-
// FunctionSpace constructs Fields (via WrapField) and moves a Field's name,
72-
// layout and data out to build a Function (via CreateFunction).
71+
// FieldFactory constructs Fields (via WrapField); FunctionSpace constructs
72+
// Fields the same way and moves a Field's name, layout and data out to build
73+
// a Function (via CreateFunction). Both stamp the field name.
74+
friend class FieldFactory;
7375
friend class FunctionSpace;
7476

7577
private:

src/pcms/field/field_factory.h

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#ifndef PCMS_FIELD_FACTORY_H
2+
#define PCMS_FIELD_FACTORY_H
3+
4+
#include "field.h"
5+
#include "field_data.h"
6+
#include "field_layout.h"
7+
#include "field_metadata.h"
8+
#include "pcms/utility/types.h"
9+
#include <memory>
10+
#include <string>
11+
12+
namespace pcms
13+
{
14+
15+
namespace detail
16+
{
17+
18+
inline size_t ExpectedFlatFieldDataSize(const FieldLayout& layout)
19+
{
20+
return static_cast<size_t>(layout.GetNumOwnedDofHolder()) *
21+
static_cast<size_t>(layout.GetNumComponents());
22+
}
23+
24+
} // namespace detail
25+
26+
// Compile-time gate: true only for the five supported field value types.
27+
template <typename T>
28+
inline constexpr bool is_supported_field_type_v =
29+
std::is_same_v<T, int8_t> || std::is_same_v<T, int32_t> ||
30+
std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
31+
std::is_same_v<T, double>;
32+
33+
// FieldFactory constructs Field<T> bundles ({layout, data}) with no evaluation
34+
// capability and no shared-ownership requirement. Comm-only backends (e.g.
35+
// XGCFieldFactory) derive it directly. FunctionSpace is a separate abstraction
36+
// (not a FieldFactory) that additionally supports evaluation and produces
37+
// Functions; a FieldFactory can never be passed where a FunctionSpace is
38+
// required, which is the compile-time form of "comm-only cannot be evaluated".
39+
class FieldFactory
40+
{
41+
public:
42+
virtual std::shared_ptr<const FieldLayout> GetLayout() const noexcept = 0;
43+
44+
virtual ~FieldFactory() noexcept = default;
45+
46+
// Create a new named field with freshly allocated data. The name is optional
47+
// (empty by default) and identifies the field to consumers such as a coupler.
48+
template <typename T>
49+
[[nodiscard]] Field<T> CreateField(std::string name = "",
50+
FieldMetadata metadata = {}) const;
51+
52+
// Expert API: wrap externally constructed field data into a Field. The
53+
// concrete factory validates backend-specific field-data type and storage
54+
// size compatibility.
55+
template <typename T>
56+
[[nodiscard]] Field<T> CreateField(std::string name,
57+
std::unique_ptr<FieldData<T>> data) const;
58+
59+
protected:
60+
template <typename T>
61+
static Field<T> WrapField(std::shared_ptr<const FieldLayout> layout,
62+
std::unique_ptr<FieldData<T>> data)
63+
{
64+
return Field<T>(std::string{}, std::move(layout), std::move(data));
65+
}
66+
67+
virtual FieldVariant CreateFieldImpl(Type value_type,
68+
FieldMetadata metadata) const = 0;
69+
70+
virtual FieldVariant CreateFieldImpl(FieldDataVariant data) const = 0;
71+
};
72+
73+
template <typename T>
74+
Field<T> FieldFactory::CreateField(std::string name, FieldMetadata metadata) const
75+
{
76+
static_assert(is_supported_field_type_v<T>,
77+
"T is not a supported field type");
78+
Field<T> field =
79+
std::get<Field<T>>(CreateFieldImpl(TypeEnumFromType<T>(), metadata));
80+
field.name_ = std::move(name);
81+
return field;
82+
}
83+
84+
template <typename T>
85+
Field<T> FieldFactory::CreateField(std::string name,
86+
std::unique_ptr<FieldData<T>> data) const
87+
{
88+
static_assert(is_supported_field_type_v<T>,
89+
"T is not a supported field type");
90+
if (!data) {
91+
throw pcms_error("FieldFactory::CreateField: data must not be null");
92+
}
93+
Field<T> field =
94+
std::get<Field<T>>(CreateFieldImpl(FieldDataVariant{std::move(data)}));
95+
field.name_ = std::move(name);
96+
return field;
97+
}
98+
99+
} // namespace pcms
100+
101+
#endif // PCMS_FIELD_FACTORY_H

src/pcms/field/function_space.h

Lines changed: 20 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "evaluation_request.h"
66
#include "field.h"
77
#include "field_data.h"
8-
#include "field_evaluator_factory.h"
8+
#include "field_factory.h"
99
#include "field_layout.h"
1010
#include "field_metadata.h"
1111
#include "out_of_bounds_policy.h"
@@ -20,37 +20,17 @@
2020
namespace pcms
2121
{
2222

23-
namespace detail
24-
{
25-
26-
inline size_t ExpectedFlatFieldDataSize(const FieldLayout& layout)
27-
{
28-
return static_cast<size_t>(layout.GetNumOwnedDofHolder()) *
29-
static_cast<size_t>(layout.GetNumComponents());
30-
}
31-
32-
} // namespace detail
33-
34-
// Compile-time gate: true only for the five supported field value types.
35-
template <typename T>
36-
inline constexpr bool is_supported_field_type_v =
37-
std::is_same_v<T, int8_t> || std::is_same_v<T, int32_t> ||
38-
std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
39-
std::is_same_v<T, double>;
40-
4123
// FunctionSpace is an abstract interface representing an evaluatable field
42-
// space: layout, evaluation rules, and coordinate interpretation.
24+
// space: layout, evaluation rules, and coordinate interpretation. It produces
25+
// Functions (a Field bound to this space).
4326
//
4427
// Concrete implementations (e.g. LagrangeFunctionSpace) provide backends for
45-
// specific discretizations or mesh types.
46-
//
47-
// FunctionSpace is used as the parameter type for operation objects such as
48-
// Interpolator<T>, so that operations are not coupled to a specific backend.
49-
//
50-
// FunctionSpace inherits enable_shared_from_this because CreateFunction stamps
51-
// a shared_ptr<const FunctionSpace> into each Function it produces. Concrete
52-
// spaces must therefore be owned by a shared_ptr (the From* factories return
53-
// one); calling CreateFunction on a stack-allocated space throws bad_weak_ptr.
28+
// specific discretizations or mesh types, and their From* factories return a
29+
// shared_ptr. A Function co-owns its space via shared_ptr (obtained through
30+
// enable_shared_from_this), which is why concrete spaces are always
31+
// shared-owned; a stack-allocated FunctionSpace is not constructible (the
32+
// backend constructors are private and reachable only via the shared_ptr
33+
// From* factories).
5434
class FunctionSpace : public std::enable_shared_from_this<FunctionSpace>
5535
{
5636
public:
@@ -66,28 +46,13 @@ class FunctionSpace : public std::enable_shared_from_this<FunctionSpace>
6646

6747
virtual ~FunctionSpace() noexcept = default;
6848

69-
// Create a new named field with freshly allocated data for this function
70-
// space. The name is optional (empty by default) and identifies the field to
71-
// consumers such as a coupler. Compile-time error for unsupported T; runtime
72-
// error for T unsupported by the concrete backend.
73-
template <typename T>
74-
[[nodiscard]] Field<T> CreateField(std::string name = "",
75-
FieldMetadata metadata = {}) const;
76-
77-
// Expert API: wrap externally constructed field data into a Field for this
78-
// function space. The concrete function space validates backend-specific
79-
// field-data type and storage size compatibility.
80-
template <typename T>
81-
[[nodiscard]] Field<T> CreateField(std::string name,
82-
std::unique_ptr<FieldData<T>> data) const;
83-
84-
// Like CreateField, but returns a Function that retains a shared reference to
85-
// this space (so it can be evaluated / used to build transfer operators).
86-
// Requires this space to be owned by a shared_ptr (see class note).
8749
template <typename T>
8850
[[nodiscard]] Function<T> CreateFunction(std::string name = "",
8951
FieldMetadata metadata = {}) const;
9052

53+
// Expert API: wrap externally constructed field data into a Function for this
54+
// space. The concrete space validates backend-specific field-data type and
55+
// storage size compatibility.
9156
template <typename T>
9257
[[nodiscard]] Function<T> CreateFunction(
9358
std::string name, std::unique_ptr<FieldData<T>> data) const;
@@ -132,70 +97,30 @@ class FunctionSpace : public std::enable_shared_from_this<FunctionSpace>
13297
Type value_type, const EvaluationRequest& request) const = 0;
13398
};
13499

135-
// Wrap a by-value FunctionSpace (typically the result of a From* factory) in a
136-
// shared_ptr so it can produce Functions and participate in transfers via
137-
// AddFunction/CreateTransfer. Ordinary by-value use — CreateField, point
138-
// evaluation, constructing a transfer operator directly — does not need this;
139-
// only Function (which retains a shared_ptr<const FunctionSpace>) does.
140-
//
141-
// auto space = pcms::Share(LagrangeFunctionSpace::FromMesh(...));
142-
// auto fn = space->CreateFunction<Real>();
143-
template <typename Space>
144-
[[nodiscard]] std::shared_ptr<Space> Share(Space&& space)
145-
{
146-
static_assert(
147-
std::is_base_of_v<FunctionSpace, std::remove_reference_t<Space>>,
148-
"Share expects a FunctionSpace-derived type");
149-
return std::make_shared<std::remove_reference_t<Space>>(
150-
std::forward<Space>(space));
151-
}
152-
153100
template <typename T>
154-
Field<T> FunctionSpace::CreateField(std::string name,
155-
FieldMetadata metadata) const
101+
Function<T> FunctionSpace::CreateFunction(std::string name,
102+
FieldMetadata metadata) const
156103
{
157104
static_assert(is_supported_field_type_v<T>,
158105
"T is not a supported field type");
159106
Field<T> field =
160107
std::get<Field<T>>(CreateFieldImpl(TypeEnumFromType<T>(), metadata));
161-
field.name_ = std::move(name);
162-
return field;
108+
return WrapFunction<T>(std::move(name), std::move(field.layout_),
109+
std::move(field.data_), shared_from_this());
163110
}
164111

165112
template <typename T>
166-
Field<T> FunctionSpace::CreateField(std::string name,
167-
std::unique_ptr<FieldData<T>> data) const
113+
Function<T> FunctionSpace::CreateFunction(
114+
std::string name, std::unique_ptr<FieldData<T>> data) const
168115
{
169116
static_assert(is_supported_field_type_v<T>,
170117
"T is not a supported field type");
171118
if (!data) {
172-
throw pcms_error("FunctionSpace::CreateField: data must not be null");
119+
throw pcms_error("FunctionSpace::CreateFunction: data must not be null");
173120
}
174121
Field<T> field =
175122
std::get<Field<T>>(CreateFieldImpl(FieldDataVariant{std::move(data)}));
176-
field.name_ = std::move(name);
177-
return field;
178-
}
179-
180-
template <typename T>
181-
Function<T> FunctionSpace::CreateFunction(std::string name,
182-
FieldMetadata metadata) const
183-
{
184-
static_assert(is_supported_field_type_v<T>,
185-
"T is not a supported field type");
186-
Field<T> field = CreateField<T>(std::move(name), metadata);
187-
return WrapFunction<T>(std::move(field.name_), std::move(field.layout_),
188-
std::move(field.data_), shared_from_this());
189-
}
190-
191-
template <typename T>
192-
Function<T> FunctionSpace::CreateFunction(
193-
std::string name, std::unique_ptr<FieldData<T>> data) const
194-
{
195-
static_assert(is_supported_field_type_v<T>,
196-
"T is not a supported field type");
197-
Field<T> field = CreateField<T>(std::move(name), std::move(data));
198-
return WrapFunction<T>(std::move(field.name_), std::move(field.layout_),
123+
return WrapFunction<T>(std::move(name), std::move(field.layout_),
199124
std::move(field.data_), shared_from_this());
200125
}
201126

0 commit comments

Comments
 (0)