Program Listing for File state.h

Return to documentation for file (include/immersx/core/state.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_state_h
#define immersx_state_h

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

#include <immersx/core/field.h>

#include <functional>
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>

namespace ImmersX
{
  template <typename VectorType>
  class StateAccessor
  {
  public:
    virtual ~StateAccessor() = default;

    virtual const VectorType &
    field(FieldId field_id, double time) const = 0;
  };

  template <typename VectorType>
  class StateView : public StateAccessor<VectorType>
  {
  public:
    StateView(const StateLayout &layout, const double time)
      : layout_(layout)
      , time_(time)
      , fields_(layout.n_fields(), nullptr)
    {}

    StateView &
    bind(const FieldId field_id, const VectorType &values)
    {
      validate_field(field_id);
      AssertThrow(fields_[field_id.value()] == nullptr,
                  dealii::ExcMessage("A state field may only be bound once."));
      fields_[field_id.value()] = &values;
      return *this;
    }

    StateView &
    bind(const FieldId, VectorType &&) = delete;

    StateView &
    bind(const FieldId, const VectorType &&) = delete;

    const VectorType &
    field(const FieldId field_id, const double requested_time) const override
    {
      validate_field(field_id);
      AssertThrow(requested_time == time_,
                  dealii::ExcMessage(
                    "StateView does not provide a different time; "
                    "use a history-backed StateAccessor instead."));
      AssertThrow(fields_[field_id.value()] != nullptr,
                  dealii::ExcMessage(
                    "The requested state field is not bound."));
      return *fields_[field_id.value()];
    }

    double
    time() const
    {
      return time_;
    }

  private:
    void
    validate_field(const FieldId field_id) const
    {
      AssertThrow(layout_.contains(field_id),
                  dealii::ExcMessage("Field identifier is not in the state "
                                     "layout."));
    }

    const StateLayout              &layout_;
    double                          time_;
    std::vector<const VectorType *> fields_;
  };

  enum class TermTreatment
  {
    all,
    explicit_term,
    implicit_term
  };

  class TermSelection
  {
  public:
    static TermSelection
    all()
    {
      return TermSelection();
    }

    static TermSelection
    none()
    {
      TermSelection result;
      result.all_terms_ = false;
      return result;
    }

    static TermSelection
    only(const std::string &term)
    {
      TermSelection result = none();
      result.selected_terms_.insert(term);
      return result;
    }

    TermSelection &
    set(const std::string &term, const TermTreatment treatment)
    {
      treatments_[term] = treatment;
      if (!all_terms_)
        selected_terms_.insert(term);
      return *this;
    }

    TermSelection &
    include(const std::string &term)
    {
      all_terms_ = false;
      selected_terms_.insert(term);
      return *this;
    }

    TermSelection &
    exclude(const std::string &term)
    {
      all_terms_ = false;
      selected_terms_.erase(term);
      return *this;
    }

    bool
    contains(const std::string &term) const
    {
      return all_terms_ || selected_terms_.find(term) != selected_terms_.end();
    }

    TermTreatment
    treatment(const std::string &term) const
    {
      const auto it = treatments_.find(term);
      return it == treatments_.end() ? TermTreatment::all : it->second;
    }

    bool
    includes(const std::string &term, const TermTreatment requested) const
    {
      if (!contains(term))
        return false;
      const auto selected = treatment(term);
      return selected == TermTreatment::all ||
             requested == TermTreatment::all || selected == requested;
    }

  private:
    bool                                 all_terms_ = true;
    std::set<std::string>                selected_terms_;
    std::map<std::string, TermTreatment> treatments_;
  };

  template <typename VectorType>
  class EvaluationContext
  {
  public:
    using HistoryQuery = std::function<VectorType(HistoryGroupId, double)>;

    EvaluationContext(
      const double                     time,
      const StateAccessor<VectorType> &state,
      const StateAccessor<VectorType> *state_derivative = nullptr,
      TermSelection                    terms            = {},
      HistoryQuery                     history_query    = {})
      : time_(time)
      , state_(state)
      , state_derivative_(state_derivative)
      , terms_(std::move(terms))
      , history_query_(std::move(history_query))
    {}

    EvaluationContext(double,
                      StateAccessor<VectorType> &&,
                      const StateAccessor<VectorType> * = nullptr,
                      TermSelection                     = {},
                      HistoryQuery                      = {}) = delete;

    EvaluationContext(double,
                      const StateAccessor<VectorType> &&,
                      const StateAccessor<VectorType> * = nullptr,
                      TermSelection                     = {},
                      HistoryQuery                      = {}) = delete;

    double
    time() const
    {
      return time_;
    }

    const StateAccessor<VectorType> &
    state() const
    {
      return state_;
    }

    const StateAccessor<VectorType> *
    state_derivative() const
    {
      return state_derivative_;
    }

    const VectorType &
    state(const FieldId field_id) const
    {
      return state_.field(field_id, time_);
    }

    const VectorType &
    derivative(const FieldId field_id) const
    {
      AssertThrow(state_derivative_ != nullptr,
                  dealii::ExcMessage(
                    "This evaluation has no state derivative."));
      return state_derivative_->field(field_id, time_);
    }

    bool
    has_state_derivative() const
    {
      return state_derivative_ != nullptr;
    }

    const TermSelection &
    terms() const
    {
      return terms_;
    }

    bool
    has_history() const
    {
      return static_cast<bool>(history_query_);
    }

    VectorType
    historical_state(const HistoryGroupId group, const double time) const
    {
      AssertThrow(history_query_,
                  dealii::ExcMessage(
                    "No state history query is attached to this evaluation "
                    "context."));
      return history_query_(group, time);
    }

  private:
    double                           time_;
    const StateAccessor<VectorType> &state_;
    const StateAccessor<VectorType> *state_derivative_;
    TermSelection                    terms_;
    HistoryQuery                     history_query_;
  };

} // namespace ImmersX

#endif // immersx_state_h