/**
 * @file   field_collection.cc
 *
 * @author Till Junge <till.junge@altermail.ch>
 *
 * @date   11 Aug 2019
 *
 * @brief  Implementations for field collections
 *
 * Copyright © 2019 Till Junge
 *
 * µGrid is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3, or (at
 * your option) any later version.
 *
 * µGrid is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with µGrid; see the file COPYING. If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Additional permission under GNU GPL version 3 section 7
 *
 * If you modify this Program, or any covered work, by linking or combining it
 * with proprietary FFT implementations or numerical libraries, containing parts
 * covered by the terms of those libraries' licenses, the licensors of this
 * Program grant you additional permission to convey the resulting work.
 *
 */

#include "collection/field_collection.hh"
#include "field/field.hh"
#include "field/state_field.hh"
#include "field/field_typed.hh"

namespace muGrid {

    /* ---------------------------------------------------------------------- */
    template <class DefaultDestroyable>
    void FieldDestructor<DefaultDestroyable>::operator()(
        DefaultDestroyable * field) {
        delete field;
    }

    /* ---------------------------------------------------------------------- */
    template struct FieldDestructor<Field>;
    template struct FieldDestructor<StateField>;

    /* ---------------------------------------------------------------------- */
    FieldCollection::FieldCollection(ValidityDomain domain,
                                     Dim_t spatial_dimension,
                                     const SubPtMap_t & nb_sub_pts,
                                     StorageOrder storage_order, Device device)
        : domain{domain}, spatial_dim{spatial_dimension},
          nb_sub_pts{nb_sub_pts},
          // Use StructureOfArrays storage order for device memory
          // to ensure optimal memory coalescing on GPU
          storage_order{device.is_device() ? StorageOrder::StructureOfArrays
                                           : storage_order},
          device{device} {
        this->set_nb_sub_pts(PixelTag, 1);
    }

    /* ---------------------------------------------------------------------- */
    Device FieldCollection::get_device() const { return this->device; }

