|
ampsci
High-precision calculations for one- and two-valence atomic systems
|
How to write your own DiracOperator for use in ampsci modules.
Operators in ampsci are single-particle tensor operators of rank \( k \), acting on DiracSpinor objects. They are represented by the DiracOperator::TensorOperator virtual base class. All built-in operators (E1, hfs, pnc, ...) derive from this class, and you can add your own the same way.
./ampsci -o command-line option so see a list of available operators./ampsci -o <OperatorName> for list of input options for specific operatorIf you need a matrix element that is not already implemented, the operator framework gives you:
Module::MatrixElements, RPA, structure radiation).See DiracOperator::TensorOperator for the full interface.
The base class computes reduced matrix elements (RMEs) as
\[ \langle a \| \hat{h} \| b \rangle = A_{ab} \cdot R_{ab} \]
where \( A_{ab} \) is the angular factor (see angularF()) and the default radial integral is
\[ R_{ab} = c \int_0^\infty v(r)\left( C_{ff}\,f_a f_b + C_{fg}\,f_a g_b + C_{gf}\,g_a f_b + C_{gg}\,g_a g_b \right)\,{\rm d}r \]
where \( c \) is an overall constant, \( v(r) \) is an optional radial function, and the \( C_{xy} \) are angular coefficients (defaulting to \( C_{ff}=C_{gg}=1 \), \( C_{fg}=C_{gf}=0 \)).
Done in three steps (details below):
TensorOperator, implement angularF() and any needed overrides.generate() factory – required for runtime lookup by name; without it the operator cannot be used from input files or by any module.Register<T> entry in a .cpp file so ampsci discovers it at startup.By default, put everything in a single new .cpp file (class, generate(), and registration), dropped anywhere in the source tree – no header needed. Split into .hpp/.cpp only if the class needs to be visible to other files (i.e., if we plan to add this to the core ampsci operator list). The .cpp file may also live outside the source tree (see "Built-in vs. external operators" below).
First, derive from DiracOperator::TensorOperator and pass the operator properties to the base constructor, for example:
Key parameters passed to the base constructor:
rank – tensor rank \( k \) (integer).parity – Parity::even or Parity::odd.constant – overall multiplicative factor \( c \).vec – radial function \( v(r) \) as std::vector<double> (may be empty, equivalent to 1).RorI – Realness::real or Realness::imaginary.freq_dep – set true if the operator depends on frequency or momentum transfer.You can add any other public/private functions or variables if you require.
rank, parity, and Realness correct, since they dictate the selection rules and symmetry properties.For operators whose radial integral fits the default form above:
angularF() (required)angularCff(), angularCgg(), angularCfg(), angularCgf()The radial integral is then handled automatically.
If the radial integral cannot be expressed in the default form (e.g., it involves derivatives, non-local terms, or mixing not captured by the \( C_{xy} \) coefficients), override both radial_rhs() and radialIntegral() consistently:
radial_rhs(ka, Fb) returns \( \delta F_b \) such that \( F_a \cdot \delta F_b = R_{ab} \).radialIntegral(Fa, Fb) returns \( R_{ab} \) directly.angularCff(), angularCgg(), angularCfg(), angularCgf() are not used (unless you explicitely use them in your overridden radial functions)For frequency-dependent operators, pass freq_dep=true to the base constructor and override updateFrequency(double omega). This is called by modules such as MatrixElements whenever the frequency changes (e.g., when omega = each is set in the input). The base implementation aborts at runtime if this is every required but not overridden to prevent silent failures.
generate()Add this static function to your class so ampsci can find the operator by name (from input files, ampsci -o, and modules like MatrixElements). The signature and return value must be exactly like:
The implementation details are up to you; for most simple operators, they will be very simple (or even blank). You don't have to use input or wf, but they are available for reading user options and accessing the wavefunction/nucleaus/grid etc. For example, an operator that depends on the radial grid, the nuclear charge, as well as two parameters, \( c \) and \( n \), may be written like:
input.check(...) is optional but strongly recommended – it is what populates ampsci -o and catches typos in user input. Return nullptr for the help case.if (input.has_option("help")) return nullptr;ampsci -o command-line helper works, and how users will know what options your operator takesAdd a Register<T> entry, where T is your operator class name, into the same .cpp file as your class (inside an anonymous namespace) so ampsci discovers it at startup:
(If your operator is written into a .hpp file instead, add this to a seperate .cpp file – see "Built-in vs. external operators" below). The first argument is the name used in input files (e.g., MatrixElements::MyOp{}); the second is shown by ampsci -o.
External (recommended): put everything in a single .cpp file that lives outside the main ampsci source code, and add it to EXTERNAL_OPERATORS in your Makefile – no changes to the ampsci source required:
generate() – you cannot use it directly as a concrete C++ type. This is sufficient for the vast majority of use cases.Built-in (contributing to the ampsci repo): define the class in src/DiracOperator/Operators/MyOperator.hpp, add a Register<MyOperator> line to src/DiracOperator/RegisterOperators.cpp, and add the header to src/DiracOperator/Operators/include.hpp.
Browse src/DiracOperator/Operators/ for a wide range of examples:
Ek.hpp.hfs.hpp.The following links are to the detailed documentation for the specific namespaces/classes that will be most useful for writing your own operator:
wf.valence(), core via wf.core(), basis via wf.basis().