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