    /* ---------------------------------------------------------------------- */
    bool FieldCollection::is_on_device() const {
        return this->device.is_device();
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::register_real_field(
        const std::string & unique_name, const Index_t & nb_components,
        const std::string & sub_division_tag, const Unit & unit) {
        return this->register_field_helper<Real>(unique_name, nb_components,
                                                 sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::register_real_field(
        const std::string & unique_name, const Shape_t & components_shape,
        const std::string & sub_division_tag, const Unit & unit) {
        return this->register_field_helper<Real>(unique_name, components_shape,
                                                 sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::register_complex_field(
        const std::string & unique_name, const Index_t & nb_components,
        const std::string & sub_division_tag, const Unit & unit) {
        return this->register_field_helper<Complex>(unique_name, nb_components,
                                                    sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::register_complex_field(
        const std::string & unique_name, const Shape_t & components_shape,
        const std::string & sub_division_tag, const Unit & unit) {
        return this->register_field_helper<Complex>(
            unique_name, components_shape, sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::register_int_field(
        const std::string & unique_name, const Index_t & nb_components,
        const std::string & sub_division_tag, const Unit & unit) {
        return this->register_field_helper<Int>(unique_name, nb_components,
                                                sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::register_int_field(
        const std::string & unique_name, const Shape_t & components_shape,
        const std::string & sub_division_tag, const Unit & unit) {
        return this->register_field_helper<Int>(unique_name, components_shape,
                                                sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::register_uint_field(
        const std::string & unique_name, const Index_t & nb_components,
        const std::string & sub_division_tag, const Unit & unit) {
        return this->register_field_helper<Uint>(unique_name, nb_components,
                                                 sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::register_uint_field(
        const std::string & unique_name, const Shape_t & components_shape,
        const std::string & sub_division_tag, const Unit & unit) {
        return this->register_field_helper<Uint>(unique_name, components_shape,
                                                 sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    TypedStateField<Real> & FieldCollection::register_real_state_field(
        const std::string & unique_name, const Index_t & nb_memory,
        const Index_t & nb_components, const std::string & sub_division_tag,
        const Unit & unit) {
        return this->register_state_field_helper<Real>(
            unique_name, nb_memory, nb_components, sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    TypedStateField<Complex> & FieldCollection::register_complex_state_field(
        const std::string & unique_name, const Index_t & nb_memory,
        const Index_t & nb_components, const std::string & sub_division_tag,
        const Unit & unit) {
        return this->register_state_field_helper<Complex>(
            unique_name, nb_memory, nb_components, sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    TypedStateField<Int> & FieldCollection::register_int_state_field(
        const std::string & unique_name, const Index_t & nb_memory,
        const Index_t & nb_components, const std::string & sub_division_tag,
        const Unit & unit) {
        return this->register_state_field_helper<Int>(
            unique_name, nb_memory, nb_components, sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    TypedStateField<Uint> & FieldCollection::register_uint_state_field(
        const std::string & unique_name, const Index_t & nb_memory,
        const Index_t & nb_components, const std::string & sub_division_tag,
        const Unit & unit) {
        return this->register_state_field_helper<Uint>(
            unique_name, nb_memory, nb_components, sub_division_tag, unit);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::real_field(const std::string & unique_name,
                                        const Index_t & nb_components,
                                        const std::string & sub_division_tag,
                                        const Unit & unit) {
        return this->register_field_helper<Real>(unique_name, nb_components,
                                                 sub_division_tag, unit, true);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::real_field(const std::string & unique_name,
                                        const Shape_t & components_shape,
                                        const std::string & sub_division_tag,
                                        const Unit & unit) {
        return this->register_field_helper<Real>(unique_name, components_shape,
                                                 sub_division_tag, unit, true);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::complex_field(const std::string & unique_name,
                                           const Index_t & nb_components,
                                           const std::string & sub_division_tag,
                                           const Unit & unit) {
        return this->register_field_helper<Complex>(
            unique_name, nb_components, sub_division_tag, unit, true);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::complex_field(const std::string & unique_name,
                                           const Shape_t & components_shape,
                                           const std::string & sub_division_tag,
                                           const Unit & unit) {
        return this->register_field_helper<Complex>(
            unique_name, components_shape, sub_division_tag, unit, true);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::int_field(const std::string & unique_name,
                                       const Index_t & nb_components,
                                       const std::string & sub_division_tag,
                                       const Unit & unit) {
        return this->register_field_helper<Int>(unique_name, nb_components,
                                                sub_division_tag, unit, true);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::int_field(const std::string & unique_name,
                                       const Shape_t & components_shape,
                                       const std::string & sub_division_tag,
                                       const Unit & unit) {
        return this->register_field_helper<Int>(unique_name, components_shape,
                                                sub_division_tag, unit, true);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::uint_field(const std::string & unique_name,
                                        const Index_t & nb_components,
                                        const std::string & sub_division_tag,
                                        const Unit & unit) {
        return this->register_field_helper<Uint>(unique_name, nb_components,
                                                 sub_division_tag, unit, true);
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::uint_field(const std::string & unique_name,
                                        const Shape_t & components_shape,
                                        const std::string & sub_division_tag,
                                        const Unit & unit) {
        return this->register_field_helper<Uint>(unique_name, components_shape,
                                                 sub_division_tag, unit, true);
    }

    /* ---------------------------------------------------------------------- */
    TypedStateField<Real> & FieldCollection::real_state_field(
        const std::string & unique_name, const Index_t & nb_memory,
        const Index_t & nb_components, const std::string & sub_division_tag,
        const Unit & unit) {
        return this->register_state_field_helper<Real>(
            unique_name, nb_memory, nb_components, sub_division_tag, unit,
            true);
    }

    /* ---------------------------------------------------------------------- */
    TypedStateField<Complex> & FieldCollection::complex_state_field(
        const std::string & unique_name, const Index_t & nb_memory,
        const Index_t & nb_components, const std::string & sub_division_tag,
        const Unit & unit) {
        return this->register_state_field_helper<Complex>(
            unique_name, nb_memory, nb_components, sub_division_tag, unit,
            true);
    }

    /* ---------------------------------------------------------------------- */
    TypedStateField<Int> & FieldCollection::int_state_field(
        const std::string & unique_name, const Index_t & nb_memory,
        const Index_t & nb_components, const std::string & sub_division_tag,
        const Unit & unit) {
        return this->register_state_field_helper<Int>(
            unique_name, nb_memory, nb_components, sub_division_tag, unit,
            true);
    }

    /* ---------------------------------------------------------------------- */
    TypedStateField<Uint> & FieldCollection::uint_state_field(
        const std::string & unique_name, const Index_t & nb_memory,
        const Index_t & nb_components, const std::string & sub_division_tag,
        const Unit & unit) {
        return this->register_state_field_helper<Uint>(
            unique_name, nb_memory, nb_components, sub_division_tag, unit,
            true);
    }

    /* ---------------------------------------------------------------------- */
    bool FieldCollection::field_exists(const std::string & unique_name) const {
        return this->fields.find(unique_name) != this->fields.end();
    }

    /* ---------------------------------------------------------------------- */
    bool FieldCollection::state_field_exists(
        const std::string & unique_prefix) const {
        return this->state_fields.find(unique_prefix) !=
               this->state_fields.end();
    }

    /* ---------------------------------------------------------------------- */
    Index_t FieldCollection::get_nb_pixels() const { return this->nb_pixels; }

    /* ---------------------------------------------------------------------- */
    Index_t FieldCollection::get_nb_pixels_without_ghosts() const {
        return this->get_nb_pixels();
    }

    /* ---------------------------------------------------------------------- */
    Index_t FieldCollection::get_nb_buffer_pixels() const {
        return this->nb_buffer_pixels;
    }

    /* ---------------------------------------------------------------------- */
    bool FieldCollection::has_nb_sub_pts(const std::string & tag) const {
        if (this->nb_sub_pts.count(tag) == 0) {
            return false;
        } else {
            return this->get_nb_sub_pts(tag) != Unknown;
        }
    }

    /* ---------------------------------------------------------------------- */
    Index_t FieldCollection::get_nb_sub_pts(const std::string & tag) {
        if (not this->has_nb_sub_pts(tag)) {
            return this->nb_sub_pts[tag] = Unknown;
        }
        return this->nb_sub_pts.at(tag);
    }

    /* ---------------------------------------------------------------------- */
    Index_t FieldCollection::get_nb_sub_pts(const std::string & tag) const {
        return this->nb_sub_pts.at(tag);
    }

    /* ---------------------------------------------------------------------- */
    void FieldCollection::set_nb_sub_pts(const std::string & tag,
                                         const Index_t & nb_sub_pts_per_pixel) {
        if (this->has_nb_sub_pts(tag)) {
            auto && nb_pts{this->nb_sub_pts.at(tag)};
            if (nb_pts != nb_sub_pts_per_pixel) {
                std::stringstream error{};
                error << "The number of '" << tag
                      << "' points per pixel has already been set to " << nb_pts
                      << " and cannot be changed to " << nb_sub_pts_per_pixel
                      << '.';
                throw FieldCollectionError(error.str());
            }
        }
        if (nb_sub_pts_per_pixel < 1) {
            std::stringstream error{};
            error << "The number of '" << tag
                  << "' points per pixel must be positive. " << "You chose "
                  << nb_sub_pts_per_pixel;
            throw FieldCollectionError(error.str());
        }
        this->nb_sub_pts[tag] = nb_sub_pts_per_pixel;

        for (auto && item : this->fields) {
            auto & field{std::get<1>(item)};
            if (field->get_sub_division_tag() == tag) {
                field->set_nb_sub_pts(nb_sub_pts_per_pixel);
            }
        }
    }

    /* ---------------------------------------------------------------------- */
    Dim_t FieldCollection::get_spatial_dim() const { return this->spatial_dim; }

    /* ---------------------------------------------------------------------- */
    FieldCollection::ValidityDomain FieldCollection::get_domain() const {
        return this->domain;
    }

    /* ---------------------------------------------------------------------- */
    StorageOrder FieldCollection::get_storage_order() const {
        return this->storage_order;
    }

    //! check whether two field collections have the same memory layout
    bool FieldCollection::has_same_memory_layout(
        const FieldCollection & other) const {
        return this->get_pixels_shape() == other.get_pixels_shape() and
               this->get_storage_order() == other.get_storage_order() and
               this->get_pixels_strides() == other.get_pixels_strides();
    }

    /* ---------------------------------------------------------------------- */
    bool FieldCollection::is_initialised() const { return this->initialised; }

    /* ---------------------------------------------------------------------- */
    auto FieldCollection::get_pixel_indices() const -> PixelIndexIterable {
        return PixelIndexIterable{*this};
    }

    /* ---------------------------------------------------------------------- */
    auto FieldCollection::get_sub_pt_indices(const std::string & tag) const
        -> IndexIterable {
        return IndexIterable{*this, tag};
    }

    /* ---------------------------------------------------------------------- */
    void FieldCollection::allocate_fields() {
        for (auto && item : this->fields) {
            auto && field{*item.second};
            const auto field_size{field.get_current_nb_entries()};
            if ((field_size != 0) and (field_size != field.get_nb_entries())) {
                std::stringstream err_stream{};
                err_stream << "Field '" << field.get_name() << "' contains "
                           << field_size
                           << " entries, but the field collection "
                           << "has " << this->get_nb_pixels()
                           << " pixels, and the field should have "
                           << field.get_nb_sub_pts()
                           << " sub-points, i.e., a total of "
                           << this->get_nb_pixels() * field.get_nb_sub_pts()
                           << " entries.";
                throw FieldCollectionError(err_stream.str());
            }
            // resize is being called unconditionally, because it alone
            // guarantees the validity of the field's `data_ptr`
            field.resize();
        }
    }

    /* ---------------------------------------------------------------------- */
    void FieldCollection::initialise_maps() {
        for (auto & weak_callback : this->init_callbacks) {
            if (auto shared_callback{weak_callback.lock()}) {
                auto && callback{*shared_callback};
                callback();
            }
        }
        this->init_callbacks.clear();
    }

    /* ---------------------------------------------------------------------- */
    Field & FieldCollection::get_field(const std::string & unique_name) {
        if (not this->field_exists(unique_name)) {
            std::stringstream err_stream{};
            err_stream << "The field '" << unique_name << "' does not exist";
            throw FieldCollectionError(err_stream.str());
        }
        return *this->fields[unique_name];
    }

    /* ---------------------------------------------------------------------- */
    const Field &
    FieldCollection::get_field(const std::string & unique_name) const {
        if (not this->field_exists(unique_name)) {
            std::stringstream err_stream{};
            err_stream << "The field '" << unique_name << "' does not exist";
            throw FieldCollectionError(err_stream.str());
        }
        return *this->fields.at(unique_name);
    }

    /* ---------------------------------------------------------------------- */
    auto FieldCollection::pop_field(const std::string & unique_name)
        -> Field_ptr {
        if (not this->field_exists(unique_name)) {
            std::stringstream err_stream{};
            err_stream << "The field '" << unique_name << "' does not exist";
            throw FieldCollectionError(err_stream.str());
        }
        auto field_ptr{std::move(this->fields[unique_name])};
        this->fields.erase(unique_name);
        return field_ptr;
    }

    /* ---------------------------------------------------------------------- */
    StateField &
    FieldCollection::get_state_field(const std::string & unique_prefix) {
        if (not this->state_field_exists(unique_prefix)) {
            std::stringstream err_stream{};
            err_stream << "The state field '" << unique_prefix
                       << "' does not exist";
            throw FieldCollectionError(err_stream.str());
        }
        return *this->state_fields[unique_prefix];
    }

    /* ---------------------------------------------------------------------- */
    std::vector<std::string> FieldCollection::list_fields() const {
        std::vector<std::string> field_names;
        for (const auto & f : this->fields) {
            field_names.push_back(std::get<0>(f));
        }
        return field_names;
    }

    /* ---------------------------------------------------------------------- */
    std::vector<std::string>
    FieldCollection::list_state_field_unique_prefixes() const {
        std::vector<std::string> unique_prefixes;
        for (const auto & map : this->state_fields) {
            unique_prefixes.push_back(std::get<0>(map));
        }
        return unique_prefixes;
    }

    /* ---------------------------------------------------------------------- */
    void FieldCollection::preregister_map(
        std::shared_ptr<std::function<void()>> & call_back) {
        if (this->initialised) {
            throw FieldCollectionError("Collection is already initialised");
        }
        this->init_callbacks.push_back(call_back);
    }

    /**
     * Technically, these explicit instantiations are not necessary, as they are
     * implicitly instantiated when the register_<T>field(...) member functions
     * are compiled.
     */
    template Field & FieldCollection::register_field<Real>(const std::string &,
                                                           const Index_t &,
                                                           const std::string &,
                                                           const Unit &);

    template Field &
    FieldCollection::register_field<Complex>(const std::string &,
                                             const Index_t &,
                                             const std::string &, const Unit &);

    template Field & FieldCollection::register_field<Int>(const std::string &,
                                                          const Index_t &,
                                                          const std::string &,
                                                          const Unit &);

    template Field & FieldCollection::register_field<Uint>(const std::string &,
                                                           const Index_t &,
                                                           const std::string &,
                                                           const Unit &);

    template Field &
    FieldCollection::register_field<Index_t>(const std::string &,
                                             const Index_t &,
                                             const std::string &, const Unit &);

    template FieldCollection::Field_ptr
    FieldCollection::detached_field<Real>(const std::string &, const Shape_t &,
                                          const std::string &, const Unit &);

    template FieldCollection::Field_ptr
    FieldCollection::detached_field<Complex>(const std::string &,
                                             const Shape_t &,
                                             const std::string &, const Unit &);

    template FieldCollection::Field_ptr
    FieldCollection::detached_field<Int>(const std::string &, const Shape_t &,
                                         const std::string &, const Unit &);

    template FieldCollection::Field_ptr
    FieldCollection::detached_field<Index_t>(const std::string &,
                                             const Shape_t &,
                                             const std::string &, const Unit &);

    template FieldCollection::Field_ptr
    FieldCollection::detached_field<Uint>(const std::string &, const Shape_t &,
                                          const std::string &, const Unit &);

    /* ---------------------------------------------------------------------- */
    FieldCollection::PixelIndexIterable::PixelIndexIterable(
        const FieldCollection & collection)
        : collection{collection} {}

    /* ---------------------------------------------------------------------- */
    auto FieldCollection::PixelIndexIterable::begin() const -> iterator {
        return this->collection.pixel_indices.begin();
    }

    /* ---------------------------------------------------------------------- */
    auto FieldCollection::PixelIndexIterable::end() const -> iterator {
        return this->collection.pixel_indices.end();
    }

    /* ---------------------------------------------------------------------- */
    size_t FieldCollection::PixelIndexIterable::size() const {
        return this->collection.get_nb_pixels();
    }

    /* ---------------------------------------------------------------------- */
    Index_t FieldCollection::check_nb_sub_pts(const Index_t & nb_sub_pts,
                                              const IterUnit & iteration_type,
                                              const std::string & tag) const {
        switch (iteration_type) {
        case IterUnit::SubPt: {
            auto && correct_stride{this->get_nb_sub_pts(tag)};
            if (nb_sub_pts != Unknown and nb_sub_pts != correct_stride) {
                std::stringstream err_msg{};
                err_msg
                    << "The number of stride you specified (" << nb_sub_pts
                    << ") is incompatible with the number of sub points per "
                       "pixel already registered with this field collection ("
                    << correct_stride << ")";
                throw FieldCollectionError(err_msg.str());
            }
            return correct_stride;
        }
        case IterUnit::Pixel: {
            constexpr size_t correct_stride{OneNode};
            if (nb_sub_pts != Unknown and nb_sub_pts != correct_stride) {
                std::stringstream err_msg{};
                err_msg << "The stride you specified (" << nb_sub_pts
                        << ") is not one. Pixel iteration always has a stride "
                           "of 1.";
                throw FieldCollectionError(err_msg.str());
            }
            return correct_stride;
        }
        default:
            throw FieldCollectionError("Unknown Subdivision type");
        }
    }

    /* ---------------------------------------------------------------------- */
    size_t FieldCollection::check_initialised_nb_sub_pts(
        const Index_t & nb_sub_pts, const IterUnit & iteration_type,
        const std::string & tag) const {
        if (iteration_type == IterUnit::SubPt) {
            if (not this->has_nb_sub_pts(tag)) {
                throw FieldCollectionError(
                    "Can't compute a sub-point iterator before the number of "
                    "sub points per pixel/voxel has been set.");
            }
        }
        // the other cases are handled in the regular(uninitialised) checker
        return static_cast<size_t>(
            this->check_nb_sub_pts(nb_sub_pts, iteration_type, tag));
    }

    /* ---------------------------------------------------------------------- */
    std::string FieldCollection::generate_unique_name() const {
        static Index_t counter{0};
        bool field_found{false};
        std::string name{};
        do {
            std::stringstream name_stream{};
            name_stream << "generated_unique_name_" << counter++;
            name = name_stream.str();
            field_found = this->field_exists(name);
        } while (field_found);
        return name;
    }

    /* ---------------------------------------------------------------------- */
    FieldCollection::IndexIterable::IndexIterable(
        const FieldCollection & collection, const std::string & tag,
        const Index_t & nb_sub_pts)
        : collection{collection}, iteration_type{IterUnit::SubPt},
          stride{collection.check_initialised_nb_sub_pts(
              nb_sub_pts, this->iteration_type, tag)} {}

    /* ---------------------------------------------------------------------- */
    FieldCollection::IndexIterable::IndexIterable(
        const FieldCollection & collection, const Index_t & nb_sub_pts)
        : collection{collection}, iteration_type{IterUnit::Pixel},
          stride{collection.check_initialised_nb_sub_pts(
              nb_sub_pts, this->iteration_type,
              "Unnamed, because pixel iterator")} {}

    /* ---------------------------------------------------------------------- */
    auto FieldCollection::IndexIterable::begin() const -> iterator {
        return iterator(this->collection.pixel_indices.begin(), this->stride);
    }

    /* ---------------------------------------------------------------------- */
    auto FieldCollection::IndexIterable::end() const -> iterator {
        return iterator(this->collection.pixel_indices.end(), this->stride);
    }

    /* ---------------------------------------------------------------------- */
    size_t FieldCollection::IndexIterable::size() const {
        return this->collection.get_nb_pixels() * this->stride;
    }

    /* ---------------------------------------------------------------------- */
    FieldCollection::IndexIterable::iterator::iterator(
        const PixelIndexIterator_t & pixel_index_iterator,
        const size_t & stride)
        : stride{stride}, pixel_index_iterator{pixel_index_iterator} {}

    /* ---------------------------------------------------------------------- */
    std::ostream &
    operator<<(std::ostream & os,
               const muGrid::FieldCollection::ValidityDomain & value) {
        const char * s = 0;
        switch (value) {
        case (muGrid::FieldCollection::ValidityDomain::Global):
            s = "ValidityDomain::Global";
            break;
        case (muGrid::FieldCollection::ValidityDomain::Local):
            s = "ValidityDomain::Local";
            break;
        default:
            s = "ValidityDomain is not defined";
        }
        return os << s;
    }
}  // namespace muGrid
