Program Listing for File field_factory.h

↰ Return to documentation for file (src/pcms/field/field_factory.h)

#ifndef PCMS_FIELD_FACTORY_H
#define PCMS_FIELD_FACTORY_H

#include "field.h"
#include "field_data.h"
#include "field_layout.h"
#include "pcms/field/value_view.hpp"
#include "pcms/utility/types.h"
#include <memory>
#include <optional>
#include <string>

namespace pcms
{

namespace detail
{

inline size_t ExpectedFlatFieldDataSize(const FieldLayout& layout)
{
  return static_cast<size_t>(layout.GetNumOwnedDofHolder()) *
         static_cast<size_t>(layout.GetNumComponents());
}

} // namespace detail

// Compile-time gate: true only for the five supported field value types.
template <typename T>
inline constexpr bool is_supported_field_type_v =
  std::is_same_v<T, int8_t> || std::is_same_v<T, int32_t> ||
  std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
  std::is_same_v<T, double>;

// FieldFactory constructs Field<T> bundles ({layout, data}) with no evaluation
// capability and no shared-ownership requirement. Comm-only backends (e.g.
// XGCFieldFactory) derive it directly. FunctionSpace is a separate abstraction
// (not a FieldFactory) that additionally supports evaluation and produces
// Functions; a FieldFactory can never be passed where a FunctionSpace is
// required, which is the compile-time form of "comm-only cannot be evaluated".
class FieldFactory
{
public:
  virtual std::shared_ptr<const FieldLayout> GetLayout() const noexcept = 0;

  virtual ~FieldFactory() noexcept = default;

  template <typename T>
  [[nodiscard]] Field<T> CreateField(
    std::string name = "", VarianceSignature variance = values::Scalar,
    std::optional<ComponentScaling> scaling = std::nullopt) const;

  template <typename T>
  [[nodiscard]] Field<T> CreateField(
    std::string name, VarianceSignature variance,
    std::shared_ptr<const CoordinateSystem> system,
    std::optional<ComponentScaling> scaling = std::nullopt) const;

  // Expert API: wrap externally constructed field data into a Field. The
  // concrete factory validates backend-specific field-data type and storage
  // size compatibility.
  template <typename T>
  [[nodiscard]] Field<T> CreateField(std::string name,
                                     std::unique_ptr<FieldData<T>> data) const;

protected:
  template <typename T>
  static Field<T> WrapField(std::shared_ptr<const FieldLayout> layout,
                            std::unique_ptr<FieldData<T>> data)
  {
    return Field<T>(std::string{}, std::move(layout), std::move(data));
  }

  virtual FieldVariant CreateFieldImpl(Type storage_type,
                                       ValueBasis basis) const = 0;

  virtual FieldVariant CreateFieldImpl(FieldDataVariant data) const = 0;
};

template <typename T>
Field<T> FieldFactory::CreateField(
  std::string name, VarianceSignature variance,
  std::optional<ComponentScaling> scaling) const
{
  return CreateField<T>(std::move(name), variance, nullptr, scaling);
}

template <typename T>
Field<T> FieldFactory::CreateField(
  std::string name, VarianceSignature variance,
  std::shared_ptr<const CoordinateSystem> system,
  std::optional<ComponentScaling> scaling) const
{
  static_assert(is_supported_field_type_v<T>,
                "T is not a supported field type");
  ValueBasis basis = detail::MakeValueBasis(
    GetLayout()->GetCoordinateSystem(), variance, std::move(system), scaling);
  detail::ValidateValueSemantics(basis, GetLayout()->GetNumComponents());
  Field<T> field = std::get<Field<T>>(
    CreateFieldImpl(TypeEnumFromType<T>(), std::move(basis)));
  field.name_ = std::move(name);
  return field;
}

template <typename T>
Field<T> FieldFactory::CreateField(std::string name,
                                   std::unique_ptr<FieldData<T>> data) const
{
  static_assert(is_supported_field_type_v<T>,
                "T is not a supported field type");
  if (!data) {
    throw pcms_error("FieldFactory::CreateField: data must not be null");
  }
  detail::ValidateValueSemantics(data->GetValueBasis(),
                                 GetLayout()->GetNumComponents());
  Field<T> field =
    std::get<Field<T>>(CreateFieldImpl(FieldDataVariant{std::move(data)}));
  field.name_ = std::move(name);
  return field;
}

} // namespace pcms

#endif // PCMS_FIELD_FACTORY_H