Program Listing for File field.h

Return to documentation for file (include/immersx/core/field.h)

// ---------------------------------------------------------------------
//
// Copyright (C) 2026 by Luca Heltai
//
// This file is part of the ImmersX application, based on
// the deal.II library.
//
// The ImmersX application is free software; you can use
// it, redistribute it, and/or modify it under the terms of the Apache-2.0
// License WITH LLVM-exception (the "License"); either version 3.0 of the
// License, or (at your option) any later version. The full text of the
// license can be found in the LICENSE.md file at the top level of the
// ImmersX distribution.
//
// ---------------------------------------------------------------------

#ifndef immersx_field_h
#define immersx_field_h

#include <deal.II/base/exceptions.h>
#include <deal.II/base/index_set.h>

#include <cstddef>
#include <limits>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

namespace ImmersX
{
  class FieldId
  {
  public:
    inline static constexpr std::size_t invalid_value =
      std::numeric_limits<std::size_t>::max();

    constexpr FieldId() = default;

    explicit constexpr FieldId(const std::size_t value)
      : value_(value)
    {}

    constexpr std::size_t
    value() const
    {
      return value_;
    }

    constexpr bool
    is_valid() const
    {
      return value_ != invalid_value;
    }

    friend constexpr bool
    operator==(const FieldId left, const FieldId right)
    {
      return left.value_ == right.value_;
    }

    friend constexpr bool
    operator!=(const FieldId left, const FieldId right)
    {
      return !(left == right);
    }

    friend constexpr bool
    operator<(const FieldId left, const FieldId right)
    {
      return left.value_ < right.value_;
    }

  private:
    std::size_t value_ = invalid_value;
  };

  class HistoryGroupId
  {
  public:
    inline static constexpr std::size_t invalid_value =
      std::numeric_limits<std::size_t>::max();

    constexpr HistoryGroupId() = default;

    explicit constexpr HistoryGroupId(const std::size_t value)
      : value_(value)
    {}

    constexpr std::size_t
    value() const
    {
      return value_;
    }

    constexpr bool
    is_valid() const
    {
      return value_ != invalid_value;
    }

    friend constexpr bool
    operator==(const HistoryGroupId left, const HistoryGroupId right)
    {
      return left.value_ == right.value_;
    }

    friend constexpr bool
    operator!=(const HistoryGroupId left, const HistoryGroupId right)
    {
      return !(left == right);
    }

    friend constexpr bool
    operator<(const HistoryGroupId left, const HistoryGroupId right)
    {
      return left.value_ < right.value_;
    }

  private:
    std::size_t value_ = invalid_value;
  };

  struct FieldDescriptor
  {
    std::string      name;
    HistoryGroupId   history_group;
    dealii::IndexSet locally_owned;
    dealii::IndexSet locally_relevant;
    dealii::IndexSet differential_components;
  };

  class StateLayout
  {
  public:
    FieldId
    add_field(FieldDescriptor descriptor)
    {
      AssertThrow(!descriptor.name.empty(),
                  dealii::ExcMessage("A field must have a name."));
      AssertThrow(!has_field(descriptor.name),
                  dealii::ExcMessage("Duplicate field name '" +
                                     descriptor.name + "'."));
      AssertThrow(descriptor.differential_components.size() == 0 ||
                    descriptor.differential_components.size() ==
                      descriptor.locally_owned.size(),
                  dealii::ExcMessage(
                    "Differential components must have the same global size "
                    "as the field vector."));
      if (descriptor.differential_components.size() == 0)
        descriptor.differential_components =
          dealii::IndexSet(descriptor.locally_owned.size());

      const FieldId id(fields_.size());
      field_names_.emplace(descriptor.name, id);
      fields_.push_back(std::move(descriptor));
      return id;
    }

    bool
    has_field(const std::string &name) const
    {
      return field_names_.find(name) != field_names_.end();
    }

    FieldId
    field_id(const std::string &name) const
    {
      const auto it = field_names_.find(name);
      AssertThrow(it != field_names_.end(),
                  dealii::ExcMessage("Unknown field name '" + name + "'."));
      return it->second;
    }

    bool
    contains(const FieldId id) const
    {
      return id.is_valid() && id.value() < fields_.size();
    }

    const FieldDescriptor &
    field(const FieldId id) const
    {
      AssertThrow(contains(id),
                  dealii::ExcMessage("Field identifier is not in the state "
                                     "layout."));
      return fields_[id.value()];
    }

    std::size_t
    n_fields() const
    {
      return fields_.size();
    }

  private:
    std::vector<FieldDescriptor>             fields_;
    std::unordered_map<std::string, FieldId> field_names_;
  };
} // namespace ImmersX

#endif // immersx_field_h