.. 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.

******************************
Advanced Module Implementation
******************************

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

The previous page covered the basics of writing a module, which is fine for
many applications. For more complicated algorithms however, there are additional
considerations. That is what this page focuses on.

Module-Specific Inputs
======================

Continuing from our electric field example, the factor of:
:math:`||\vec{r} - \vec{r}_i||^2` means that point charges which are far from
the evaluation point, :math:`\vec{r}`, minimally contributes to the electric
field (assuming all point charges have approximately the same charge).
Consequently, we can reduce the computational prefactor slightly by only
considering point charges "near" :math:`\vec{r}`. What constitutes "near" will
in general be situational (*i.e.*, depends on magnitudes of point charges
involved, target precision, *etc.*). The point of all this is that for
computational reasons, we would like to add a threshold paramter to our electric
field module.

For simplicity we will implement a new module ``ScreenedCoulombsLaw`` rather
than modifying ``CoulombsLaw``. Relative to our ``CoulombsLaw`` module, the new
parameter slightly modifies the constructor and the ``run_`` member functions.
With the new input the constructor becomes:

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

Specifically we declare a new double-precision input called "threshold", with a
brief description and a default value set to the maximum double value (*i.e.*,
no screening will occur by default). The relevant change to the ``run_`` member
is:

.. literalinclude:: ../../../../tests/cxx/doc_snippets/screened_coulombs_law.cpp
   :language: c++
   :lines: 48-53,75-77

Here the point to note is that our module is responsible for manually unwrapping
the new input parameter. It will **NOT** automatically be unwrapped by the
property type.

.. note::

   Module-specific inputs work best for parameters that are typically set in
   advance versus parameters that would typically be passed as an input.

Satisfying Multiple Property Types
==================================

Sometimes an algorithm will compute more than one property. Reasons why this may
occur include performance (the two quantities may share many common
intermediates) and wrapping code not originally designed for PluginPlay (for
example calling a library which has a single API that computes many properties).
This poses no problem as PluginPlay allows modules to satisfy multiple property
types.

If a module satisfies :math:`n` property types, the inputs to the module are the
union of the inputs to the :math:`n` property types as well as any
module-specific inputs that the module declares. Similarly the returns of the
module are the union of the returns of the :math:`n` property types as well as
any module-specific results that the module returns.

TODO: Add examples

Lambda Modules
==============

Lambda modules are one-off modules which satisfy a single property type. They
can be used anywhere the property type is needed. Unlike a normal module which
requires defining a class and metadata, lambda modules can be written inline in
turn allowing the lambda module to use runtime state in its defintion. Lambda
functions are generated by wrapping a function (either a free function or a
lambda function).

TODO: Add examples

Facade Modules
==============

Facade modules are modules which ignore the arguments they are given and return
a specified value. They are a simplified version of a lambda module which can
be created by providing a single value (instead of a function like a lambda
module, or a class like a normal module).
