.. _WorkingWithData:

#####################################
Working with data in GEOS
#####################################

In ``GEOS``, data is typically registered in the :ref:`dataRepository`.
This allows for the writing/reading of data to/from restart and plot files.
Any object that derives from :ref:`Group` may have data registered on it
through the methods described in :ref:`Group`.
Similarly, accessing data from outside the scope of an object is possible
through one of the various ``Group::get()``.
Of course, for temporary data that does not need to persist between cycles,
or across physics packages, you may simply define member or local variable which
will not be registered with the :ref:`dataRepository`.

Working with data on the Mesh objects
=====================================
The mesh objects in ``GEOS`` such as the ``FaceManager`` or ``NodeManager``,
are derived from ``ObjectManagerBase``, which in turn derives from :ref:`Group`.
The important distinction is that ``ObjectManagerBase`` contains various members
that are useful when defining mesh object managers.
When considering data that is attached to a mesh object, we group the data into
two categories:

* `Intrinsic <https://www.merriam-webster.com/dictionary/intrinsic>`_ data is
  data that is required to describe the object.
  For instance, to define a ``Node``, the ``NodeManager`` contains an array of
  positions corresponding to each ``Node`` it contains.
  Thus the ``ReferencePosition`` is ``Intrinsic`` data.
  ``Intrinsic`` data is almost always a member of the mesh object, and is
  registered on the mesh object in the constructor of mesh object itself.
* Field data (or `Extrinsic <https://www.merriam-webster.com/dictionary/extrinsic>`_ data) is
  data that is not required to define the object.
  For instance, a physics package
  may request that a ``Velocity`` value be stored on the nodes.
  Appropriately the data will be registered on the ``NodeManager``.
  However, this data is not required to define a ``Node``, and is viewed as
  ``Fields`` or ``Extrinsic``.
  ``Field`` data is never a member of the mesh object, and is typically
  registered on the mesh object outside of the definition of the mesh object
  (i.e. from a physics solver).


Registering Intrinsic data on a Mesh Object
-------------------------------------------
As mentioned above, ``Intrinsic`` data is typically a member of the mesh object,
and is registered in the constructor of the mesh Object.
Taking the ``NodeManager`` and the ``referencePosition`` as an example, we
point out that the reference position is actually a member in the
``NodeManager``.

.. literalinclude:: ../../../../coreComponents/mesh/NodeManager.hpp
   :language: c++
   :start-after: //START_SPHINX_REFPOS
   :end-before: //END_SPHINX_REFPOS

This member is registered in the constructor for the ``NodeManager``.

.. literalinclude:: ../../../../coreComponents/mesh/NodeManager.cpp
   :language: c++
   :start-after: //START_SPHINX_REFPOS_REG
   :end-before: //END_SPHINX_REFPOS_REG

Finally in order to access this data, the ``NodeManager`` provides explicit
accessors.

.. literalinclude:: ../../../../coreComponents/mesh/NodeManager.hpp
   :language: c++
   :start-after: //START_SPHINX_REFPOS_ACCESS
   :end-before: //END_SPHINX_REFPOS_ACCESS

Thus the interface for ``Intrinsic`` data is set by the object that it is a part
of, and the developer may only access the data through the accesssors from
outside of the mesh object class scope.

Registering Field data on a Mesh Object
---------------------------------------
To register ``Field`` data, there are many ways a developer may proceed.
We will use the example of registering a ``totalDisplacement`` on the ``NodeManager``
from the ``SolidMechanics`` solver.
The most general approach is to define a string key and call one of the
`Group::registerWrapper() <../../../doxygen_output/html/classgeos_1_1data_repository_1_1_group.html#a741c3b5728fc47b33fbaad6c4f124991>`_
functions from ``PhysicsSolverBase::registerDataOnMesh()``.
Then when you want to use the data, you can call ``Group::getReference()``.
For example this would look something like:

.. code-block:: c++

    void SolidMechanicsLagrangianFEM::registerDataOnMesh( Group * const MeshBodies )
    {
      for( auto & mesh : MeshBodies->GetSubGroups() )
      {
        NodeManager & nodes = mesh.second->groupCast< MeshBody * >()->getMeshLevel( 0 ).getNodeManager();

        nodes.registerWrapper< array2d< real64, nodes::TOTAL_DISPLACEMENT_PERM > >( keys::totalDisplacement ).
          setPlotLevel( PlotLevel::LEVEL_0 ).
          setRegisteringObjects( this->getName()).
          setDescription( "An array that holds the total displacements on the nodes." ).
          reference().resizeDimension< 1 >( 3 );
      }
    }

and

.. code-block:: c++

    arrayView2d< real64, nodes::TOTAL_DISPLACEMENT_USD > const & u = nodes.getReference< array2d< real64, nodes::TOTAL_DISPLACEMENT_PERM > >( keys::totalDisplacement );
    ... do something with u

This approach is flexible and extendible, but is potentially error prone due to
its verbosity and lack of information centralization.
Therefore we also provide a more controlled/uniform method by which to register
and extract commonly used data on the mesh.
The ``trait approach`` requires the definition of a ``traits struct`` for each
data object that will be supported.
To apply the ``trait approach`` to the example use case shown above, there
should be the following definition somewhere in a header file:

.. code-block:: c++

    namespace fields
    {
    struct totalDisplacement
    {
      static constexpr auto key = "totalDisplacement";
      using DataType = real64;
      using Type = array2d< DataType, nodes::TOTAL_DISPLACEMENT_PERM >;
      static constexpr DataType defaultValue = 0;
      static constexpr auto plotLevel = dataRepository::PlotLevel::LEVEL_0;

      /// Description of the data associated with this trait.
      static constexpr auto description = "An array that holds the total displacements on the nodes.";
    };
    }

Also note that you should use the ``DECLARE_FIELD`` C++ macro that will perform this tedious task for you.
Then the registration is simplified as follows:

.. code-block:: c++

    void SolidMechanicsLagrangianFEM::registerDataOnMesh( Group * const MeshBodies )
    {
      for( auto & mesh : MeshBodies->GetSubGroups() )
      {
        NodeManager & nodes = mesh.second->groupCast< MeshBody * >()->getMeshLevel( 0 ).getNodeManager();
        nodes.registerField< fields::totalDisplacement >( this->getName() ).resizeDimension< 1 >( 3 );
      }
    }

And to extract the data, the call would be:

.. code-block:: c++

    arrayView2d< real64, nodes::TOTAL_DISPLACEMENT_USD > const & u = nodes.getField< fields::totalDisplacement >();
    ... do something with u

The end result of the ``trait approach`` to this example is that the developer
has defined a standard specification for ``totalDisplacement``, which may be
used uniformly across the code.