/* * transformation_check.c * * Check that unit cell transformations work * * Copyright © 2012-2020 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2012-2014 Thomas White * * This file is part of CrystFEL. * * CrystFEL is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CrystFEL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CrystFEL. If not, see . * */ #include #include #include #include #include #include #define MAX_REFLS (10*1024) static void intmat_set_all_3x3(IntegerMatrix *m, signed int m11, signed int m12, signed int m13, signed int m21, signed int m22, signed int m23, signed int m31, signed int m32, signed int m33) { intmat_set(m, 0, 0, m11); intmat_set(m, 0, 1, m12); intmat_set(m, 0, 2, m13); intmat_set(m, 1, 0, m21); intmat_set(m, 1, 1, m22); intmat_set(m, 1, 2, m23); intmat_set(m, 2, 0, m31); intmat_set(m, 2, 1, m32); intmat_set(m, 2, 2, m33); } static struct rvec *all_refls(UnitCell *cell, double max_r, int *n) { double asx, asy, asz; double bsx, bsy, bsz; double csx, csy, csz; double ax, ay, az; double bx, by, bz; double cx, cy, cz; signed int h, k, l; int hmax, kmax, lmax; struct rvec *r; int i = 0; r = malloc(sizeof(struct rvec)*MAX_REFLS); if ( r == NULL ) return NULL; cell_get_cartesian(cell, &ax, &ay, &az, &bx, &by, &bz, &cx, &cy, &cz); hmax = max_r * modulus(ax, ay, az); kmax = max_r * modulus(bx, by, bz); lmax = max_r * modulus(cx, cy, cz); cell_get_reciprocal(cell, &asx, &asy, &asz, &bsx, &bsy, &bsz, &csx, &csy, &csz); for ( h=-hmax; h<=hmax; h++ ) { for ( k=-kmax; k<=kmax; k++ ) { for ( l=-lmax; l<=lmax; l++ ) { if ( (h==0) && (k==0) && (l==0) ) continue; if ( forbidden_reflection(cell, h, k, l) ) continue; if ( 2.0*resolution(cell, h, k, l) > max_r ) continue; r[i].u = h*asx + k*bsx + l*csx; r[i].v = h*asy + k*bsy + l*csy; r[i].w = h*asz + k*bsz + l*csz; i++; if ( i == MAX_REFLS ) { ERROR("Too many reflections.\n"); return NULL; } } } } *n = i; return r; } static int tolerance(double a, double b) { if ( fabs(a-b) < 1e6 ) return 1; return 0; } static int find_rvec(struct rvec *l, int n, struct rvec f) { int i; for ( i=0; i na ) return 1; return 0; } static int check_same_reflections(UnitCell *cell, UnitCell *cnew) { struct rvec *vecs; struct rvec *tvecs; int na, nb; /* Check that the two cells predict the same reflections */ vecs = all_refls(cell, 1e9, &na); tvecs = all_refls(cnew, 1e9, &nb); if ( compare_rvecs(vecs, na, tvecs, nb) || compare_rvecs(tvecs, nb, vecs, na) ) { ERROR("********************************************** "); ERROR("Transformed cell didn't predict the same reflections\n"); //printf("---\n"); //for ( i=0; i Before transformation:\n"); cell_print(cell); STATUS("----> The transformation matrix:\n"); intmat_print(tfn); STATUS("----> After transformation:\n"); cell_print(cnew); STATUS("----> After back transformation:\n"); cell_print(cback); if ( pred_test ) { check_same_reflections(cell, cnew); } else { STATUS("Cells not expected to predict the same reflections.\n"); } /* Check we got the parameters back */ cell_get_cartesian(cell, &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &a[8]); cell_get_cartesian(cback, &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], &b[7], &b[8]); for ( i=0; i<9; i++ ) { if ( !tolerance(a[i], b[i]) ) { //STATUS("%e %e\n", a[i], b[i]); fail = 1; } } if ( fail ) { ERROR("********************************************** "); ERROR("Original cell not recovered after transformation:\n"); STATUS("----> After transformation and transformation back:\n"); cell_print(cback); } else { STATUS("The original cell was recovered after inverse transform.\n"); } return fail; } static int check_uncentering(UnitCell *cell) { UnitCell *ct; IntegerMatrix *C; RationalMatrix *Ci; UnitCell *cback; double a[9], b[9]; int i; int fail = 0; STATUS("-----------------------\n"); STATUS("----> Before transformation:\n"); cell_print_full(cell); ct = uncenter_cell(cell, &C, &Ci); if ( ct == NULL ) return 1; STATUS("----> The primitive unit cell:\n"); cell_print(ct); STATUS("----> The matrix to put the centering back:\n"); intmat_print(C); STATUS("----> The recovered centered cell:\n"); cback = cell_transform_intmat(ct, C); cell_print(cback); cell_get_cartesian(cell, &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &a[8]); cell_get_cartesian(cback, &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], &b[7], &b[8]); for ( i=0; i<9; i++ ) { if ( fabs(a[i] - b[i]) > 1e-12 ) { fail = 1; } } if ( fail ) { ERROR("********************************************** "); ERROR("Original cell not recovered after back transformation\n"); } fail += check_same_reflections(cell, ct); cell_free(ct); cell_free(cback); return fail; } static int check_identity(UnitCell *cell, IntegerMatrix *tfn) { UnitCell *cnew; double a[9], b[9]; int i; int fail = 0; STATUS("-----------------------\n"); cnew = cell_transform_intmat(cell, tfn); STATUS("----> Before identity transformation:\n"); cell_print(cell); STATUS("----> The identity transformation matrix:\n"); intmat_print(tfn); STATUS("----> After identity transformation:\n"); cell_print(cnew); cell_get_cartesian(cell, &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &a[8]); cell_get_cartesian(cnew, &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], &b[7], &b[8]); for ( i=0; i<9; i++ ) { if ( !within_tolerance(a[i], b[i], 0.1) ) { fail = 1; //STATUS("%e %e\n", a[i], b[i]); } } if ( fail ) { ERROR("********************************************** "); ERROR("Original cell not recovered after identity transformation:\n"); cell_print(cell); intmat_print(tfn); cell_print(cnew); } return fail; } int main(int argc, char *argv[]) { int fail = 0; UnitCell *cell, *cref; IntegerMatrix *tfn; IntegerMatrix *part1, *part2; gsl_rng *rng; rng = gsl_rng_alloc(gsl_rng_mt19937); cref = cell_new_from_parameters(50e-10, 55e-10, 70e-10, deg2rad(67.0), deg2rad(70.0), deg2rad(77.0)); if ( cref == NULL ) return 1; cell = cell_rotate(cref, random_quaternion(rng)); if ( cell == NULL ) return 1; cell_free(cref); tfn = intmat_identity(3); /* Permutation of axes */ if ( tfn == NULL ) return 1; intmat_set_all_3x3(tfn, 0,0,1, 1,0,0, 0,1,0); fail += check_transformation(cell, tfn, 1); /* Doubling of cell in one direction */ if ( tfn == NULL ) return 1; intmat_set_all_3x3(tfn, 2,0,0, 0,1,0, 0,0,1); fail += check_transformation(cell, tfn, 0); /* Shearing */ if ( tfn == NULL ) return 1; intmat_set_all_3x3(tfn, 1,0,0, 1,1,0, 0,0,1); fail += check_transformation(cell, tfn, 1); /* Crazy */ if ( tfn == NULL ) return 1; intmat_set_all_3x3(tfn, 1,0,0, 1,1,1, 0,0,1); fail += check_transformation(cell, tfn, 0); /* Identity in two parts */ part1 = intmat_identity(3); part2 = intmat_identity(3); if ( tfn == NULL ) return 1; intmat_set_all_3x3(part1, 0,0,-1, 0,1,0, 1,0,0); intmat_set_all_3x3(part2, 0,0,1, 0,1,0, -1,0,0); tfn = intmat_times_intmat(part1, part2); fail += check_identity(cell, tfn); intmat_free(part1); intmat_free(part2); intmat_free(tfn); /* Check some uncentering transformations */ cref = cell_new_from_parameters(50e-10, 50e-10, 50e-10, deg2rad(90.0), deg2rad(90.0), deg2rad(90.0)); cell_set_lattice_type(cref, L_CUBIC); cell_set_centering(cref, 'F'); fail += check_uncentering(cref); cell_set_centering(cref, 'I'); fail += check_uncentering(cref); cref = cell_new_from_parameters(50e-10, 50e-10, 90e-10, deg2rad(90.0), deg2rad(90.0), deg2rad(90.0)); cell_set_lattice_type(cref, L_TETRAGONAL); cell_set_centering(cref, 'I'); cell_set_unique_axis(cref, 'c'); fail += check_uncentering(cref); cref = cell_new_from_parameters(90e-10, 50e-10, 50e-10, deg2rad(90.0), deg2rad(90.0), deg2rad(90.0)); cell_set_lattice_type(cref, L_TETRAGONAL); cell_set_centering(cref, 'I'); cell_set_unique_axis(cref, 'a'); fail += check_uncentering(cref); cref = cell_new_from_parameters(50e-10, 60e-10, 70e-10, deg2rad(90.0), deg2rad(90.0), deg2rad(90.0)); cell_set_lattice_type(cref, L_ORTHORHOMBIC); cell_set_centering(cref, 'C'); fail += check_uncentering(cref); cell_set_centering(cref, 'A'); fail += check_uncentering(cref); cell_set_centering(cref, 'B'); fail += check_uncentering(cref); cref = cell_new_from_parameters(50e-10, 60e-10, 70e-10, deg2rad(90.0), deg2rad(100.0), deg2rad(90.0)); cell_set_lattice_type(cref, L_MONOCLINIC); cell_set_unique_axis(cref, 'b'); cell_set_centering(cref, 'C'); fail += check_uncentering(cref); cell_set_centering(cref, 'I'); fail += check_uncentering(cref); cell_set_centering(cref, 'A'); fail += check_uncentering(cref); cref = cell_new_from_parameters(50e-10, 50e-10, 70e-10, deg2rad(90.0), deg2rad(90.0), deg2rad(120.0)); cell_set_lattice_type(cref, L_HEXAGONAL); cell_set_unique_axis(cref, 'c'); cell_set_centering(cref, 'H'); fail += check_uncentering(cref); cell_free(cell); gsl_rng_free(rng); return fail; }