.. Copyright 2022 NWChemEx-Project
..
.. Licensed under the Apache License, Version 2.0 (the "License");
.. you may not use this file except in compliance with the License.
.. You may obtain a copy of the License at
..
.. http://www.apache.org/licenses/LICENSE-2.0
..
.. Unless required by applicable law or agreed to in writing, software
.. distributed under the License is distributed on an "AS IS" BASIS,
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
.. See the License for the specific language governing permissions and
.. limitations under the License.

**************************
Basics of Writing a Module
**************************

.. |acceleration| replace:: :math:`\vec{a}\left(\vec{r}\right)`
.. |force| replace:: :math:`\vec{F}\left(\vec{r}\right)`
.. |vec_r| replace:: :math:`\vec{r}`
.. |e_field| replace:: :math:`\vec{E}\left(\vec{r}\right)`

This page is intended to walk you through the basics of writing a module. Here
we focus purely on writing the module, not designing it. Because of how
PluginPlay works designing a module can sometimes be non-trivial; tips for
designing a module can be found in :ref:`module_designing`. This page assumes we are
writing a module which computes the electric field, |e_field| of a series of
point charges as detailed in :ref:`module_development`.

Declaring the Module
====================

In PluginPlay modules are implemented by inheriting from the ``ModuleBase``
class and then implementing a constructor and a ``run_`` member. The declaration
amounts to a large amount of boilerplate so PluginPlay provides a macro
``DECLARE_MODULE(T)`` which will declare a class ``T`` satisfying the required
API. Since it will be used as the name of the class, ``T`` must be a valid C
identifier.

Generally speaking modules of the same property type differ by the algorithm
used to compute the property. It thus makes sense to somehow include the
algorithm in the module's name. For the purposes of our electric field module we
will compute the electric field via Coulomb's Law so we choose to name our
module ``CoulombsLaw`` and we store the declaration in a file ``modules.hpp``.
The relevant lines are:

.. literalinclude:: ../../../../tests/cxx/doc_snippets/modules.hpp
   :language: c++
   :lines: 17-22,31-32

.. note::

   Most libraries include more than one module, and given that declaring a
   module requires a single line, it is not uncommon to declare multiple modules
   in a single header file.

.. note::

   Declaring a module is not required if the module is written in Python.

Defining the Module
===================

The definition of our ``CoulombsLaw`` module goes in a source file. Defining the
module amounts to implementing two functions: the default constructor and the
``run_`` member function. The constructor is where all the set-up for your
module occurs; it is also where you can set meta-data associated with your
module. The ``run_`` member function is where your module does the work.

Defining the Constructor
------------------------

PluginPlay provides the macro ``MODULE_CTOR(T)``, where ``T`` is the name of the
module to aid in defining the module's constructor. At a minimum the constructor
must set the property type(s) that the module satisfies. It is also a good idea
to provide a short description about the algorithm implemented in the module.

.. hint::

   The property type will be needed in a few places so it is a good idea to set
   a typedef, this way if you need to change the property type for any reason
   you only need to change it in the typedef.

.. hint::

   The raw string literal introduced in C++11 denoted by
   ``auto str = R"(...)";`` is a great way to write a lengthy description
   without having to use escapes or other workarounds for line endings.

For our ``CoulombsLaw`` module a basic constructor looks like:

.. tabs::

   .. tab:: C++

      .. literalinclude:: ../../../../tests/cxx/doc_snippets/coulombs_law.cpp
         :language: c++
         :lines: 21-33, 36-39

   .. tab:: Python

      .. literalinclude:: ../../../../tests/python/doc_snippets/coulombslaw_force.py
         :language: python
         :lines: 22-25

One particularly nice feature of PluginPlay is that it can autogenerate
restructured text documentation for modules by scraping the meta-data and fields
of the module. The description we provided serves as a preface to the resulting
page. Some of the other meta-data that module developers can set in the
constructor includes: a list of citations, the author of the module, and the
version. A full list of meta-data is available (TODO: Add link).


.. note::

   PluginPlay automatically documents input parameters and submodules so there
   is no need to include these in your description.


For some modules, inputs that are not defined by the property type(s)
may be needed. Additional inputs can be specified in the constructor with the
``add_inputs`` method. A similar option for added results exist, though it is
less common. Additional inputs have to be set before the module is run, either
with a default value or by calling the ``change_input`` method. Here are
examples for adding a screening threshold to our ``ScreenedCoulombsLaw`` module:

.. tabs::

   .. tab:: C++

      .. literalinclude:: ../../../../tests/cxx/doc_snippets/screened_coulombs_law.cpp
         :language: c++
         :lines: 39-46

   .. tab:: Python

      .. literalinclude:: ../../../../tests/python/doc_snippets/coulombslaw_force.py
         :language: python
         :lines: 47-51



Defining the Run Member
-----------------------

