Files
sics/singlebi.c
Ferdi Franceschini 10d29d597c Cleaned up ANSTO code to merge with sinqdev.sics
This is our new RELEASE-4_0 branch which was taken from ansto/93d9a7c
Conflicts:
	.gitignore
	SICSmain.c
	asynnet.c
	confvirtualmot.c
	counter.c
	devexec.c
	drive.c
	event.h
	exebuf.c
	exeman.c
	histmem.c
	interface.h
	motor.c
	motorlist.c
	motorsec.c
	multicounter.c
	napi.c
	napi.h
	napi4.c
	network.c
	nwatch.c
	nxscript.c
	nxxml.c
	nxxml.h
	ofac.c
	reflist.c
	scan.c
	sicshipadaba.c
	sicsobj.c
	site_ansto/docs/Copyright.txt
	site_ansto/instrument/lyrebird/config/tasmad/sicscommon/nxsupport.tcl
	site_ansto/instrument/lyrebird/config/tasmad/taspub_sics/tasscript.tcl
	statusfile.c
	tasdrive.c
	tasub.c
	tasub.h
	tasublib.c
	tasublib.h
2015-04-23 20:49:26 +10:00

574 lines
14 KiB
C

/**
* This is an implementation of the polymorphic single crystal calculation
* system defined in singlediff.h for a bisecting four circle diffractometer with
* an eulerian cradle.
*
* copyright: see file COPYRIGHT
*
* Mark Koennecke, August 2008
*/
#include <stdlib.h>
#include <assert.h>
#include <sics.h>
#include "singlediff.h"
#include "fourlib.h"
#include "ubfour.h"
#include "motor.h"
#include "singlex.h"
#include "motorlist.h"
#include "lld.h"
/*
the tolerance in chi we give before we allow to fix omega with phi
*/
#define CHITOLERANCE 3.
#define ABS(x) (x < 0 ? -(x) : (x))
/*--------------------------------------------------------------------*/
static int chiVertical(double chi)
{
if (ABS(chi - .0) < CHITOLERANCE) {
return 1;
}
if (ABS(chi - 180.0) < CHITOLERANCE) {
return 1;
}
return 0;
}
/*-----------------------------------------------------------------------*/
static int checkTheta(pSingleDiff self, double *stt)
{
char pError[132];
int iTest;
float fHard;
pMotor pTheta;
pTheta = SXGetMotor(TwoTheta);
if (pTheta == NULL) {
return 0;
}
iTest = MotorCheckBoundary(pTheta, (float) *stt, &fHard, pError, 131);
if (!iTest) {
return -1;
}
return iTest;
}
/*-----------------------------------------------------------------------*/
static int checkBisecting(pSingleDiff self,
double *stt, double om, double chi, double phi)
{
int iTest;
float fHard, fLimit;
char pError[132];
pMotor pMot;
/* check two theta */
iTest = checkTheta(self, stt);
if (iTest != 1) {
return -1;
}
/* for omega check against the limits +- SCANBORDER in order to allow for
a omega scan
*/
pMot = SXGetMotor(Omega);
if (pMot == NULL) {
return 0;
}
MotorGetPar(pMot, "softlowerlim", &fLimit);
if ((float) om < fLimit) {
iTest = 0;
} else {
iTest = 1;
MotorGetPar(pMot, "softupperlim", &fLimit);
if ((float) om > fLimit) {
iTest = 0;
} else {
iTest = 1;
}
}
/* check chi and phi */
pMot = SXGetMotor(Chi);
if (pMot == NULL) {
return 0;
}
iTest += MotorCheckBoundary(pMot, (float) chi, &fHard, pError, 131);
pMot = SXGetMotor(Phi);
if (pMot == NULL) {
return 0;
}
iTest += MotorCheckBoundary(pMot, (float) phi, &fHard, pError, 131);
if (iTest == 3) { /* none of them burns */
return 1;
}
return 0;
}
/*-----------------------------------------------------------------------
tryOmegaTweak tries to calculate a psi angle in order to put an
offending omega back into range.
This routine also handles the special case when chi ~ 0 or chi ~ 180.
Then it is possible to fix a omega problem by turing in phi.
-----------------------------------------------------------------------*/
static int tryOmegaTweak(pSingleDiff self, MATRIX z1, double *stt,
double *om, double *chi, double *phi)
{
int status;
float fLower, fUpper, omTarget, omOffset, phiSign;
double dumstt, offom, offchi, offphi, lambda;
pMotor pOmega, pPhi;
pOmega = SXGetMotor(Omega);
pPhi = SXGetMotor(Phi);
lambda = self->lambda;
if (pOmega == NULL) {
return 0;
}
status = checkBisecting(self, stt, *om, *chi, *phi);
if (status < 0) {
return 0; /* stt is burning */
} else if (status == 1) {
return 1;
}
/*
Is omega really the problem?
*/
omTarget = -9999;
MotorGetPar(pOmega, "softlowerlim", &fLower);
MotorGetPar(pOmega, "softupperlim", &fUpper);
if (*om < fLower) {
omTarget = fLower;
}
if (*om > fUpper) {
omTarget = fUpper;
}
if (omTarget < -7000) {
return 0;
}
/*
calculate omega offset
*/
omOffset = *om - omTarget;
omOffset = -omOffset;
/*
check for the special case of chi == 0 or chi == 180
*/
if (chiVertical(*chi)) {
dumstt = *stt;
offom = omTarget;
offchi = *chi;
MotorGetPar(pPhi, "sign", &phiSign);
offphi = *phi - omOffset * phiSign;
if (checkBisecting(self, &dumstt, offom, offchi, offphi)) {
*om = offom;
*chi = offchi;
*phi = offphi;
return 1;
}
}
/*
calculate angles with omega offset
*/
status = z1ToAnglesWithOffset(lambda, z1, omOffset, &dumstt,
&offom, &offchi, &offphi);
if (!status) {
return 0;
}
if (checkBisecting(self, &dumstt, offom, offchi, offphi)) {
*om = offom;
*chi = offchi;
*phi = offphi;
return 1;
}
return 0;
}
/*----------------------------------------------------------------------*/
static int hklInRange(void *data, double fSet[4], int mask[4])
{
pSingleDiff self = (pSingleDiff) data;
float fHard, fLimit;
char pError[132];
int i, test;
double dTheta;
pMotor pOmega, pChi, pPhi;
pOmega = SXGetMotor(Omega);
pChi = SXGetMotor(Chi);
pPhi = SXGetMotor(Phi);
if (pOmega == NULL || pChi == NULL || pPhi == NULL) {
return 0;
}
/* check two theta */
dTheta = fSet[0];
mask[0] = checkTheta(self, &dTheta);
fSet[0] = dTheta;
mask[1] = MotorCheckBoundary(pOmega, fSet[1], &fHard, pError, 131);
mask[2] = MotorCheckBoundary(pChi, fSet[2], &fHard, pError, 131);
mask[3] = MotorCheckBoundary(pPhi, fSet[3], &fHard, pError, 131);
for (i = 0, test = 0; i < 4; i++) {
test += mask[i];
}
if (test != 4) {
return 0;
} else {
return 1;
}
}
/*----------------------------------------------------------------------*/
static int hklInRangeOrion(void *data, double fSet[4], int mask[4])
{
pSingleDiff self = (pSingleDiff) data;
float fHard, fLimit;
char pError[132];
int i, test;
double dTheta;
pMotor pOmega, pChi, pPhi;
pOmega = SXGetMotor(Omega);
pChi = SXGetMotor(Chi);
pPhi = SXGetMotor(Phi);
if (pOmega == NULL || pChi == NULL || pPhi == NULL) {
return 0;
}
/* check two theta */
dTheta = fSet[0];
mask[0] = checkTheta(self, &dTheta);
fSet[0] = dTheta;
/* Orion is not upside down... */
fSet[2] = -1.*(fSet[2]+180.) + 360. ;
fSet[3] = circlify(fSet[3]+180.);
mask[1] = MotorCheckBoundary(pOmega, fSet[1], &fHard, pError, 131);
mask[2] = MotorCheckBoundary(pChi, fSet[2], &fHard, pError, 131);
mask[3] = MotorCheckBoundary(pPhi, fSet[3], &fHard, pError, 131);
for (i = 0, test = 0; i < 4; i++) {
test += mask[i];
}
if (test != 4) {
return 0;
} else {
return 1;
}
}
/*-------------------------------------------------------------------*/
static int calculateBiSettings(pSingleDiff self,
double *hkl, double *settings)
{
MATRIX z1;
double stt, om, chi, phi, psi, ompsi, chipsi, phipsi, myPsi;
int i, test, mask[4], iRetry;
double lambda;
myPsi = hkl[3];
if (myPsi > 0.1) {
iRetry = 1;
} else {
iRetry = 699;
}
z1 = calculateScatteringVector(self, hkl);
/*
just the plain angle calculation
*/
if (!z1mToBisecting(self->lambda, z1, &stt, &om, &chi, &phi)) {
return 0;
}
settings[0] = stt;
settings[1] = om;
settings[2] = chi;
settings[3] = phi;
if (iRetry == 1) {
rotatePsi(om, chi, phi, myPsi, &ompsi, &chipsi, &phipsi);
settings[1] = ompsi;
settings[2] = circlify(chipsi);
settings[3] = circlify(phipsi);
return 1;
} else {
if (hklInRange(self, settings, mask) == 1) {
return 1;
} else {
if (tryOmegaTweak(self, z1, &stt, &om, &chi, &phi) == 1) {
settings[0] = stt;
settings[1] = om;
settings[2] = chi;
settings[3] = phi;
return 1;
} else {
return findAllowedBisecting(self->lambda, z1, settings, hklInRange,
self);
}
}
}
return 0;
}
/*-------------------------------------------------------------------*/
static int calculateBiSettingsOrion(pSingleDiff self,
double *hkl, double *settings)
{
MATRIX z1;
double stt, om, chi, phi, psi, ompsi, chipsi, phipsi, myPsi;
int i, test, mask[4], iRetry;
double lambda;
myPsi = hkl[3];
if (myPsi > 0.1) {
iRetry = 1;
} else {
iRetry = 699;
}
z1 = calculateScatteringVector(self, hkl);
/*
just the plain angle calculation
*/
if (!z1mToBisecting(self->lambda, z1, &stt, &om, &chi, &phi)) {
return 0;
}
settings[0] = stt;
settings[1] = om;
settings[2] = -1.*(180.+chi) + 360.;
settings[3] = phi+180.;
if (iRetry == 1) {
rotatePsi(om, chi, phi, myPsi, &ompsi, &chipsi, &phipsi);
settings[1] = ompsi;
settings[2] = circlify(chipsi);
settings[3] = circlify(phipsi);
return 1;
} else {
if (hklInRange(self, settings, mask) == 1) {
return 1;
} else {
if (tryOmegaTweak(self, z1, &stt, &om, &chi, &phi) == 1) {
settings[0] = stt;
settings[1] = om;
settings[2] = chi;
settings[3] = phi;
return 1;
} else {
return findAllowedBisecting(self->lambda, z1, settings, hklInRangeOrion,
self);
}
}
}
return 0;
}
/*-------------------------------------------------------------------*/
static int settingsToBiList(struct __SingleDiff *self, double *settings)
{
setNewMotorTarget(self->motList, (char *) SXGetMotorName(TwoTheta),
(float) settings[0]);
setNewMotorTarget(self->motList, (char *) SXGetMotorName(Omega),
(float) settings[1]);
setNewMotorTarget(self->motList, (char *) SXGetMotorName(Chi),
(float) settings[2]);
setNewMotorTarget(self->motList, (char *) SXGetMotorName(Phi),
(float) settings[3]);
return 1;
}
/*--------------------------------------------------------------------*/
static int hklFromBiAngles(struct __SingleDiff *self, double *hkl)
{
pIDrivable pDriv;
MATRIX UBinv, z1m, rez;
double z1[3], stt, om, chi, phi;
int i;
pDriv = makeMotListInterface();
pDriv->GetValue(&self->motList, pServ->dummyCon);
UBinv = mat_inv(self->UB);
if (UBinv == NULL) {
return 0;
}
stt = getListMotorPosition(self->motList,
(char *) SXGetMotorName(TwoTheta));
om = getListMotorPosition(self->motList, (char *) SXGetMotorName(Omega));
chi = getListMotorPosition(self->motList, (char *) SXGetMotorName(Chi));
phi = getListMotorPosition(self->motList, (char *) SXGetMotorName(Phi));
z1FromAngles(self->lambda, stt, om, chi, phi, z1);
z1m = vectorToMatrix(z1);
rez = mat_mul(UBinv, z1m);
for (i = 0; i < 3; i++) {
hkl[i] = rez[i][0];
}
mat_free(UBinv);
mat_free(z1m);
mat_free(rez);
return 1;
}
/*--------------------------------------------------------------------*/
static int hklFromBiAnglesGiven(struct __SingleDiff *self,
double *settings, double *hkl)
{
MATRIX UBinv, z1m, rez;
double z1[3], stt, om, chi, phi;
int i;
UBinv = mat_inv(self->UB);
if (UBinv == NULL) {
return 0;
}
stt = settings[0];
om = settings[1];
chi = settings[2];
phi = settings[3];
z1FromAngles(self->lambda, stt, om, chi, phi, z1);
z1m = vectorToMatrix(z1);
rez = mat_mul(UBinv, z1m);
for (i = 0; i < 3; i++) {
hkl[i] = rez[i][0];
}
mat_free(UBinv);
mat_free(z1m);
mat_free(rez);
return 1;
}
/*------------------------------------------------------------------*/
static int getBiReflection(char *id, reflection * r)
{
pSICSOBJ refList;
double hkl[3], angles[4];
refList = SXGetReflectionList();
if (!GetRefIndexID(refList, id, hkl)) {
return 0;
} else {
r->h = hkl[0];
r->k = hkl[1];
r->l = hkl[2];
GetRefAnglesID(refList, id, angles);
r->s2t = angles[0];
r->om = angles[1];
r->chi = angles[2];
r->phi = angles[3];
}
return 1;
}
/*-------------------------------------------------------------------*/
MATRIX calcBiUBFromTwo(pSingleDiff self,
char *refid1, char *refid2, int *err)
{
MATRIX newUB;
reflection r1, r2;
lattice direct;
direct.a = self->cell[0];
direct.b = self->cell[1];
direct.c = self->cell[2];
direct.alpha = self->cell[3];
direct.beta = self->cell[4];
direct.gamma = self->cell[5];
if (!getBiReflection(refid1, &r1)) {
*err = REFERR;
return NULL;
}
if (!getBiReflection(refid2, &r2)) {
*err = REFERR;
return NULL;
}
newUB = calcUBFromCellAndReflections(direct, r1, r2, err);
return newUB;
}
/*-------------------------------------------------------------------*/
MATRIX calcBiUBFromThree(pSingleDiff self,
char *refid1, char *refid2, char *refid3,
int *err)
{
MATRIX newUB;
reflection r1, r2, r3;
if (!getBiReflection(refid1, &r1)) {
*err = REFERR;
return NULL;
}
if (!getBiReflection(refid2, &r2)) {
*err = REFERR;
return NULL;
}
if (!getBiReflection(refid3, &r3)) {
*err = REFERR;
return NULL;
}
newUB = calcUBFromThreeReflections(r1, r2, r3, self->lambda, err);
return newUB;
}
/*--------------------------------------------------------------------*/
static int calcBiZ1(pSingleDiff self, char *refid, double z1[3])
{
reflection r1;
if (!getBiReflection(refid, &r1)) {
return 0;
}
z1FromAngles(self->lambda, r1.s2t, r1.om, r1.chi, r1.phi, z1);
return 1;
}
/*--------------------------------------------------------------------*/
void initializeSingleBisecting(pSingleDiff diff)
{
if (diff->motList >= 0) {
LLDdelete(diff->motList);
}
diff->motList = LLDcreate(sizeof(MotControl));
addMotorToList(diff->motList, (char *) SXGetMotorName(TwoTheta), .0);
addMotorToList(diff->motList, (char *) SXGetMotorName(Omega), .0);
addMotorToList(diff->motList, (char *) SXGetMotorName(Chi), .0);
addMotorToList(diff->motList, (char *) SXGetMotorName(Phi), .0);
diff->calculateSettings = calculateBiSettings;
diff->settingsToList = settingsToBiList;
diff->hklFromAngles = hklFromBiAngles;
diff->hklFromAnglesGiven = hklFromBiAnglesGiven;
diff->calcUBFromTwo = calcBiUBFromTwo;
diff->calcUBFromThree = calcBiUBFromThree;
diff->calcZ1 = calcBiZ1;
}
/*--------------------------------------------------------------------*/
void initializeSingleBisectingOrion(pSingleDiff diff)
{
initializeSingleBisecting(diff);
diff->calculateSettings = calculateBiSettingsOrion;
}