diff --git a/doc/src/Developer_cxx_vs_c_style.rst b/doc/src/Developer_cxx_vs_c_style.rst index 868b183ec0..0b0526e7f9 100644 --- a/doc/src/Developer_cxx_vs_c_style.rst +++ b/doc/src/Developer_cxx_vs_c_style.rst @@ -32,11 +32,11 @@ abstraction of commonly used functionality. Object oriented code ^^^^^^^^^^^^^^^^^^^^ -LAMMPS is designed to be an object oriented code, that is each simulation -is represented by an instance of the LAMMPS class. When running in parallel, -of course, each MPI process will create such an instance. This can be seen -in the ``main.cpp`` file where the core steps of running a LAMMPS simulation -are the following 3 lines of code: +LAMMPS is designed to be an object oriented code, that is each +simulation is represented by an instance of the LAMMPS class. When +running in parallel, of course, each MPI process will create such an +instance. This can be seen in the ``main.cpp`` file where the core +steps of running a LAMMPS simulation are the following 3 lines of code: .. code-block:: C++ @@ -44,30 +44,164 @@ are the following 3 lines of code: lammps->input->file(); delete lammps; -The first line creates a LAMMPS class instance and passes the command line arguments -and the global communicator to its constructor. The second line tells the LAMMPS -instance to process the input (either from standard input or the provided input file) -until the end. And the third line deletes that instance again. The remainder of -the main.cpp file are for error handling, MPI configuration and other special features. +The first line creates a LAMMPS class instance and passes the command +line arguments and the global communicator to its constructor. The +second line tells the LAMMPS instance to process the input (either from +standard input or the provided input file) until the end. And the third +line deletes that instance again. The remainder of the main.cpp file +are for error handling, MPI configuration and other special features. -In the constructor of the LAMMPS class instance the basic LAMMPS class hierachy + +In the constructor of the LAMMPS class instance the basic LAMMPS class hierarchy is created as shown in :ref:`class-topology`. While processing the input further class instances are created, or deleted, or replaced and specific member functions of specific classes are called to trigger actions like creating atoms, computing forces, computing properties, propagating the system, or writing output. - -Inheritance and Compositing +Compositing and Inheritance =========================== +LAMMPS makes extensive use of the object oriented programming (OOP) +principles of *compositing* and *inheritance*. Classes like the +``LAMMPS`` class are a **composite** containing pointers to instances of +other classes like ``Atom``, ``Comm``, ``Force``, ``Neighbor``, +``Modify``, and so on. Each of these classes implement certain +functionality by storing and manipulating data related to the simulation +and providing member functions that trigger certain actions. Some of +those classes like ``Force`` and a composite again containing instances +of classes describing the force interactions or ``Modify`` containing +and calling fixes and computes. In most cases there is only one instance +of those member classes allowed, but in a few cases there can also be +multiple instances and the parent class is maintaining a list of the +pointers of instantiated classes. + +Changing behavior or adjusting how LAMMPS handles the simulation is +implemented via **inheritance** where different variants of the +functionality are realized by creating *derived* classes that can share +common functionality in their base class and provide a consistent +interface where the derived classes replace (dummy or pure) functions in +the base class. The higher level classes can then call those methods of +the instantiated classes without having to know which specific derived +class variant was instantiated. In the LAMMPS documentation those +derived classes are usually referred to a "styles", e.g. pair styles, +fix styles, atom styles and so on. + +This is the origin of the flexibility of LAMMPS and facilitates for +example to compute forces for very different non-bonded potential +functions by having different pair styles (implemented as different +classes derived from the ``Pair`` class) where the evaluation of the +potential function is confined to the implementation of the individual +classes. Whenever a new :doc:`pair_style` or :doc:`bond_style` or +:doc:`comm_style` or similar command is processed in the LAMMPS input +any existing class instance is deleted and a new instance created in +it place. + +Further code sharing is possible by creating derived classes from the +derived classes (for instance to implement an accelerated version of a +pair style) where then only a subset of the methods are replaced with +the accelerated versions. + Polymorphism ============ +Polymorphism and dynamic dispatch are another OOP feature that play an +important part of how LAMMPS selects which code to execute. In a nutshell, +this is a mechanism where the decision of which member function to call +from a class is determined at runtime and not when the code is compiled. +To enable it, the function has to be declared as ``virtual`` and all +corresponding functions in derived classes should be using the ``override`` +property. Below is a brief example. + +.. code-block:: c++ + + class Base { + public: + virtual ~Base() = default; + void call(); + void normal(); + virtual void poly(); + }; + + void Base::call() { + normal(); + poly(); + } + + class Derived : public Base { + public: + ~Derived() override = default; + void normal(); + void poly() override; + }; + + // [....] + + Base *base1 = new Base(); + Base *base2 = new Derived(); + + base1->call(); + base2->call(); + +The difference in behavior of the ``normal()`` and the ``poly()`` member +functions is in which of the two member functions is called when +executing `base1->call()` and `base2->call()`. Without polymorphism, a +function within the base class will call only member functions within +the same scope, that is ``Base::call()`` will always call +``Base::normal()``. But for the `base2->call()` the call for the +virtual member function will be dispatched to ``Derived::poly()`` +instead. This mechanism allows to always call functions within the +scope of the class type that was used to create the class instance, even +if they are assigned to a pointer using the type of a base class. +Thanks to dynamic dispatch, LAMMPS can even use styles that are loaded +at runtime from a shared object file with the :doc:`plugin command `. + +A special case of virtual functions are so-called pure functions. These +are virtual functions that are initialized to 0 in the class declaration +(see example below). + +.. code-block:: c++ + + class Base { + public: + virtual void pure() = 0; + }; + +This has the effect that it will no longer be possible to create an instance +of the base class and that derived classes **must** implement these classes. +Many of the functions listed with the various styles in the section :doc:`Modify` +are such pure functions. The motivation for this is to define the interface +or API of functions. + +However, there are downsides to this. For example, calls virtual functions +from within a constructor, will not be in the scope of the derived class and thus +it is good practice to either avoid calling them or to provide an explicit scope like +in ``Base::poly()``. Furthermore, any destructors in classes containing +virtual functions should be declared virtual, too, so they are processed +in the expected order before types are removed from dynamic dispatch. + +.. admonition:: Important Notes + + In order to be able to detect incompatibilities and to avoid unexpected + behavior already at compile time, it is crucial that all member functions + that are intended to replace a virtual or pure function use the ``override`` + property keyword. For the same reason it should be avoided to use overloads + or default arguments for virtual functions. + +Style Factories +=============== + +In order to create class instances of the different styles, LAMMPS often +uses a programming pattern called `Factory`. Those are functions that create +an instance of a specific derived class, say ``PairLJCut`` and return a pointer +to the type of the common base class of that style, ``Pair`` in this case. +To associate the factory function with the style keyword, an ``std::map`` +class is used in which function pointers are indexed by their keyword +(for example "lj/cut" for ``PairLJCut`` and "morse" ``PairMorse``). +A couple of typedefs help to keep the code readable and a template function +is used to implement the actual factory functions for the individual classes. I/O and output formatting ^^^^^^^^^^^^^^^^^^^^^^^^^ Memory management ^^^^^^^^^^^^^^^^^ - - diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 1d4c27822b..935a31069f 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -3408,6 +3408,7 @@ typeI typeJ typeN typeargs +typedefs Tz Tzou ub