/* * symmetry.c * * Symmetry * * (c) 2006-2010 Thomas White * * Part of CrystFEL - crystallography with a FEL * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "utils.h" #ifdef DEBUG #define SYM_DEBUG STATUS #else /* DEBUG */ #define SYM_DEBUG(...) #endif /* DEBUG */ /* Check if a reflection is in the asymmetric unit cell */ static int check_cond(signed int h, signed int k, signed int l, const char *sym) { /* Triclinic */ if ( strcmp(sym, "1") == 0 ) return ( 1 ); if ( strcmp(sym, "-1") == 0 ) return ( (l>0) || ( (l==0) && (k>0) ) || ( (l==0) && (k==0) && (h>=0) ) ); /* Orthorhombic */ if ( strcmp(sym, "mmm") == 0 ) return ( (h>=0) && (k>=0) && (l>=0) ); if ( strcmp(sym, "222") == 0 ) return ( (h>=0) && (k>0) ) || ( (h>0) && (k>=0) ) || ( (h>=0) && (k==0) && (l>=0) ) || ( (h==0) && (k>=0) && (l>=0) ); /* Tetragonal */ if ( strcmp(sym, "4/mmm") == 0 ) return ( (((h>0) && (k>=0)) || ((h==0) && (k==0))) && (l>=0) && (h>=k) ); /* Like 6/mmm */ if ( strcmp(sym, "422") == 0 ) return ( (((h>0) && (k>=0)) || ((h==0) && (k==0))) && (h>=k) ); if ( strcmp(sym, "4/m") == 0 ) return ( (((h>0) && (k>=0)) || ((h==0) && (k==0))) && (l>=0) ); if ( strcmp(sym, "4") == 0 ) return ( ((h>0) && (k>=0)) || ((h==0) && (k==0)) ); /* Hexagonal */ if ( strcmp(sym, "6") == 0 ) return ( ((h>0) && (k>=0)) || ((h==0) && (k==0)) ); if ( strcmp(sym, "6/m") == 0 ) return ( (((h>0) && (k>=0)) || ((h==0) && (k==0))) && (l>=0) ); if ( strcmp(sym, "6/mmm") == 0 ) return ( (((h>0) && (k>=0)) || ((h==0) && (k==0))) && (l>=0) && (h>=k) ); /* TODO: Add more groups here */ return 1; } /* Macros for checking the above conditions and returning if satisfied */ #define CHECK_COND(h, k, l, sym) \ if ( check_cond((h), (k), (l), (sym)) ) { \ *hp = (h); *kp = (k); *lp = (l); \ return; \ } int num_general_equivs(const char *sym) { /* Triclinic */ if ( strcmp(sym, "1") == 0 ) return 1; if ( strcmp(sym, "-1") == 0 ) return 2; /* Orthorhombic */ if ( strcmp(sym, "222") == 0 ) return 4; if ( strcmp(sym, "mmm") == 0 ) return 8; /* Tetragonal */ if ( strcmp(sym, "4") == 0 ) return 4; if ( strcmp(sym, "4/m") == 0 ) return 8; if ( strcmp(sym, "422") == 0 ) return 8; if ( strcmp(sym, "4/mmm") == 0 ) return 16; /* Hexagonal */ if ( strcmp(sym, "6") == 0 ) return 6; if ( strcmp(sym, "6/m") == 0 ) return 12; if ( strcmp(sym, "6/mmm") == 0 ) return 24; /* TODO: Add more groups here */ return 1; } void get_general_equiv(signed int h, signed int k, signed int l, signed int *he, signed int *ke, signed int *le, const char *sym, int idx) { signed int i = -h-k; /* The returned indices when idx=0 *must* be the same as the input. * After that, the order does not matter. */ if ( strcmp(sym, "1") == 0 ) { *he = h; *ke = k; *le = l; return; } if ( strcmp(sym, "-1") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = -h; *ke = -k; *le = -l; return; } } if ( strcmp(sym, "222") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = -h; *ke = -k; *le = l; return; case 2 : *he = -h; *ke = k; *le = -l; return; case 3 : *he = h; *ke = -k; *le = -l; return; } } if ( strcmp(sym, "mmm") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = -h; *ke = -k; *le = l; return; case 2 : *he = -h; *ke = k; *le = -l; return; case 3 : *he = h; *ke = -k; *le = -l; return; case 4 : *he = -h; *ke = -k; *le = -l; return; case 5 : *he = h; *ke = k; *le = -l; return; case 6 : *he = h; *ke = -k; *le = l; return; case 7 : *he = -h; *ke = k; *le = l; return; } } if ( strcmp(sym, "4") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = -h; *ke = -k; *le = l; return; case 2 : *he = -k; *ke = h; *le = l; return; case 3 : *he = k; *ke = -h; *le = l; return; } } if ( strcmp(sym, "4/m") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = -h; *ke = -k; *le = l; return; case 2 : *he = -k; *ke = h; *le = l; return; case 3 : *he = k; *ke = -h; *le = l; return; case 4 : *he = -h; *ke = -k; *le = -l; return; case 5 : *he = h; *ke = k; *le = -l; return; case 6 : *he = k; *ke = -h; *le = -l; return; case 7 : *he = -k; *ke = h; *le = -l; return; } } if ( strcmp(sym, "422") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = -h; *ke = -k; *le = l; return; case 2 : *he = -k; *ke = h; *le = l; return; case 3 : *he = k; *ke = -h; *le = l; return; case 4 : *he = -h; *ke = k; *le = -l; return; case 5 : *he = h; *ke = -k; *le = -l; return; case 6 : *he = k; *ke = h; *le = -l; return; case 7 : *he = -k; *ke = -h; *le = -l; return; } } if ( strcmp(sym, "4/mmm") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = -h; *ke = -k; *le = l; return; case 2 : *he = -k; *ke = h; *le = l; return; case 3 : *he = k; *ke = -h; *le = l; return; case 4 : *he = -h; *ke = k; *le = -l; return; case 5 : *he = h; *ke = -k; *le = -l; return; case 6 : *he = k; *ke = h; *le = -l; return; case 7 : *he = -k; *ke = -h; *le = -l; return; case 8 : *he = -h; *ke = -k; *le = -l; return; case 9 : *he = h; *ke = k; *le = -l; return; case 10 : *he = k; *ke = -h; *le = -l; return; case 11 : *he = -k; *ke = h; *le = -l; return; case 12 : *he = h; *ke = -k; *le = l; return; case 13 : *he = -h; *ke = k; *le = l; return; case 14 : *he = -k; *ke = -h; *le = l; return; case 15 : *he = k; *ke = h; *le = l; return; } } if ( strcmp(sym, "6") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = i; *ke = h; *le = l; return; case 2 : *he = k; *ke = i; *le = l; return; case 3 : *he = -h; *ke = -k; *le = l; return; case 4 : *he = -i; *ke = -h; *le = l; return; case 5 : *he = -k; *ke = -i; *le = l; return; } } if ( strcmp(sym, "6/m") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = i; *ke = h; *le = l; return; case 2 : *he = k; *ke = i; *le = l; return; case 3 : *he = -h; *ke = -k; *le = l; return; case 4 : *he = -i; *ke = -h; *le = l; return; case 5 : *he = -k; *ke = -i; *le = l; return; case 6 : *he = h; *ke = k; *le = -l; return; case 7 : *he = i; *ke = h; *le = -l; return; case 8 : *he = k; *ke = i; *le = -l; return; case 9 : *he = -h; *ke = -k; *le = -l; return; case 10 : *he = -i; *ke = -h; *le = -l; return; case 11 : *he = -k; *ke = -i; *le = -l; return; } } if ( strcmp(sym, "6/mmm") == 0 ) { switch ( idx ) { case 0 : *he = h; *ke = k; *le = l; return; case 1 : *he = i; *ke = h; *le = l; return; case 2 : *he = k; *ke = i; *le = l; return; case 3 : *he = -h; *ke = -k; *le = l; return; case 4 : *he = -i; *ke = -h; *le = l; return; case 5 : *he = -k; *ke = -i; *le = l; return; case 6 : *he = k; *ke = h; *le = -l; return; case 7 : *he = h; *ke = i; *le = -l; return; case 8 : *he = i; *ke = k; *le = -l; return; case 9 : *he = -k; *ke = -h; *le = -l; return; case 10 : *he = -h; *ke = -i; *le = -l; return; case 11 : *he = -i; *ke = -k; *le = -l; return; case 12 : *he = -h; *ke = -k; *le = -l; return; case 13 : *he = -i; *ke = -h; *le = -l; return; case 14 : *he = -k; *ke = -i; *le = -l; return; case 15 : *he = h; *ke = k; *le = -l; return; case 16 : *he = i; *ke = h; *le = -l; return; case 17 : *he = k; *ke = i; *le = -l; return; case 18 : *he = -k; *ke = -h; *le = l; return; case 19 : *he = -h; *ke = -i; *le = l; return; case 20 : *he = -i; *ke = -k; *le = l; return; case 21 : *he = k; *ke = h; *le = l; return; case 22 : *he = h; *ke = i; *le = l; return; case 23 : *he = i; *ke = k; *le = l; return; } } /* TODO: Add more groups here */ ERROR("Unrecognised symmetry '%s'\n", sym); abort(); } /* Given a reflection and a point group, this returns (by reference) the indices * of the "idx"th equivalent reflection, taking special positions into account. * It returns "idx" if successful. Otherwise, it returns the number of * equivalents for the particular reflection (taking special positions into * account). Therefore, set idx=-1 to get the number of equivalents. */ static int special_position(signed int hs, signed int ks, signed int ls, signed int *hp, signed int *kp, signed int *lp, const char *sym, signed int idx) { int n_general; int i; ReflItemList *equivs; int n_equivs = 0; if ( idx == 0 ) { /* Index zero is always the original reflection */ *hp = hs; *kp = ks; *lp = ls; return 0; } equivs = new_items(); n_general = num_general_equivs(sym); for ( i=0; ih; k = item->k; l = item->l; new_ops = coset_decomp(h, k, l, holo, mero); union_op_items(ops, new_ops); delete_items(new_ops); } /* Idiot check */ actual = num_items(ops); expected = num_general_equivs(holo) / num_general_equivs(mero); if ( actual != expected ) { ERROR("Found %i twin possibilities, but expected %i.\n", actual, expected); ERROR("I couldn't find the number of twin laws that I expected." " This is an internal error, and shouldn't happen. " "Sorry.\n"); abort(); } return ops; } int find_unique_equiv(ReflItemList *items, signed int h, signed int k, signed int l, const char *mero, signed int *hu, signed int *ku, signed int *lu) { int i; int found = 0; for ( i=0; ih, it->k, it->l, 1); } for ( i=0; ih, it->k, it->l, sym); j++ ) { signed int he, ke, le; get_equiv(it->h, it->k, it->l, &he, &ke, &le, sym, j); if ( abs(he) > INDMAX ) continue; if ( abs(le) > INDMAX ) continue; if ( abs(ke) > INDMAX ) continue; found += lookup_flag(flags, he, ke, le); } if ( found > 1 ) { free(flags); return 1; /* Symmetry is wrong! */ } } free(flags); return 0; }