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.

enumerator orb_s = 0
[source]

s orbital

enumerator orb_pm1 = 1
[source]

py orbital

enumerator orb_p_0
[source]

pz orbital

enumerator orb_p_1
[source]

px orbital

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)

double normal_vector[3]
[source]

the normal vector around which to generate the symmetry, in real space (irrelevant for ‘I’, ‘F’, ‘E’)

double angle
[source]

angle of rotation (relevant for ‘R’, ‘r’, ‘S’)

struct site_descr_t
[source]

defines a single orbital (in the language of n_orb defined in diverge_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.function and site_descr_t.amplitude arrays, i.e., number of real harmonic basis functions of the site

double xaxis[MAX_ORBS_PER_SITE][3]
[source]

orientation of the x axis of each of the real harmonic basis functions defined in the amplitude/function arrays. defaults to the cartesian x axis, OPTIONAL.

double zaxis[MAX_ORBS_PER_SITE][3]
[source]

orientation of the z axis of each of the real harmonic basis functions defined in the amplitude/function arrays. defaults to the cartesian z axis, OPTIONAL.

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_t array orbs and a symmetry operation described by the sym_op_t array 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_orbs

  • n_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)