Symmetries
Note
For models with many orbitals or inaccurate positions, implementing symmetries can be difficult. Remember to check:
the orbital ordering in the hopping matrix elements. Quick ‘n dirty solution is to just swap it and see if it works.
that your orbital axes align with the orbital axes defined in the previous steps of obtaining the TB model
the accuracy of what is considered “zero” or “one” in some of the symmetrization routines of divERGe. These values could previously be changed upon recompilation, but now they are accessible at run time through
diverge_generate_symm_maps_set_precision().build your symmetry operations “bottom-up”, i.e. start with a small subgroup of your full point group and see if that works.
If none of that quickly fixes your issues, you’ll have to dig into the TB model and accurately map out its symmetries by yourself.
Example: Hubbard Model
For the automatic patch finding algorithm, symmetries are strongly
recommended. Truncated unity FRG benefits from symmetries by reducing the
computational cost. Grid FRG requires symmetries for re-symmetrization of the
loop objects for multi-site models. As the model we implement in this example is
a square lattice Hubbard model, we write all the elements of C4v into the
symmetry arrays. Not that the snippet below makes use of the Eigen3 classes
imported in diverge_Eigen3.hpp. From the python interface, consider
diverge_model_t.orb_symmetries as a (n_sym, n_orb, n_orb) array of
complex128_t and diverge_model_t.rs_symmetries as a (n_sym, 3, 3)
array of doubles. The former must be allocated (e.g. via
diverge_mem_alloc_complex128_t(), or just plain malloc/calloc).
mod->n_sym = 8;
mod->orb_symmetries = (complex128_t*)diverge_mem_alloc_complex128_t(8);
for (index_t s=0; s<8; ++s) {
mod->orb_symmetries[s] = 1.0;
// map rs_symmetries[s] to 3x3 matrix
Map<Mat3d> symmat(mod->rs_symmetries[s][0]);
// and fill with appropriate symmetry operation
switch (s) {
case 0: symmat = Mat3d::Identity(); break;
case 1: symmat = Mat3d::Identity(); symmat.block<2,2>(0,0) = Rot2d(M_PI).toRotationMatrix(); break;
case 2: symmat = Mat3d::Identity(); symmat.block<2,2>(0,0) = Rot2d(0.5*M_PI).toRotationMatrix(); break;
case 3: symmat = Mat3d::Identity(); symmat.block<2,2>(0,0) = Rot2d(1.5*M_PI).toRotationMatrix(); break;
case 4: symmat = Mat3d::Identity(); symmat(0,0) = -1; break;
case 5: symmat = Mat3d::Identity(); symmat(1,1) = -1; break;
case 6: symmat = Mat3d::Identity(); symmat(0,0) = -1;
symmat.block<2,2>(0,0) = Rot2d(0.25*M_PI)*symmat.block<2,2>(0,0)*Rot2d(-0.25*M_PI); break;
case 7: symmat = Mat3d::Identity(); symmat(1,1) = -1;
symmat.block<2,2>(0,0) = Rot2d(0.25*M_PI)*symmat.block<2,2>(0,0)*Rot2d(-0.25*M_PI); break;
default: break;
}
}
Symmetry Generators
Since symmetries can be complex to construct by hand in general models, with
potentially many orbitals and non-SU2 symmetries we offer a tool to
automatically generate symmetries, the function
diverge_generate_symm_trafo().
void diverge_generate_symm_trafo( index_t n_spin,
const site_descr_t* orbs, index_t n_orbs, const sym_op_t* syms,
index_t n_syms, double* rs_trafo, complex128_t* orb_trafo );
This function requires no model instance, but needs the number of spins
(\(n_\mathrm{spin} = 2S+1\), but only 1 if the model will be \(SU(2)\)
symmetric) and the number of orbitals, \(n_\mathrm{orbs}\) as input
parameters. For each of these orbitals, the user has to provide a
site_descr_t object describing the real-spherical
harmonics the atomic orbital consists of, giving for each an amplitude, the
spherical harmonic encoded by the real_harmonics_t
enum, the number of spherical harmonics for this orbital, and optionally the
orientation of the x and z axis of each individual real harmonic. To describe a
symmetry, the sym_op_t array syms in combination with
n_syms is used. If n_syms > 1 we subsequently execute the operation
defined in syms[i] for i = 0, 1, ..., n_syms one after another. For
example for a three orbital model containing \(d_{x^2-y^2}\), \(p_+\)
and \(p_-\) orbitals, we obtain the symmetries as
mod->n_sym = 8;
mod->orb_symmetries = (complex128_t*)diverge_mem_alloc_complex128_t(8);
site_descr_t* sites = (site_descr_t*)calloc(3, sizeof(site_descr_t));
sites[0].n_functions = 1;
sites[0].amplitude[0] = 1.;
sites[0].function[0] = orb_dx2y2;
sites[1].n_functions = 2;
sites[1].amplitude[0] = 1.;
sites[1].function[0] = orb_dpx;
sites[1].amplitude[1] = I128;
sites[1].function[1] = orb_dpy;
sites[2].n_functions = 2;
sites[2].amplitude[0] = 1.;
sites[2].function[0] = orb_dpx;
sites[2].amplitude[1] = -I128;
sites[2].function[1] = orb_dpy;
sym_op_t* curr_symm = (sym_op_t*)calloc(3, sizeof(sym_op_t));
curr_symm[0].type = 'E';
diverge_generate_symm_trafo(1, sites, 3, curr_symm, 1,
&(mod->rs_symmetries[0][0][0]), mod->orb_symmetries);
curr_symm[0].type = 'R';
curr_symm[0].normal_vector[2] = 1.;
curr_symm[0].angle = 90;
diverge_generate_symm_trafo(1, sites, 3, curr_symm, 1,
&(mod->rs_symmetries[1][0][0]), mod->orb_symmetries+9);
curr_symm[0].angle = 180;
diverge_generate_symm_trafo(1, sites, 3, curr_symm, 1,
&(mod->rs_symmetries[2][0][0]), mod->orb_symmetries+9*2);
curr_symm[0].angle = 270;
diverge_generate_symm_trafo(1, sites, 3, curr_symm, 1,
&(mod->rs_symmetries[3][0][0]), mod->orb_symmetries+9*3);
curr_symm[0].type = 'M';
curr_symm[0].normal_vector[0] = 1;
curr_symm[0].normal_vector[1] = 0;
curr_symm[0].normal_vector[2] = 0;
diverge_generate_symm_trafo(1, sites, 3, curr_symm, 1,
&(mod->rs_symmetries[4][0][0]), mod->orb_symmetries+9*4);
curr_symm[0].normal_vector[0] = 0;
curr_symm[0].normal_vector[1] = 1;
curr_symm[0].normal_vector[2] = 0;
diverge_generate_symm_trafo(1, sites, 3, curr_symm, 1,
&(mod->rs_symmetries[5][0][0]), mod->orb_symmetries+9*5);
curr_symm[0].normal_vector[0] = 1;
curr_symm[0].normal_vector[1] = 1;
curr_symm[0].normal_vector[2] = 0;
diverge_generate_symm_trafo(1, sites, 3, curr_symm, 1,
&(mod->rs_symmetries[6][0][0]), mod->orb_symmetries+9*6);
curr_symm[0].type = 'R';
curr_symm[0].normal_vector[0] = 0;
curr_symm[0].normal_vector[1] = 0;
curr_symm[0].normal_vector[2] = 1;
curr_symm[0].angle = -45;
curr_symm[1].type = 'M';
curr_symm[1].normal_vector[0] = 1;
curr_symm[1].normal_vector[1] = 0;
curr_symm[1].normal_vector[2] = 0;
curr_symm[2].type = 'R';
curr_symm[2].normal_vector[0] = 0;
curr_symm[2].normal_vector[1] = 0;
curr_symm[2].normal_vector[2] = 1;
curr_symm[2].angle = 45;
diverge_generate_symm_trafo(1, sites, 3, curr_symm, 1,
&(mod->rs_symmetries[7][0][0]), mod->orb_symmetries+9*7);
where we constructed one of the diagonal mirrors by concatenating three symmetry operations.
-
enum real_harmonics_t
[source] enumerates all possible orbitals supported by divERGe’s symmetry generator. For orbitals other than \(s\), \(p_y\), \(p_z\), \(p_x\), see the source code.
-
struct sym_op_t
[source] defines one symmetry operation
-
char type
[source] single character: possible choices are ‘R’(rotation), ‘r’(rotoinversion), ‘S’(spin rotation), ‘M’(mirror), ‘I’(inversion), ‘F’(spin flip), ‘E’(identity)
-
char type
-
struct site_descr_t
[source] defines a single orbital (in the language of
n_orbdefined indiverge_model_t) on a single site.-
complex128_t amplitude[MAX_ORBS_PER_SITE]
[source] array of amplitudes in the real harmonic basis
-
real_harmonics_t function[MAX_ORBS_PER_SITE]
[source] array of real harmonics defining the basis (see
real_harmonics_t)
-
index_t n_functions
[source] length of the
site_descr_t.functionandsite_descr_t.amplitudearrays, i.e., number of real harmonic basis functions of the site
-
complex128_t amplitude[MAX_ORBS_PER_SITE]
-
void diverge_generate_symm_trafo(index_t n_spin, const site_descr_t *orbs, index_t n_orbs, const sym_op_t *syms, index_t n_syms, double *rs_trafo, complex128_t *orb_trafo)
[source] generates symmetry transform for the orbitals decribed in the
site_descr_tarray orbs and a symmetry operation described by thesym_op_tarray syms. Outputs the orbital and real-space symmetry matrices- Parameters:
n_spin (index_t) – number of spins
orbs (site_descr_t*) – site descriptor array of length
n_orbsn_orbs (index_t) – number of ‘orbitals’ (cf.
diverge_model_t)syms (sym_op_t*) – symmetry operation array (of length
n_syms) of the atomic symmetry operations, first element (syms[0]) acts first, then second (syms[1]), etc.n_syms (index_t) – number of atomic symmetry operations to build symmetry operation at hand
rs_trafo (double*) – output for the realspace matrix (size must be >= 9)
orb_trafo (complex128_t*) – output for the orbital space matrix (size must be >= n_orbs*n_orbs)