The ``run_`` member of the module takes a set of inputs and a set of submodules,
and returns the property (or properties). Like the module constructor,
PluginPlay provides the macro ``MODULE_RUN(T)``, where ``T`` is the name of the
module to aid in defining the module's ``run_`` member function. By convention
(and hidden in the definition of the macro) the names of the two arguments are:
``inputs`` and ``submods``. Presently we will ignore the ``submods`` argument.

For defining our ``CoulombsLaw::run_`` member the relevant PluginPlay bits of
code are:

.. tabs::

   .. tab:: C++

      .. literalinclude:: ../../../../tests/cxx/doc_snippets/coulombs_law.cpp
         :language: c++
         :lines: 41-45,65-67

   .. tab:: Python

      .. literalinclude:: ../../../../tests/python/doc_snippets/coulombslaw_force.py
         :language: python
         :lines: 27-30,42-43

The inputs to a module are type-erased. Getting them back in a usable typed form
can be done automatically via template meta-programming. The details of this
process are beyond our present scope; for now know that every property type
defines a static function ``unwrap_inputs`` which takes the type-erased inputs
and returns an ``std::tuple`` with the typed inputs (in the above example we
use C++17's structured bindings to automatically unpack the tuple). In a similar
vein the results of every module are also type-erased and every property type
defines a static function ``wrap_results`` which takes the typed results and
returns a result map with the type-erased results.

.. note::

   If a module has additional inputs beyond those specified by the property
   type, they will have to be specifically unpacked from the ``inputs``. In the
   case of the ``ScreenedCoulombsLaw`` module, the screening threshold is
   acquired as:

   .. tabs::

    .. tab:: C++

        .. literalinclude:: ../../../../tests/cxx/doc_snippets/screened_coulombs_law.cpp
            :language: c++
            :lines: 48-50

    .. tab:: Python

        .. literalinclude:: ../../../../tests/python/doc_snippets/coulombslaw_force.py
            :language: python
            :lines: 53-56

The full definition of the ``run_`` member for the ``CoulombsLaw`` module
(including the source code for computing the electric field) is:

.. tabs::

   .. tab:: C++

      .. literalinclude:: ../../../../tests/cxx/doc_snippets/coulombs_law.cpp
         :language: c++
         :lines: 41-67

   .. tab:: Python

      .. literalinclude:: ../../../../tests/python/doc_snippets/coulombslaw_force.py
         :language: python
         :lines: 27-43

Submodules
==========

Coulomb's Law is pretty simple. Most simulation techniques are much more
complicated than that and involve an intricate interplay of many different
properties.

As an example say we are writing a module which computes the force of a moving
charged particle at a position |vec_r|, |force|. Assuming the particle
has a mass :math:`m`, its acceleration at |vec_r| is given by |acceleration|,
and that it has a charge :math:`q`, the |force| is given by:

.. math::

   \newcommand{\force}{\vec{F}\left(\vec{r}\right)}
   \newcommand{\acceleration}{\vec{a}\left(\vec{r}\right)}
   \newcommand{\efield}{\vec{E}\left(\vec{r}\right)}

   \force = m\acceleration + q\efield

The important point for this tutorial is that :math:`F\left(\vec{r}\right)` can
be written in terms of the electric field, |e_field|. To capitialize on all the
innovations in computing electric fields, we decide to write our force module in
terms of an electric field submodule.

Submodules change the class's definition. In the constructor we now need to
declare that our module uses a submodule and that the submodule must satisfy the
property type `ElectricField`. This looks like:

.. literalinclude:: ../../../../tests/cxx/doc_snippets/classical_force.cpp
   :language: c++
   :lines: 38-45

PluginPlay also requires that we specify a tag (we choose the tag
``"electric field"``). The tag ensures that the module can specify and
distinguish between multiple submodules of the same type. When appropriately
named, the tag also aids in readability of the code. The ``run_`` function for
our module looks like:

.. literalinclude:: ../../../../tests/cxx/doc_snippets/classical_force.cpp
   :language: c++
   :lines: 47-59

Of note is the line:

.. literalinclude:: ../../../../tests/cxx/doc_snippets/classical_force.cpp
   :language: c++
   :lines: 53

which says we are running the submodule tagged with ``"electric field"`` as an
``efield_type``. We provide the submodule with the point charge's location and
the list of point charges defining the electric field.

Submodules are integral to PluginPlay so it is worth noting:

- By using submodules we establish data dependencies (*i.e.* here we stated we
  need an electric field, we don't care how it's computed)
- If tomorrow someone writes a new electric field module, it can immediately be
  used with our force module without modifying the force module.
- The submodule that is called can be changed at runtime by the user and our
  module doesn't have to maintain the logic to do so (*i.e.* our force module
  doesn't need to maintain a list of all possible submodules and switch between
  them).
- If the calculation crashes after computing the electric field, but before the
  module completes (pretty unlikely given how few operations remain) memoization
  will avoid the need to recompute the electric field when the calculation is
  restarted. The module does not have to maintain separate checkpoint/restart
  logic!!!!!
