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

.. _module_manager_design:

############################
Designing the Module Manager
############################

:ref:`pp_architecture` calls for a module manager component which will be
the user-facing :ref:`api` of PluginPlay. This section describes the design of
that component.

***************************
What is the module manager?
***************************

Based on object-oriented design principles we need an object to represent the
PluginPlay framework. The majority of interactions with the framework can be
summarized as "module management" so we have opted to call the corresponding
component the "module manager" and the literal class ``ModuleManager``.

*****************************
Module Manager Considerations
*****************************

Since the module manager is the user-facing :ref:`api` every major feature of
PluginPlay needs to be accessible through it. This means the module manager
component needs to support:

#. Dynamic module management

  - Users may hard-coded some configuration, but ultimately
    users also need to be able to override those configurations at runtime.
  - The state of the framework needs to adapt to changes in real time
  - The state of the framework needs to be query-able  in real time.
  - Responsible for lifetime of modules (and framework)

#. Managing of :ref:`module` pool

   - Need to be able to load, remove, *etc.* :ref:`module` objects
   - Iterate over, list, count, *etc.* loaded :ref:`module` objects
   - Discern among different configurations

#. Modify call graph

   - Change which :ref:`submodule` a :ref:`module` calls
   - Change inputs bound to :ref:`module` objects

#. Run call graph

   - Once the call graph is setup need to actually be able to run it.
   - Running call graph should be efficient/performant

#. Saving, loading, and restarting a previous session

   - Intermediates can be expensive, need to manage size of save state
   - May need to go from distributed state to single node, vice versa

***************************
Module Manager Architecture
***************************

.. _fig_mm_arch:

.. figure:: assets/mm_arch.png
   :align: center

   Architecture of the module manager component.

:numref:`fig_mm_arch` shows the architecture of the module manager
component. The user-facing :ref:`api` is codified by a ``ModuleManager`` class.
Using the :ref:`pimpl` idiom, the implementation of the ``ModuleManager`` (and
PluginPlay itself) is separated from the :ref:`api`. The current design
actually has the call graph, cache, and ParallelZone components residing
within the ``ModuleManagerPIMPL`` (not as separate from the ``ModuleManager``
as shown in :numref:`fig_pp_arch_full`). This is primarily for convenience,
but since it is in the :ref:`pimpl`, they could be separated out later
if need be.

With the exception of the "Loaded Module" component (which we discuss in more
detail below). The remainder of the ``ModuleManagerPIMPL``'s state was also
present in top-level architecture diagram and has simply passed through. The
design of the module manager component punts many of the module manager
considerations to these components (see summary below), but provides
user-facing APIs for doing them.

Module Pool
===========

.. _fig_mm_module_pool:

.. figure:: assets/module_pool.png
   :align: center

   Architecture of the "Loaded Module" component, *i.e.*, a module pool.

The new component here is the "Loaded Module" component, which is an associative
array of loaded modules used like a :ref:`module` pool. Conceptually the main
points of this pool are summarized in :numref:`fig_mm_module_pool`. Here
our user has loaded one module under the module key ``"Module A"`` (we'll
discuss ``"Module B"`` below). Module keys are used to refer to the modules
in the module pool. Each module choice has a unique key meant to facilitate
referring back to a specific module.

When the user loads ``"Module A"``, ``"Module A"`` is inserted into the module
pool as is. :numref:`fig_mm_module_pool` depicts ``"Module A"`` as having
four, members. The values of these four members, define the default state for
``"Module A"``. If a user does not want to use ``"Module A"`` in its default
state, they can create a new configuration. This is what ``"Module B"``
represents in :numref:`fig_mm_module_pool`, *i.e.*, ``"Module B"`` is a
different configuration of ``"Module A"`` which differs in that the value of
``Member C`` is replaced with some new value ``Member E``. Thus configurations
are stored as differences. That is to say each configuration contains a link to
the original module and a list of changes to apply. Note that by design, aside
from the different value for the third member, to PluginPlay users and the call
graph, ``"Module B"`` is indistinguishable from ``"Module A"``. This avoids
placing any special emphasis on the default configuration.


Preserving the original state of the module is an important design point.
First, it establishes a default configuration, from which we can define our
differences. Second it facilitates recording provenance. When looking back at a
previous session it can be important to know if the value used was the default
value, or if the user set the value. Note that module defaults can change over
time, which makes it harder to ascertain this information if it is not
recorded.  The third, and primary reason for preserving the original state is
we necessarily treat each module as a black-box. Therefore we want to avoid
copying the module on account of not knowing how expensive the copy actually
will be.

Summary
=======

The current design of the module manager really only directly addresses concern
two (*i.e.*, managing of :ref:`module` pool) and punts the remaining concerns.
More specifically:

#. Dynamic module management

  - Operations go through ``ModuleManager`` class
  - Different configurations handled by module pool
  - Module pool is query-able and modifiable in real time
  - Call graph handles querying and modifying modules comprising program

#. Managing of :ref:`module` pool

   - Explicit component of the current design

#. Modify call graph

   - User/call-graph interactions go through ``ModuleManager``
   - Actual interactions fall to call graph component

#. Run call graph

   - Running the call graph done through ``ModuleManager``, but actual execution
     falls to call graph component
   - Efficiency tied to memoization and falls to the cache and call graph
     components

#. Saving, loading, and restarting a previous session

   - User-facing :ref:`api` is exposed through ``ModuleManager``
   - Falls to cache and call graph component (memoization)
