Files
sics/simidx.c
koennecke 361ee9ebea - Reworked the connection object and the IO system
- Reworked the support for TRICS
- Added a second generation motor
2009-02-03 08:05:39 +00:00

680 lines
18 KiB
C

/**
* This is the simple reflection indexer. The algorithm is simple:
* - Three non coplanar reflections at low two theta are selected.
* - Candidate indices are calculated from the lattice constants
* - Permutations of the generated indices are changed for a couple
* of conditions:
* -- Does the angle between the reflections matches the expectaions
* -- Do the reflections form a right handed set
* UB matrics matching these conditions are calculated and stored
* for later retrieval.
*
* The software is organized such that this is a standalone module.
* For reasons of laziness I use statically sized arrays for
* candidate indices and for the reflection list. This is simple and
* this code is for small problems anyway.
*
* Mark Koennecke, August 2008
*/
#include <math.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "simidx.h"
#include "cell.h"
#include "vector.h"
#include "ubfour.h"
#include "fourlib.h"
/*======================== defines ===========================*/
#define MAXCANDIDATES 20
#define MAXREF 20
#define MAXIDX 20
#define MAXSOLUTION 20
#define ABS(x) (x < 0 ? -(x) : (x))
/*======================== types =============================*/
typedef struct {
int h,k,l;
double diff;
} HKL, *pHKL;
typedef struct {
double uvw[3];
MATRIX UVW;
HKL indices[MAXCANDIDATES];
int nHKL;
int currentIDX;
double twotheta;
int originalID;
} IndexVector, *pIndexVector;
/*================== module parameters ========================*/
static lattice direct;
static double lambda;
static T_SgInfo *spgrp = NULL;
static IndexVector reflections[MAXREF];
static int nReflections = 0;
static double sttlim = .5, anglim = 2.;
static OutFunc outFunc = NULL;
static void *userData;
static int outLevel = 10;
static IndexSolution solutions[MAXSOLUTION];
static int nSolutions;
/*------------------------------------------------------------*/
void SimIdxInit(){
int i;
for(i = 0; i < MAXREF; i++){
reflections[i].UVW = mat_creat(3,1,ZERO_MATRIX);
}
}
/*=============== configuration functions =====================*/
void SimIdxSetCell(double cell[6]){
direct.a = cell[0];
direct.b = cell[1];
direct.c = cell[2];
direct.alpha = cell[3];
direct.beta = cell[4];
direct.gamma = cell[5];
}
/*-------------------------------------------------------------*/
void SimIdxSetLambda(double lmda){
lambda = lmda;
}
/*-------------------------------------------------------------*/
void SimIdxSetSttLim(double lmda){
sttlim = lmda;
}
/*-------------------------------------------------------------*/
void SimIdxSetAngLim(double lmda){
anglim = lmda;
}
/*-------------------------------------------------------------*/
void SimIdxSetSpacegroup(T_SgInfo *sg){
spgrp = sg;
}
/*-------------------------------------------------------------*/
void SimIdxClearReflection(){
nReflections = 0;
}
/*-------------------------------------------------------------*/
void SimIdxAddReflection(double uvw[3]){
int i;
if(nReflections < MAXREF){
memcpy(&reflections[nReflections].uvw, uvw, 3*sizeof(double));
reflections[nReflections].nHKL = 0;
reflections[nReflections].currentIDX = 0;
reflections[nReflections].originalID = nReflections;
for(i = 0; i < 3; i++){
reflections[nReflections].UVW[i][0] = uvw[i];
}
nReflections++;
}
}
/*-------------------------------------------------------------*/
void SimIdxOutput(void *data, OutFunc out, int level){
userData = data;
outFunc = out;
outLevel = level;
}
/*-------------------------------------------------------------*/
static void SimIdxPrint(int level, char *fmt, ...){
va_list ap;
char buf[1024];
int l;
if(level > outLevel){
return;
}
va_start(ap, fmt);
l = vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
if(outFunc != NULL){
outFunc(userData,buf);
} else {
printf("%s\n",buf);
}
}
/*=============== The alkoholism ===============================*/
static int thetaCompare(const void *d1, const void *d2){
pIndexVector iv1, iv2;
iv1 = (pIndexVector)d1;
iv2 = (pIndexVector)d2;
if(iv1->twotheta == iv2->twotheta) {
return 0;
}
if(iv1->twotheta < iv2->twotheta){
return -1;
} else {
return 1;
}
}
/*--------------------------------------------------------------*/
static void calcRefTheta(){
int i;
double theta, d;
for(i = 0; i < nReflections; i++){
calcTheta(lambda, reflections[i].UVW, &d, &theta);
reflections[i].twotheta = 2.* theta;
}
qsort(reflections,nReflections, sizeof(IndexVector),
thetaCompare);
SimIdxPrint(10,"%d Reflections", nReflections);
}
/*---------------------------------------------------------------*/
double calcIdxTwoTheta(int h, int k, int l, MATRIX B){
MATRIX H, Z1;
double om, d;
H = mat_creat(3,1,ZERO_MATRIX);
if(H == NULL){
SimIdxPrint(1,"ERROR: out of memory calculating H matrix");
return 0.;
}
H[0][0] = (double)h;
H[1][0] = (double)k;
H[2][0] = (double)l;
Z1 = mat_mul(B,H);
calcTheta(lambda,Z1,&d,&om);
om *= 2.;
mat_free(Z1);
mat_free(H);
return om;
}
/*---------------------------------------------------------------*/
static void AddCandidate(int n, int h, int k, int l,
double diff){
int cur = reflections[n].nHKL;
if(cur < MAXCANDIDATES){
reflections[n].indices[cur].h = h;
reflections[n].indices[cur].k = k;
reflections[n].indices[cur].l = l;
reflections[n].indices[cur].diff = diff ;
reflections[n].nHKL++;
}
}
/*---------------------------------------------------------------*/
static int calcIndexes(){
int h, k, l, i, status;
int minh, mink, minl;
MATRIX B;
double twotheta;
B = mat_creat(3,3,UNIT_MATRIX);
if(B == NULL){
SimIdxPrint(1,"ERROR: out of memory calculating B matrix");
return 0;
}
status = calculateBMatrix(direct,B);
if(status < 0) {
SimIdxPrint(1,"ERROR: invalid cell constants, failed to calculate B matrix");
return 0;
}
minh = -MAXIDX;
mink = -MAXIDX;
minl = -MAXIDX;
SetListMin_hkl(spgrp,MAXIDX, MAXIDX,&minh, &mink, &minl);
for(h = MAXIDX; h > -MAXIDX; h--){
for(k = MAXIDX; k > -MAXIDX; k--){
for(l = MAXIDX; l > -MAXIDX; l--){
if(IsSysAbsent_hkl(spgrp,h,k,l,NULL) != 0) {
continue;
}
twotheta = calcIdxTwoTheta(h,k,l,B);
for(i = 0; i < nReflections; i++){
if(reflections[i].twotheta > twotheta - sttlim &&
reflections[i].twotheta < twotheta + sttlim){
AddCandidate(i, h,k,l, ABS(twotheta - reflections[i].twotheta));
}
}
}
}
}
mat_free(B);
return 1;
}
/*-------------------------------------------------------------*/
static double angleBetweenScatVec(MATRIX v1, MATRIX v2){
double angle;
angle = angleBetween(v1,v2);
return angle;
}
/*---------------------------------------------------------------*/
static void printRefDiagnostic(){
int i, j;
double angle;
SimIdxPrint(10,"Reflection List and Candidate Indices");
SimIdxPrint(10," N STT U V W");
for(i = 0; i < nReflections; i++){
SimIdxPrint(10,"%3.3d %8.4f %8.4f %8.4f %8.4f", i,
reflections[i].twotheta,
reflections[i].uvw[0],
reflections[i].uvw[1], reflections[i].uvw[2]);
for(j = 0; j < reflections[i].nHKL; j++){
SimIdxPrint(10,"\t%4d %4d %4d",
reflections[i].indices[j].h,
reflections[i].indices[j].k,
reflections[i].indices[j].l);
}
}
SimIdxPrint(10,"Angles between reflections");
SimIdxPrint(10,"IDX1 IDX2 Angle");
for(i = 0; i < nReflections; i++){
for(j = i; j < nReflections; j++){
if(i != j){
angle = angleBetweenScatVec(reflections[i].UVW, reflections[j].UVW);
SimIdxPrint(10,"%3d %3d %8.2f",i,j,angle);
}
}
}
}
/*-------------------------------------------------------------*/
static double calculateVolume(double v1[3], double v2[3],
double v3[3]){
MATRIX m;
int i;
double vol;
m = mat_creat(3,3,ZERO_MATRIX);
for(i = 0; i < 3; i++){
m[i][0] = v1[i];
m[i][1] = v2[i];
m[i][2] = v3[i];
}
vol = mat_det(m);
mat_free(m);
return vol;
}
/*-------------------------------------------------------------*/
static int areCoplanar(MATRIX v1, MATRIX v2,
MATRIX v3){
MATRIX norm;
double dot;
norm = vectorCrossProduct(v1,v2);
if(norm != NULL){
dot = vectorDotProduct(norm,v3);
mat_free(norm);
} else {
dot = .0;
}
if(ABS(dot) > .00001){
return 0;
} else {
return 1;
}
}
/*--------------------------------------------------------------
* - We want the shortest vectors
* - We do not want vectors at 180 to each other
* - We do not want the three vectors to be coplanar
*-------------------------------------------------------------*/
static int chooseTriplet(int triplet[3]){
double angle, vol;
int idx = 1;
triplet[0] = 0;
/*
* test for 180
*/
while(idx < nReflections){
angle = angleBetweenScatVec(reflections[0].UVW,
reflections[idx].UVW);
if(angle < 160 && angle > -160){
triplet[1] = idx;
break;
}
idx++;
}
if(idx >= nReflections){
SimIdxPrint(1,"ERROR: no second index found");
return 0;
}
for(idx = 1; idx < nReflections; idx++){
if(idx != triplet[1]) {
if(!areCoplanar(reflections[triplet[0]].UVW,
reflections[triplet[1]].UVW,
reflections[idx].UVW)){
triplet[2] = idx;
return 1;
}
}
}
SimIdxPrint(1,"ERROR: no three non coplanar reflections found");
return 0;
}
/*------------------------------------------------------------*/
static double reflectionsAngle(MATRIX B, int hkl1[3],
int hkl2[3]){
double angle;
reflection r1, r2;
r1.h = hkl1[0];
r1.k = hkl1[1];
r1.l = hkl1[2];
r2.h = hkl2[0];
r2.k = hkl2[1];
r2.l = hkl2[2];
return angleBetweenReflections(B,r1,r2);
}
/*-------------------------------------------------------------*/
static int findAngleMatch(MATRIX B, int idxr1, int r1,
int r2start, int r2, double *diff){
double scatAngle, hklAngle;
MATRIX H1, H2;
int i, r, hkl1[3], hkl2[3];
scatAngle = angleBetweenScatVec(reflections[r1].UVW,
reflections[r2].UVW);
hkl1[0] = reflections[r1].indices[idxr1].h;
hkl1[1] = reflections[r1].indices[idxr1].k;
hkl1[2] = reflections[r1].indices[idxr1].l;
for(i = r2start; i < reflections[r2].nHKL; i++){
hkl2[0] = reflections[r2].indices[i].h;
hkl2[1] = reflections[r2].indices[i].k;
hkl2[2] = reflections[r2].indices[i].l;
hklAngle = reflectionsAngle(B,hkl1, hkl2);
*diff = ABS(scatAngle - hklAngle);
if(*diff < anglim){
return i;
}
}
return -1;
}
/*-------------------------------------------------------------
* If the system is right handed the determinat of the
* matrix having the indices as columns must be positive
-------------------------------------------------------------*/
static int testRightHandedness(int r1, int r1idx,
int r2, int r2idx,
int r3, int r3idx){
MATRIX T;
double vol;
T = mat_creat(3,3,ZERO_MATRIX);
if(T == NULL){
return 0;
}
T[0][0] = reflections[r1].indices[r1idx].h;
T[1][0] = reflections[r1].indices[r1idx].k;
T[2][0] = reflections[r1].indices[r1idx].l;
T[0][1] = reflections[r2].indices[r2idx].h;
T[1][1] = reflections[r2].indices[r2idx].k;
T[2][1] = reflections[r2].indices[r2idx].l;
T[0][2] = reflections[r3].indices[r3idx].h;
T[1][2] = reflections[r3].indices[r3idx].k;
T[2][2] = reflections[r3].indices[r3idx].l;
vol = mat_det(T);
mat_free(T);
if(vol > .0){
return 1;
} else {
return 0;
}
}
/*-------------------------------------------------------------*/
static void storeSolution(int r1, int r1idx,
int r2, int r2idx,
int r3, int r3idx, double diff){
IndexSolution is;
is.h[0] = reflections[r1].indices[r1idx].h;
is.k[0] = reflections[r1].indices[r1idx].k;
is.l[0] = reflections[r1].indices[r1idx].l;
is.originalID[0] = reflections[r1].originalID;
is.diff = reflections[r1].indices[r1idx].diff;
is.h[1] = reflections[r2].indices[r2idx].h;
is.k[1] = reflections[r2].indices[r2idx].k;
is.l[1] = reflections[r2].indices[r2idx].l;
is.originalID[1] = reflections[r2].originalID;
is.diff += reflections[r2].indices[r2idx].diff;
if(r3 != 999){
is.h[2] = reflections[r3].indices[r3idx].h;
is.k[2] = reflections[r3].indices[r3idx].k;
is.l[2] = reflections[r3].indices[r3idx].l;
is.diff += reflections[r3].indices[r3idx].diff;
is.originalID[2] = reflections[r3].originalID;
} else {
is.h[2] = 0;
is.k[2] = 0;
is.l[2] = 0;
is.originalID[2] = 999;
}
is.diff += diff;
solutions[nSolutions] = is;
nSolutions++;
}
/*----------------------------------------------------------------------*/
static int compareSolution(const void *d1, const void *d2){
IndexSolution *iv1, *iv2;
iv1 = (IndexSolution *)d1;
iv2 = (IndexSolution *)d2;
if(iv1->diff == iv2->diff) {
return 0;
}
if(iv1->diff < iv2->diff){
return -1;
} else {
return 1;
}
}
/*--------------------------------------------------------------*/
static int findSolutionsForTriplet(int triplet[3], int testRight){
int r1, r2, r3, i, status;
int match1, match2, r2start, r3start;
double diff1, diff2;
MATRIX B;
r1 = triplet[0];
r2 = triplet[1];
r3 = triplet[2];
B = mat_creat(3,3,UNIT_MATRIX);
if(B == NULL){
SimIdxPrint(1,"ERROR: out of memory calculating B matrix");
return 0;
}
status = calculateBMatrix(direct,B);
if(status < 0) {
SimIdxPrint(1,"ERROR: invalid cell constants, failed to calculate B matrix");
return 0;
}
for(i = 0; i < reflections[r1].nHKL; i++){
r2start = 0;
while((match1 = findAngleMatch(B, i, r1, r2start, r2,&diff1)) >=0){
r3start = 0;
while((match2 = findAngleMatch(B, i, r1, r3start, r3,&diff2)) >= 0){
if(testRight == 1){
if(testRightHandedness(r1, i, r2, match1, r3, match2)){
storeSolution(r1,i,r2,match1, r3,match2, diff1 + diff2);
}
} else {
storeSolution(r1,i,r2,match1, r3,match2, diff1 + diff2);
}
r3start = match2 + 1;
}
r2start = match1 + 1;
}
}
qsort(solutions,nSolutions, sizeof(IndexSolution),
compareSolution);
return 1;
}
/*-------------------------------------------------------------
* If the system is right handed the determinat of the
* matrix having the indices as columns must be positive.
* As I have only two vectors, I simulate the third by
* using the nromal on the other two.
-------------------------------------------------------------*/
static int testDuoRightHandedness(int r1, int r1idx,
int r2, int r2idx){
MATRIX T;
double vol;
int r3, r3idx;
T = mat_creat(3,3,ZERO_MATRIX);
if(T == NULL){
return 0;
}
T[0][0] = reflections[r1].indices[r1idx].h;
T[1][0] = reflections[r1].indices[r1idx].k;
T[2][0] = reflections[r1].indices[r1idx].l;
T[0][1] = reflections[r2].indices[r2idx].h;
T[1][1] = reflections[r2].indices[r2idx].k;
T[2][1] = reflections[r2].indices[r2idx].l;
T[0][2] = reflections[r3].indices[r3idx].h;
T[1][2] = reflections[r3].indices[r3idx].k;
T[2][2] = reflections[r3].indices[r3idx].l;
vol = mat_det(T);
mat_free(T);
if(vol > .0){
return 1;
} else {
return 0;
}
}
/*--------------------------------------------------------------*/
static int findSolutionsForDuett(int triplet[3]){
MATRIX B;
int r1, r2, r2start, i, status, match1;
double diff;
if(triplet[1] == 999){
SimIdxPrint(1,"ERROR: No suitable reflection set found");
return 0;
}
r1 = triplet[0];
r2 = triplet[1];
B = mat_creat(3,3,UNIT_MATRIX);
if(B == NULL){
SimIdxPrint(1,"ERROR: out of memory calculating B matrix");
return 0;
}
status = calculateBMatrix(direct,B);
if(status < 0) {
SimIdxPrint(1,"ERROR: invalid cell constants, failed to calculate B matrix");
return 0;
}
for(i = 0; i < reflections[r1].nHKL; i++){
r2start = 0;
while((match1 = findAngleMatch(B, i, r1, r2start, r2,&diff)) >=0){
storeSolution(r1,i,r2,match1,999,0,diff);
r2start = match1 + 1;
}
}
qsort(solutions,nSolutions, sizeof(IndexSolution),
compareSolution);
return 1;
}
/*--------------------------------------------------------------
* This is used if we cannot find a solution for a triplet.
* Then we try to reduce to a duett. So we look for a second
* reflection in the original triplett which is closest
* to 90 degree in angle.
*/
static int secondForDuett(int triplet[3]){
double diff1, diff2;
diff1 = ABS(90. - angleBetweenScatVec(
reflections[triplet[0]].UVW,reflections[triplet[1]].UVW));
diff2 = ABS(90. - angleBetweenScatVec(
reflections[triplet[0]].UVW,reflections[triplet[2]].UVW));
if(diff1 < diff2){
return triplet[1];
} else {
return triplet[2];
}
}
/*--------------------------------------------------------------*/
int SimIdxRun(){
int triplet[3] = {999,999,999}, status;
SimIdxPrint(10,"SimIdx calculating with parameters:");
SimIdxPrint(10, "Cell = %f %f %f %f %f %f", direct.a, direct.b, direct.c,
direct.alpha, direct.beta, direct.gamma);
SimIdxPrint(10,"Lambda = %f", lambda);
SimIdxPrint(10,"Sttlim, anglim = %f %f", sttlim, anglim);
nSolutions = 0;
calcRefTheta();
if(!calcIndexes()){
return 0;
}
if(outLevel >= 10){
printRefDiagnostic();
}
if(nReflections >= 3){
if(!chooseTriplet(triplet)){
return findSolutionsForDuett(triplet);
}
} else {
triplet[0] = 0;
triplet[1] = 1;
return findSolutionsForDuett(triplet);
}
SimIdxPrint(10,"Choosen triplet: %d, %d, %d\n", triplet[0],
triplet[1], triplet[2]);
status = findSolutionsForTriplet(triplet,1);
if(nSolutions == 0){
SimIdxPrint(1,"WARNING: found no right handed solution set, trying to find lefthanded");
status = findSolutionsForTriplet(triplet,0);
}
if(nSolutions == 0){
SimIdxPrint(10,"Failed to find solution for triplet, trying duett");
status = secondForDuett(triplet);
triplet[1] = status;
status = findSolutionsForDuett(triplet);
}
return status;
}
/*=========================== solution retrieval ===================*/
int SimIdxGetNSolutions(){
return nSolutions;
}
/*------------------------------------------------------------------*/
IndexSolution SimIdxGetSolution(int id){
IndexSolution broken;
if(id >= 0 && id < nSolutions){
return solutions[id];
}
broken.h[0] = -999;
broken.h[1] = -999;
broken.h[2] = -999;
return broken;
}