/*! @page pag_concepts_emitter Emitter configurations

\section sec_emitters Emitter configurations

\subsection sec_emit_intro Introduction

Since emitters contribute incoherently to the diffraction pattern,
it should make no difference how the emitters are grouped and calculated.
This fact can be used to distribute a calculation over multiple parallel processes
if each process calculates the diffraction pattern coming from one particular emitter atom.
In effect, some calculation codes are implemented for a single emitter per calculation.

With PMSCO, it is easy to distribute the emitters over parallel processes.
The project just declares the number of emitters and returns one specific cluster per emitter.
In the simplest case, this means that the emitter attribute of the cluster atoms is set differently,
while the atomic coordinates are the same for all clusters generated.
PMSCO takes care of dispatching the clusters to multiple calculation processes
depending on the number of allocated MPI processes
as well as summing up the resulting diffraction patterns.

In addition, the emitter framework also supports that clusters are tailored to a specific emitter configuration.
Suppose that the unit cell contains a large number of inequivalent emitters.
If all emitters had to be included in a single calculation,
the cluster would grow very large and the calculation would include many long scattering paths
that effectively did not contribute intensity to the final result.
Splitting a large cluster into small ones built locally around one emitter
can provide a significant performance gain in complex systems.

Note that the emitter framework does not require that an emitter _configuration_ contains only one emitter _atom_.
It is up to the project to define how many emitter configurations there are and what they encompass.
This should, however, normally not be necessary.
To avoid confusion, it is recommended to declare exactly one emitter atom per configuration.


\subsection sec_emit_implement Implementation

There are several implementation routes with varying complexity.
Which route to take can depend on the complexity of the system and/or the programming skills of the user.
The following class diagram illustrates the classes and packages involved in cluster generation.

@startuml "class diagram for cluster generation"

package pmsco {
    class Project {
        cluster_generator
        export_cluster()
    }

    abstract class ClusterGenerator {
        project
        {abstract} count_emitters()
        {abstract} create_cluster()
    }

    class LegacyClusterGenerator {
        project
        count_emitters()
        create_cluster()
    }
}

package "user project" {
    class UserClusterGenerator {
        project
        count_emitters()
        create_cluster()
    }

    note bottom : for complex cluster

    class UserProject {
        count_emitters()
        create_cluster()
    }

    note bottom : for simple cluster

}

Project <|-- UserProject
ClusterGenerator <|-- LegacyClusterGenerator
ClusterGenerator <|-- UserClusterGenerator
Project *-- ClusterGenerator
UserProject .> LegacyClusterGenerator
UserProject .> UserClusterGenerator

@enduml

In general, the cluster is generated by calls to the project's cluster_generator object.
This can be either a custom generator class derived from pmsco.cluster.ClusterGenerator
or the default pmsco.cluster.LegacyClusterGenerator which calls the UserProject.
For simple clusters, it may be sufficient to implement the cluster directly in the user project class
(UserProject in the diagram).
For more complex systems, it is recommended to implement a custom cluster generator class
(UserClusterGenerator).


\subsubsection sec_emit_implement_legacy Static cluster implemented in project methods

This is the most simple route as it requires the implementation of one or two methods of the user project class.
It can be used for single-emitter and multi-emitter problems.
This implementation is active while a pmsco.cluster.LegacyClusterGenerator
is assigned to the project's cluster_generator attribute.

1. Implement a count_emitters method in your project class
   if the project uses more than one emitter configurations.
   It must have same method contract as pmsco.cluster.ClusterGenerator.count_emitters.
   Specifically, it must return the number of emitter configurations of a given model, scan and domain.
   If there is only one configuration, the method does not need to be implemented.

2. Implement a create_cluster method in your project class.
   It must have same method contract as pmsco.cluster.ClusterGenerator.create_cluster.
   Specifically, it must return a cluster.Cluster object for the given model, scan, domain and emitter configuration.
   The emitter atoms must be marked according to the emitter configuration specified by the index argument.
   Note that, depending on the index.emit argument, all emitter atoms must be marked
   or only the ones of the corresponding emitter configuration.

3. (Optionally) override the pmsco.project.Project.combine_emitters method
   if the emitters should be added with non-uniform weights.

Although it's possible to produce emitter-dependent clusters using this approach,
this is usually not recommended.
Rather, the generator approach described below should be followed in this case.


\subsubsection sec_emit_implement_generator Static cluster implemented by generator class

The preferred way of creating clusters is to implement a _generator_ class
because it is the most scalable way from simple to complex systems.
In addition, one cluster generator class can be quickly exchanged for another
if there are multiple possibilities.

1. Implement a cluster generator class which inherits from pmsco.cluster.ClusterGenerator
   in your project module.

2. Implement the create_cluster and count_emitters methods of the generator.
   The method contracts are the same as the ones described in the previous paragraph,
   just in the context of a separate class.

3. Initialize an instance of the generator and assign it to the project.cluster_generator attribute
   in the initialization of your project.


\subsubsection sec_emit_implement_local Local clusters implemented by generator class

The basic method contract outlined in the previous paragraph is equally applicable to the case
where a local cluster is generated for each emitter configuration.
Again, the generator class with the two methods (count_emitters and create_cluster) is the minimum requirement.
However, for ease of code maintenance and/or for improved performance of large clusters,
some internal structure may be helpful.

Suppose that the system consists of a large supercell containing many emitters
and that a small cluster shall be built for each emitter configuration.
During the calculations, the generator will receive several calls to the count_emitters and create_cluster methods.
Every time the model and index are the same, the functions must return the same result.
Thus, most importantly, the implementation must make sure that the results are fully deterministic.
Second, depending on the complexity, it could be more efficient to cache a cluster for later use.

One way to reduce the complexity is to introduce a _master cluster_
from which the emitter configurations and individual clusters are derived.

1. Implement a master_cluster method with the same arguments and result types as create_cluster.
   The method returns a full cluster of the supercell and its neighbouring cells.
   All inequivalent emitters are marked (which determines the number of emitter configurations).

2. Decorate the master_cluster with pmsco.dispatch.CachedCalculationMethod.
   This pre-defined decorator transparently caches the cluster
   so that subsequent calls with the same arguments do not re-create the cluster but return the cached one.

3. The count_emitters method can simply return the emitter count of the master cluster.

4. The create_cluster method calls master_cluster() and extracts the region
   corresponding to the requested emitter configuration.


\subsection sec_emit_report Reporting

The pmsco.project.Project class implements a method that saves a cluster to two XYZ files,
one containing the coordinates of all atoms
and one containing only the coordinates of the emitters.

The method is called for each cluster that is passed to the calculator, i.e., each emitter index.
You may override the method in your project to alter the reporting.

*/