.. |workflow_calculation_layer|     replace:: :py:class:`~openff.evaluator.layers.workflow.WorkflowCalculationLayer`
.. |workflow_calculation_schema|    replace:: :py:class:`~openff.evaluator.layers.workflow.WorkflowCalculationSchema`
.. |workflow_schema|                replace:: :py:class:`~openff.evaluator.workflow.schemas.WorkflowSchema`

.. |final_value_source|             replace:: :py:attr:`~openff.evaluator.workflow.schemas.WorkflowSchema.final_value_source`
.. |gradients_sources|              replace:: :py:attr:`~openff.evaluator.workflow.schemas.WorkflowSchema.gradients_sources`
.. |outputs_to_store|               replace:: :py:attr:`~openff.evaluator.workflow.schemas.WorkflowSchema.outputs_to_store`

.. |_schedule_calculation|          replace:: :py:meth:`~openff.evaluator.layers.CalculationLayer._schedule_calculation`

.. |_get_workflow_metadata|         replace:: :py:meth:`~openff.evaluator.layers.workflow.WorkflowCalculationLayer._get_workflow_metadata`
.. |_build_workflow_graph|          replace:: :py:meth:`~openff.evaluator.layers.workflow.WorkflowCalculationLayer._build_workflow_graph`
.. |workflow_to_layer_result|       replace:: :py:meth:`~openff.evaluator.layers.workflow.WorkflowCalculationLayer.workflow_to_layer_result`

.. |workflow_result|                replace:: :py:class:`~openff.evaluator.workflow.WorkflowResult`
.. |calculation_layer_result|       replace:: :py:class:`~openff.evaluator.layers.CalculationLayerResult`

.. |calculation_source|             replace:: :py:class:`~openff.evaluator.datasets.CalculationSource`
.. |fidelity|                       replace:: :py:attr:`~openff.evaluator.datasets.CalculationSource.fidelity`
.. |provenance|                     replace:: :py:class:`~openff.evaluator.datasets.CalculationSource.provenance`

.. |generate_default_metadata|      replace:: :py:meth:`~openff.evaluator.workflow.Workflow.generate_default_metadata`

.. |substance|                    replace:: :py:class:`~openff.evaluator.substances.Substance`
.. |thermodynamic_state|          replace:: :py:class:`~openff.evaluator.thermodynamics.ThermodynamicState`

.. |parameter_gradient_key|       replace:: :py:class:`~openff.evaluator.forcefield.ParameterGradientKey`

.. |quantity|                     replace:: :py:class:`~pint.Quantity`

Workflow Layers
===============

The |workflow_calculation_layer| and |workflow_calculation_schema| offer an abstract base implementation for any
calculation layers (and their associated schemas) which will perform their calculations using the built-in
:doc:`workflow engine <../workflows/workflows>`.

The |workflow_calculation_layer| takes as input from its calculation schema one |workflow_schema| object for each type
of property to be estimated by this layer. These schemas must *at a minimum* provide both the schemas of the protocols
in the workflow, and have the |final_value_source| attribute set to the value of the calculated observable. In addition,
the layer fully supports schemas which provide gradient information (see the |gradients_sources| attribute), as well as
storing any generated dataclasses (see the |outputs_to_store| attribute) to the available storage backend.

This layer implements three key methods which are available to be overridden by any subclass implementations:

.. rst-class:: spaced-list

    * |_get_workflow_metadata|: a method which returns the dictionary of :ref:`metadata <workflows/workflows:Metadata>`
      which will be made available to the workflow (see the :ref:`default metadata <layers/workflowlayer:Default Metadata>`
      section for details).

    * |_build_workflow_graph|: the method which will construct the :doc:`workflow graph <../workflows/workflowgraphs>`
      to execute using the input workflow schemas and the metadata generated by the layer.

    * |workflow_to_layer_result|: a method which will map any |workflow_result| objects generated by the workflow graph
      into the |calculation_layer_result| objects which the layer requires.

The workflow layer will by default tag each property estimated using it (or one of its derivatives) with a
|calculation_source| with the |fidelity| attribute set to the name of the layer, and the |provenance| attribute set to
the schema of the workflow used to generate the property.

Default Metadata
----------------

The metadata provided to the workflows generated by this layer is generated on a per property to estimate basis mainly
using the |generate_default_metadata| function. It includes:

.. table::
   :widths: auto
   :align: center
   :class: clean-table

   +--------------------------------+-----------------------------+--------------------------------------------------------------------------------+
   || Key                           || Type                       || Description                                                                   |
   +================================+=============================+================================================================================+
   || ``thermodynamic_state``       || |thermodynamic_state|      || The state at which the to perform any calculations .                          |
   +--------------------------------+-----------------------------+--------------------------------------------------------------------------------+
   || ``substance``                 || |substance|                || The substance to use in any calculations.                                     |
   +--------------------------------+-----------------------------+--------------------------------------------------------------------------------+
   || ``components``                || [|substance|]              || The components present in the main ``substance``.                             |
   +--------------------------------+-----------------------------+--------------------------------------------------------------------------------+
   || ``target_uncertainty``        || |quantity|                 || The target uncertainty of any calculations defined by the calculation schema. |
   +--------------------------------+-----------------------------+--------------------------------------------------------------------------------+
   || ``per_component_uncertainty`` || |quantity|                 || The ``target_uncertainty`` divided by ``sqrt(substance.n_components + 1)``    |
   +--------------------------------+-----------------------------+--------------------------------------------------------------------------------+
   || ``force_field_path``          || :py:class:`str`            || A file path to the force field parameters to use.                             |
   +--------------------------------+-----------------------------+--------------------------------------------------------------------------------+
   || ``parameter_gradient_keys``   || [|parameter_gradient_key|] || The parameters to differentiate any observables with respect to (if any).     |
   +--------------------------------+-----------------------------+--------------------------------------------------------------------------------+
