Files
sics/tasublib.c
Mark Koennecke f82197623e Merge branch 'master' into maverick
- Removed epicsmotor from motor.c
- Fixed a bad define in tasscanub.h
Conflicts:
	makefile_macosx
2015-03-17 14:01:40 +01:00

1142 lines
24 KiB
C

/**
* This is a library of functions and data structures for performing
* triple axis spectrometer angle calculations using the UB-matrix
* formalism as described by Mark Lumsden, to appear in Acta Cryst.
*
* copyright: see file COPYRIGHT
*
* Mark Koennecke, April 2005
*/
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include "trigd.h"
#include "vector.h"
#include "tasublib.h"
#define ABS(x) (x < 0 ? -(x) : (x))
#define PI 3.141592653589793
#define ECONST 2.072
#define DEGREE_RAD (PI/180.0) /* Radians per degree */
#define VERT 0
#define HOR 1
#define INPLANEPREC 0.01
/*============== monochromator/analyzer stuff =========================*/
double energyToK(double energy)
{
double K;
K = sqrt(energy / ECONST);
return K;
}
/*---------------------------------------------------------------------*/
double KtoEnergy(double k)
{
double energy;
energy = ECONST * k * k;
return energy;
}
/*-------------------------------------------------------------------*/
static double calcCurvature(double B1, double B2, double theta, int ori)
{
assert(ori == VERT || ori == HOR);
if (ori == VERT) {
return B1 + B2 / Sind(ABS(theta));
} else {
return B1 + B2 * Sind(ABS(theta));
}
}
/*--------------------------------------------------------------------*/
int maCalcTwoTheta(maCrystal data, double k, double *two_theta)
{
double fd, theta;
/* fd = k/(2.*data.dd); */
fd = PI / (data.dd * k);
if (fd > 1.0) {
return ENERGYTOBIG;
}
theta = Asind(fd) * data.ss;
*two_theta = 2. * theta;
return 1;
}
/*--------------------------------------------------------------------*/
double maCalcVerticalCurvature(maCrystal data, double two_theta)
{
return calcCurvature(data.VB1, data.VB2, two_theta / 2., VERT);
}
/*-------------------------------------------------------------------*/
double maCalcHorizontalCurvature(maCrystal data, double two_theta)
{
return calcCurvature(data.HB1, data.HB2, two_theta / 2., HOR);
}
/*--------------------------------------------------------------------*/
double maCalcK(maCrystal data, double two_theta)
{
double k;
k = ABS(data.dd * Sind(two_theta / 2));
if (ABS(k) > .001) {
k = PI / k;
} else {
k = .0;
}
return k;
}
/*==================== reciprocal space ==============================*/
static MATRIX tasReflectionToHC(tasQEPosition r, MATRIX B)
{
MATRIX h = NULL, hc = NULL;
h = makeVector();
if (h == NULL) {
return NULL;
}
vectorSet(h, 0, r.qh);
vectorSet(h, 1, r.qk);
vectorSet(h, 2, r.ql);
hc = mat_mul(B, h);
killVector(h);
return hc;
}
/*------------------------------------------------------------------
a quadrant dependent tangens
------------------------------------------------------------------*/
static double rtan(double y, double x)
{
double val;
if ((x == 0.) && (y == 0.)) {
return .0;
}
if (x == 0.) {
if (y < 0.) {
return -PI / 2.;
} else {
return PI / 2.;
}
}
if (ABS(y) < ABS(x)) {
val = atan(ABS(y / x));
if (x < 0.) {
val = PI - val;
}
if (y < 0.) {
val = -val;
}
return val;
} else {
val = PI / 2. - atan(ABS(x / y));
if (x < 0.) {
val = PI - val;
}
if (y < 0.) {
val = -val;
}
}
return val;
}
/*---------------------------------------------------------------*/
static double calcTheta(double ki, double kf, double two_theta)
{
/**
* |ki| - |kf|cos(two_theta)
* tan(theta) = --------------------------
* |kf|sin(two_theta)
*/
return rtan(ABS(ki) - ABS(kf) * Cosd(two_theta),
ABS(kf) * Sind(two_theta)) / DEGREE_RAD;
}
/*-------------------------------------------------------------------------*/
double tasAngleBetweenReflections(MATRIX B, tasReflection r1,
tasReflection r2)
{
MATRIX chi1, chi2, h1, h2;
double angle = .0;
h1 = makeVector();
if (h1 == NULL) {
return -9999.99;
}
h1[0][0] = r1.qe.qh;
h1[1][0] = r1.qe.qk;
h1[2][0] = r1.qe.ql;
h2 = makeVector();
if (h2 == NULL) {
return -999.99;
}
h2[0][0] = r2.qe.qh;
h2[1][0] = r2.qe.qk;
h2[2][0] = r2.qe.ql;
chi1 = mat_mul(B, h1);
chi2 = mat_mul(B, h2);
if (chi1 != NULL && chi2 != NULL) {
angle = tasAngleBetween(chi1, chi2);
killVector(chi1);
killVector(chi2);
}
killVector(h1);
killVector(h2);
return angle;
}
/*--------------------------------------------------------------------*/
static MATRIX uFromAngles(double om, double sgu, double sgl)
{
MATRIX u;
u = makeVector();
if (u == NULL) {
return NULL;
}
vectorSet(u, 0, Cosd(om) * Cosd(sgl));
vectorSet(u, 1, -Sind(om) * Cosd(sgu) + Cosd(om)* Sind(sgl)*Sind(sgu));
vectorSet(u, 2,
Sind(om) * Sind(sgu) + Cosd(om) * Sind(sgl) * Cosd(sgu));
return u;
}
/*---------------------------------------------------------------*/
MATRIX calcTasUVectorFromAngles(tasReflection rr)
{
double theta, om, ss;
MATRIX m;
tasReflection r;
r = rr;
if(r.angles.sample_two_theta < .0){
ss = -1.;
} else {
ss = 1.;
}
r.angles.sample_two_theta = ABS(r.angles.sample_two_theta);
theta = calcTheta(r.qe.ki, r.qe.kf, r.angles.sample_two_theta);
om = r.angles.a3 - ss*theta;
/*
This here may have to do with scattering sense.....
if (om < -180.){
om += 180;
}
*/
m = uFromAngles(om, r.angles.sgu, ss*r.angles.sgl);
/*
printf("Q = %f, %f, %f inwards Uv = %f %f %f, om = %f\n", r.qe.qh, r.qe.qk, r.qe.ql,
m[0][0], m[1][0],m[2][0], om);
*/
return m ;
}
/*-----------------------------------------------------------------------------*/
static MATRIX tasReflectionToQC(tasQEPosition r, MATRIX UB)
{
MATRIX Q, QC;
Q = makeVector();
if (Q == NULL) {
return NULL;
}
vectorSet(Q, 0, r.qh);
vectorSet(Q, 1, r.qk);
vectorSet(Q, 2, r.ql);
QC = mat_mul(UB, Q);
killVector(Q);
return QC;
}
/*-----------------------------------------------------------------*/
int makeAuxReflection(MATRIX B, tasReflection r1, tasReflection * r2,
int ss)
{
double theta, om, q, cos2t;
MATRIX QC;
r2->angles = r1.angles;
r2->qe.ki = r1.qe.ki;
r2->qe.kf = r1.qe.kf;
theta = calcTheta(r1.qe.ki, r1.qe.kf,
ss*r1.angles.sample_two_theta);
om = r1.angles.a3 - ss*theta;
om += tasAngleBetweenReflections(B, r1, *r2);
QC = tasReflectionToHC(r2->qe, B);
if (QC == NULL) {
return UBNOMEMORY;
}
q = vectorLength(QC);
q = 2. * PI * vectorLength(QC);
cos2t = (r1.qe.ki * r1.qe.ki + r1.qe.kf * r1.qe.kf - q * q) /
(2. * ABS(r1.qe.ki) * ABS(r1.qe.kf));
if (ABS(cos2t) > 1.) {
killVector(QC);
return TRIANGLENOTCLOSED;
}
r2->angles.sample_two_theta = ss * Acosd(cos2t);
theta = calcTheta(r1.qe.ki, r1.qe.kf, ss*r2->angles.sample_two_theta);
r2->angles.a3 = om + ss*theta;
r2->angles.a3 = fmod(r2->angles.a3 + ss*180.,360.) - ss*180.;
/*
r2->angles.a3 -= 180.;
if (r2->angles.a3 < -180.) {
r2->angles.a3 += 360.;
}
*/
mat_free(QC);
return 1;
}
/*------------------------------------------------------------------*/
int calcTwoTheta(MATRIX B, tasQEPosition ref, int ss, double *value)
{
MATRIX QC;
double cos2t, q;
QC = tasReflectionToHC(ref, B);
if (QC == NULL) {
return UBNOMEMORY;
}
q = vectorLength(QC);
q = 2. * PI * vectorLength(QC);
killVector(QC);
cos2t = (ref.ki * ref.ki + ref.kf * ref.kf - q * q) /
(2. * ABS(ref.ki) * ABS(ref.kf));
if (ABS(cos2t) > 1.) {
return TRIANGLENOTCLOSED;
}
*value = ss * Acosd(cos2t);
return 1;
}
/*-------------------------------------------------------------------*/
MATRIX calcPlaneNormal(tasReflection r1, tasReflection r2)
{
MATRIX u1 = NULL, u2 = NULL, planeNormal = NULL;
int i;
u1 = calcTasUVectorFromAngles(r1);
u2 = calcTasUVectorFromAngles(r2);
if (u1 != NULL && u2 != NULL) {
planeNormal = vectorCrossProduct(u1, u2);
/*
The plane normal has to point to the stars and not to the earth
core in order for the algorithm to work.
*/
/*
if (planeNormal[2][0] < .0) {
for (i = 0; i < 3; i++) {
planeNormal[i][0] = -1. * planeNormal[i][0];
}
}
*/
mat_free(u1);
mat_free(u2);
normalizeVector(planeNormal);
return planeNormal;
} else {
return NULL;
}
}
/*-------------------------------------------------------------------*/
MATRIX calcPlaneNormalQOld(MATRIX UB, tasReflection r1, tasReflection r2)
{
MATRIX u1 = NULL, u2 = NULL, planeNormal = NULL;
int i;
u1 = tasReflectionToQC(r1.qe, UB);
u2 = tasReflectionToQC(r2.qe,UB);
if (u1 != NULL && u2 != NULL) {
planeNormal = vectorCrossProduct(u1, u2);
/*
The plane normal has to point to the stars and not to the earth
core in order for the algorithm to work.
*/
/*
if (planeNormal[2][0] < .0) {
for (i = 0; i < 3; i++) {
planeNormal[i][0] = -1. * planeNormal[i][0];
}
}
*/
mat_free(u1);
mat_free(u2);
normalizeVector(planeNormal);
return planeNormal;
} else {
return NULL;
}
}
/*-------------------------------------------------------------------*/
MATRIX calcPlaneNormalQ(MATRIX UB, tasReflection r1, tasReflection r2)
{
MATRIX q1 = NULL, q2 = NULL, q3 = NULL, planeNormal = NULL;
int i;
q1 = makeVector();
q2 = makeVector();
q1[0][0] = r1.qe.qh;
q1[1][0] = r1.qe.qk;
q1[2][0] = r1.qe.ql;
q2[0][0] = r2.qe.qh;
q2[1][0] = r2.qe.qk;
q2[2][0] = r2.qe.ql;
q3 = vectorCrossProduct(q1,q2);
planeNormal = mat_mul(UB,q3);
normalizeVector(planeNormal);
if (planeNormal[2][0] < .0) {
for (i = 0; i < 3; i++) {
planeNormal[i][0] = -1. * planeNormal[i][0];
}
}
mat_free(q1);
mat_free(q2);
mat_free(q3);
planeNormal[0][0] *= -1.;
planeNormal[1][0] *= -1.;
return planeNormal;
}
/*--------------------------------------------------------------------
This is shot. The resulting UB is invalid. This needs more throught.
*/
MATRIX calcUBFromAngles(MATRIX B, double om, double sgu, double sgl)
{
MATRIX M, N, OM, UB;
int status;
M = mat_creat(3, 3, ZERO_MATRIX);
N = mat_creat(3, 3, ZERO_MATRIX);
OM = mat_creat(3, 3, ZERO_MATRIX);
if(M == NULL || N == NULL || OM == NULL){
mat_free(B);
return NULL;
}
N[0][0] = 1.;
N[1][1] = Cosd(sgu);
N[1][2] = -Sind(sgu);
N[2][1] = Sind(sgu);
N[2][2] = Cosd(sgu);
M[0][0] = Cosd(sgl);
M[0][2] = Sind(sgl);
M[1][1] = 1.;
M[2][0] = -Sind(sgl);
M[2][2] = Cosd(sgl);
OM[0][0] = Cosd(om);
OM[0][1] = -Sind(om);
OM[1][0] = Sind(om);
OM[1][1] = Cosd(om);
OM[2][2] = 1.;
/*
Multiply....
*/
UB = mat_mul(OM,M);
UB = mat_mul(UB,N);
UB = mat_mul(UB,B);
mat_free(OM);
mat_free(N);
mat_free(M);
return UB;
}
/*--------------------------------------------------------------------*/
MATRIX calcTestUB(lattice cell, double om, double sgu, double sgl)
{
MATRIX B, UB;
int status;
/*
* create matrices
*/
B = mat_creat(3, 3, ZERO_MATRIX);
status = calculateBMatrix(cell, B);
if (status < 0) {
return NULL;
}
UB = calcUBFromAngles(B,om,sgu,sgl);
mat_free(B);
return UB;
}
/*--------------------------------------------------------------------*/
MATRIX calcTestNormal(double sgu, double sgl)
{
MATRIX M, N, Z, NORM;
int status;
M = mat_creat(3, 3, ZERO_MATRIX);
N = mat_creat(3, 3, ZERO_MATRIX);
Z = mat_creat(3, 1, ZERO_MATRIX);
if(M == NULL || N == NULL || Z == NULL){
return NULL;
}
N[0][0] = 1.;
N[1][1] = Cosd(sgu);
N[1][2] = -Sind(sgu);
N[2][1] = Sind(sgu);
N[2][2] = Cosd(sgu);
M[0][0] = Cosd(sgl);
M[0][2] = Sind(sgl);
M[1][1] = 1.;
M[2][0] = -Sind(sgl);
M[2][2] = Cosd(sgl);
Z[2][0] = 1.;
NORM = mat_inv(N);
NORM = mat_mul(NORM,mat_inv(M));
NORM = mat_mul(NORM,Z);
mat_free(N);
mat_free(M);
mat_free(Z);
return NORM;
}
/*--------------------------------------------------------------------*/
MATRIX calcTasUBFromTwoReflections(lattice cell, tasReflection r1,
tasReflection r2, int *errorCode)
{
MATRIX B, HT, UT, U, UB, HTT;
MATRIX u1, u2, h1, h2, planeNormal;
double ud[3];
int status;
*errorCode = 1;
/*
calculate the B matrix and the HT matrix
*/
B = mat_creat(3, 3, ZERO_MATRIX);
status = calculateBMatrix(cell, B);
if (status < 0) {
*errorCode = status;
return NULL;
}
/*
printf("B-matrix\n");
mat_dump(B);
printf("\n");
*/
h1 = tasReflectionToHC(r1.qe, B);
h2 = tasReflectionToHC(r2.qe, B);
if (h1 == NULL || h2 == NULL) {
*errorCode = UBNOMEMORY;
mat_free(B);
return NULL;
}
HT = matFromTwoVectors(h1, h2);
if (HT == NULL) {
*errorCode = UBNOMEMORY;
mat_free(B);
mat_free(h1);
mat_free(h2);
return NULL;
}
/*
calculate U vectors and UT matrix
*/
u1 = calcTasUVectorFromAngles(r1);
u2 = calcTasUVectorFromAngles(r2);
if (u1 == NULL || u2 == NULL) {
*errorCode = UBNOMEMORY;
mat_free(B);
mat_free(h1);
mat_free(h2);
return NULL;
}
UT = matFromTwoVectors(u1, u2);
if (UT == NULL) {
*errorCode = UBNOMEMORY;
mat_free(B);
mat_free(h1);
mat_free(h2);
mat_free(u1);
mat_free(u2);
mat_free(HT);
return NULL;
}
/*
UT = U * HT
*/
HTT = mat_tran(HT);
if (HTT == NULL) {
*errorCode = UBNOMEMORY;
mat_free(B);
mat_free(h1);
mat_free(h2);
mat_free(u1);
mat_free(u2);
mat_free(HT);
return NULL;
}
U = mat_mul(UT, HTT);
if (U == NULL) {
*errorCode = UBNOMEMORY;
mat_free(B);
mat_free(h1);
mat_free(h2);
mat_free(u1);
mat_free(u2);
mat_free(HT);
mat_free(HTT);
return NULL;
}
UB = mat_mul(U, B);
if (UB == NULL) {
mat_free(B);
mat_free(h1);
mat_free(h2);
mat_free(u1);
mat_free(u2);
mat_free(HT);
mat_free(HTT);
mat_free(U);
*errorCode = UBNOMEMORY;
}
/*
clean up
*/
killVector(h1);
killVector(h2);
mat_free(HT);
mat_free(HTT);
killVector(u1);
killVector(u2);
mat_free(UT);
mat_free(U);
mat_free(B);
return UB;
}
/*-----------------------------------------------------------------------------*/
static MATRIX buildTVMatrix(MATRIX U1V, MATRIX U2V)
{
MATRIX T, T3V;
int i;
normalizeVector(U2V);
T3V = vectorCrossProduct(U1V, U2V);
normalizeVector(T3V);
if (T3V == NULL) {
return NULL;
}
T = mat_creat(3, 3, ZERO_MATRIX);
if (T == NULL) {
killVector(T3V);
return NULL;
}
for (i = 0; i < 3; i++) {
T[i][0] = U1V[i][0];
T[i][1] = U2V[i][0];
T[i][2] = T3V[i][0];
}
/*
printf("\n");
printf("U1V\n");
mat_dump(U1V);
printf("U2V\n");
mat_dump(U2V);
printf("U3V\n");
mat_dump(T3V);
printf("TV-matrix\n");
mat_dump(T);
printf("\n");
*/
killVector(T3V);
return T;
}
/*----------------------------------------------------------------------------*/
static MATRIX buildRMatrix(MATRIX UB, MATRIX planeNormal,
tasQEPosition qe, int *errorCode)
{
MATRIX U1V, U2V, TV, TVINV, M;
*errorCode = 1;
U1V = tasReflectionToQC(qe, UB);
if (U1V == NULL) {
*errorCode = UBNOMEMORY;
return NULL;
}
normalizeVector(U1V);
/*
printf("Q = %f, %f, %f outwards Uv = %f %f %f\n", qe.qh, qe.qk, qe.ql,
U1V[0][0], U1V[1][0],U1V[2][0]);
*/
U2V = vectorCrossProduct(planeNormal, U1V);
if (U2V == NULL) {
killVector(U1V);
*errorCode = UBNOMEMORY;
return NULL;
}
if (vectorLength(U2V) < .0001) {
*errorCode = BADUBORQ;
killVector(U1V);
killVector(U2V);
return NULL;
}
TV = buildTVMatrix(U1V, U2V);
if (TV == NULL) {
killVector(U1V);
killVector(U2V);
*errorCode = UBNOMEMORY;
return NULL;
}
TVINV = mat_inv(TV);
if (TVINV == NULL) {
*errorCode = BADUBORQ;
}
/*
printf("TV after inversion\n");
mat_dump(TVINV);
printf("\n");
*/
killVector(U1V);
killVector(U2V);
mat_free(TV);
return TVINV;
}
/*-------------------------------------------------------------------------------*/
int calcTasMisalignment(ptasMachine machine, tasQEPosition qe, double *misalign)
{
MATRIX R;
int errorCode = 1;
double om;
R = buildRMatrix(machine->UB, machine->planeNormal, qe, &errorCode);
if (R == NULL) {
return errorCode;
}
/**
* See below, notes on om
*/
om = Atan2d(R[1][0], R[0][0]);
*misalign = om;
mat_free(R);
return 1;
}
/*-------------------------------------------------------------------------------*/
int calcTasQAngles(MATRIX UB, MATRIX planeNormal, int ss, double a3offset,
tasQEPosition qe, ptasAngles angles)
{
MATRIX R, QC;
double om, q, theta, cos2t, cossgl;
int errorCode = 1;
R = buildRMatrix(UB, planeNormal, qe, &errorCode);
if (R == NULL) {
return errorCode;
}
/* mat_dump(R); */
cossgl = sqrt(R[0][0]*R[0][0]+R[1][0]*R[1][0]);
angles->sgl = ss*Atan2d(-R[2][0],cossgl);
if (ABS(angles->sgl - 90.) < .5) {
mat_free(R);
return BADUBORQ;
}
/*
Now, this is slightly different then in the publication by M. Lumsden.
The reason is that the atan2 helps to determine the sign of om
whereas the sin, cos formula given by M. Lumsden yield ambiguous signs
especially for om.
sgu = atan(R[2][1],R[2][2]) where:
R[2][1] = cos(sgl)sin(sgu)
R[2][2] = cos(sgu)cos(sgl)
om = atan(R[1][0],R[0][0]) where:
R[1][0] = sin(om)cos(sgl)
R[0][0] = cos(om)cos(sgl)
The definitions of the R components are taken from M. Lumsden
R-matrix definition.
*/
om = Atan2d(R[1][0]/cossgl, R[0][0]/cossgl);
angles->sgu = Atan2d(R[2][1]/cossgl, R[2][2]/cossgl);
QC = tasReflectionToQC(qe, UB);
if (QC == NULL) {
mat_free(R);
return UBNOMEMORY;
}
/*
printf("Q = %f, %f, %f calculated om = %f\n", qe.qh, qe.qk, qe.ql, om);
*/
q = vectorLength(QC);
q = 2. * PI * vectorLength(QC);
cos2t =
(qe.ki * qe.ki + qe.kf * qe.kf -
q * q) / (2. * ABS(qe.ki) * ABS(qe.kf));
if (ABS(cos2t) > 1.) {
mat_free(R);
killVector(QC);
return TRIANGLENOTCLOSED;
}
theta = calcTheta(qe.ki, qe.kf, Acosd(cos2t));
angles->sample_two_theta = ss * Acosd(cos2t);
angles->a3 = om + ss*theta + a3offset;
/*
put a3 into -180, 180 properly. We can always turn by 180 because the
scattering geometry is symmetric in this respect. It is like looking at
the scattering plane from the other side
*/
/*
angles->a3 -= 180.;
if (angles->a3 < -180 - a3offset) {
angles->a3 += 360.;
}
*/
angles->a3 = fmod(angles->a3 + ss*180.,360.) - ss*180.;
killVector(QC);
mat_free(R);
return 1;
}
/*------------------------------------------------------------------------*/
int calcTasQH(MATRIX UB, tasAngles angles, ptasQEPosition qe)
{
MATRIX UBINV = NULL, QV = NULL, Q = NULL;
double q;
tasReflection r;
int i;
UBINV = mat_inv(UB);
r.angles = angles;
r.qe = *qe;
QV = calcTasUVectorFromAngles(r);
if (UBINV == NULL || QV == NULL) {
return UBNOMEMORY;
}
/*
normalize the QV vector to be the length of the Q vector
Thereby take into account the physicists magic fudge
2PI factor
*/
q = sqrt(qe->ki * qe->ki + qe->kf * qe->kf -
2. * qe->ki * qe->kf * Cosd(angles.sample_two_theta));
qe->qm = q;
q /= 2. * PI;
for (i = 0; i < 3; i++) {
QV[i][0] *= q;
}
Q = mat_mul(UBINV, QV);
if (Q == NULL) {
mat_free(UBINV);
killVector(QV);
return UBNOMEMORY;
}
qe->qh = Q[0][0];
qe->qk = Q[1][0];
qe->ql = Q[2][0];
killVector(QV);
killVector(Q);
mat_free(UBINV);
return 1;
}
/*---------------------------------------------------------------------*/
int calcAllTasAngles(ptasMachine machine, tasQEPosition qe,
ptasAngles angles)
{
int status;
tasReflection r;
status = maCalcTwoTheta(machine->monochromator, qe.ki,
&angles->monochromator_two_theta);
if (status != 1) {
return status;
}
status =
maCalcTwoTheta(machine->analyzer, qe.kf,
&angles->analyzer_two_theta);
if (status != 1) {
return status;
}
status = calcTasQAngles(machine->UB, machine->planeNormal,
machine->ss_sample,
machine->a3offset, qe, angles);
if (status != 1) {
return status;
}
return 1;
}
/*----------------------------------------------------------------------*/
int calcTasQEPosition(ptasMachine machine, tasAngles angles,
ptasQEPosition qe)
{
int status;
qe->ki = maCalcK(machine->monochromator, angles.monochromator_two_theta);
qe->kf = maCalcK(machine->analyzer, angles.analyzer_two_theta);
status = calcTasQH(machine->UB, angles, qe);
if (status != 1) {
return status;
}
return 1;
}
/*================== POWDER Implementation ===========================*/
int calcTasPowderAngles(ptasMachine machine, tasQEPosition qe,
ptasAngles angles)
{
double cos2t;
int status;
tasReflection r;
status = maCalcTwoTheta(machine->monochromator, qe.ki,
&angles->monochromator_two_theta);
if (status != 1) {
return status;
}
cos2t =
(qe.ki * qe.ki + qe.kf * qe.kf -
qe.qm * qe.qm) / (2. * ABS(qe.ki) * ABS(qe.kf));
if (cos2t > 1.) {
return TRIANGLENOTCLOSED;
}
angles->sample_two_theta = machine->ss_sample * Acosd(cos2t);
status =
maCalcTwoTheta(machine->analyzer, qe.kf,
&angles->analyzer_two_theta);
if (status != 1) {
return status;
}
return 1;
}
/*---------------------------------------------------------------------*/
int calcTasPowderPosition(ptasMachine machine, tasAngles angles,
ptasQEPosition qe)
{
int status;
qe->ki = maCalcK(machine->monochromator, angles.monochromator_two_theta);
qe->kf = maCalcK(machine->analyzer, angles.analyzer_two_theta);
qe->qm = sqrt(qe->ki * qe->ki + qe->kf * qe->kf -
2. * qe->ki * qe->kf * Cosd(angles.sample_two_theta));
return 1;
}
/*====================== Logic implementation =========================*/
void setTasPar(ptasQEPosition qe, int tasMode, int tasVar, double value)
{
double et;
assert(tasMode == KICONST || tasMode == KFCONST || tasMode == ELASTIC);
switch (tasVar) {
case KF:
if (tasMode == ELASTIC) {
qe->kf = qe->ki;
} else {
qe->kf = value;
}
break;
case EF:
if (tasMode == ELASTIC) {
qe->kf = qe->ki;
} else {
qe->kf = energyToK(value);
}
break;
case KI:
qe->ki = value;
if (tasMode == ELASTIC) {
qe->kf = value;
}
break;
case EI:
qe->ki = energyToK(value);
if (tasMode == ELASTIC) {
qe->kf = qe->ki;
}
break;
case QH:
qe->qh = value;
break;
case QK:
qe->qk = value;
break;
case QL:
qe->ql = value;
break;
case EN:
if (tasMode == KICONST) {
et = KtoEnergy(qe->ki) - value;
qe->kf = energyToK(et);
} else if (tasMode == KFCONST) {
et = KtoEnergy(qe->kf) + value;
qe->ki = energyToK(et);
} else if (tasMode == ELASTIC) {
qe->kf = qe->ki;
} else {
assert(0);
}
break;
case QM:
qe->qm = value;
break;
default:
assert(0);
break;
}
}
/*-------------------------------------------------------------------------*/
double getTasPar(tasQEPosition qe, int tasVar)
{
switch (tasVar) {
case EI:
return KtoEnergy(qe.ki);
break;
case KI:
return qe.ki;
break;
case EF:
return KtoEnergy(qe.kf);
break;
case KF:
return qe.kf;
break;
case QH:
return qe.qh;
break;
case QK:
return qe.qk;
break;
case QL:
return qe.ql;
break;
case EN:
return KtoEnergy(qe.ki) - KtoEnergy(qe.kf);
break;
case QM:
return qe.qm;
break;
default:
assert(0);
}
}
/*-------------------------------------------------------------------------*/
int isInPlane(MATRIX planeNormal, tasQEPosition qe)
{
MATRIX v = NULL;
double val;
v = makeVector();
v[0][0] = qe.qh;
v[1][0] = qe.qk;
v[2][0] = qe.ql;
val = vectorDotProduct(planeNormal, v);
mat_free(v);
if (ABS(val) > INPLANEPREC) {
return 0;
} else {
return 1;
}
}
/*--------------------------------------------------------------------*/
MATRIX calcScatteringPlaneNormal(tasQEPosition qe1, tasQEPosition qe2)
{
MATRIX v1 = NULL, v2 = NULL, planeNormal;
v1 = makeVector();
v2 = makeVector();
if (v1 != NULL && v2 != NULL) {
v1[0][0] = qe1.qh;
v1[1][0] = qe1.qk;
v1[2][0] = qe1.ql;
v2[0][0] = qe2.qh;
v2[1][0] = qe2.qk;
v2[2][0] = qe2.ql;
planeNormal = vectorCrossProduct(v1, v2);
normalizeVector(planeNormal);
mat_free(v1);
mat_free(v2);
return planeNormal;
} else {
return NULL;
}
}