/*****************************************************************************
*
* McStas, neutron ray-tracing package
* Copyright(C) 2005 Risoe National Laboratory.
*
* Component: Isotropic_Sqw
*
* %I
* Written by: Virginie Hugouvieux, E. Farhi
* Date: August 2003
* Version:
* Origin: ILL
* Modified by: E. Farhi, Jul 2005: made it work, concentric mode, multiple use
*
* Isotropic sample handling multiple scattering and absorption for a general
* S(q,w) (coherent and/or incoherent)
*
* %D
* An isotropic sample handling multiple scattering and including as input the
* dynamic structure factor of the chosen sample (e.g. from Molecular
* Dynamics). Handles elastic/inelastic, coherent and incoherent scattering -
* depending on the input S(Q,w) - with multiple scattering and absorption.
* Only the norm of Q is handled (not the vector), and thus suitable for
* liquids, gazes, amorphous and powder samples.
*
* If incoherent S(Q,w) file is specified as empty (0 or "") then the
* scattering is constant isotropic (Vanadium like).
* In case you only have one S(q,w) data containing both coherent and
* incoherent contributions you should e.g. use 'file_coh' and set 'sigma_coh'
* to the total scattering cross section.
*
* The S(q,w) are used as probability tables, and the intensity is weighted to
* the S(q,w) integral value. This integral value may be set from:
* norm_sqw=-1 compute |S| from the density (refer to the Component Manual)
* norm_sqw>0 use |S| = norm_sqw
* norm_sqw=0 compute |S| = \int S(q,w) dq dw from raw data files
* The default and recommanded normalization is 'norm_sqw=-1'.
*
* Focusing on the relevant [q,w] data range corresponding to the instrument
* setting can speed-up substantially the computation and prevent some neutrons
* to be removed when energy transfert is higher than actual neutron energy.
* An automatic such process may be activated when 'auto_qw=1' (recommanded).
* Additionally, for single order scattering (order=1), you may restrict the
* vertical spreading of the scattering area using d_phi parameter.
*
* If you use this component and produce valuable scientific results, please
* cite authors with references bellow (in Links).
*
* Sample shape:
* Sample shape may be a cylinder, a sphere, a box or a hollow cylinder/sphere.
* box/plate: xwidth x yheight x zthick (thickness=0)
* hollow box/plate:xwidth x yheight x zthick and thickness>0
* cylinder: radius_o x yheight (radius_i=0)
* hollow cylinder: radius_o x yheight and radius_i>0 or thickness>0
* sphere: radius_o (yheight=0 radius_i=0)
* hollow sphere: radius_o and radius_i>0 or thickness>0 (yheight=0)
*
* Concentric components:
* This component has the ability to contain other components when used in
* hollow cylinder geometry (namely sample environment, e.g. cryostat and
* furnace structure). Such component 'shells' should be split into input and
* output side surrounding the 'inside' components. First part must then use
* 'concentric=1' flag to enter the inside part. The component itself must be
* repeated to mark the end of the concentric zone. The number of concentric
* shells and number of components inside is not limited.
*
* COMPONENT S_in = Isotropic_Sqw(Sqw_coh="Al.laz", concentric=1, ...)
* AT (0,0,0) RELATIVE sample_position
*
* COMPONENT something_inside ... // e.g. the sample itself or other walls
* ...
*
* COMPONENT S_out = Isotropic_Sqw(Sqw_coh="Al.laz", ...)
* AT (0,0,0) RELATIVE sample_position
*
* Sqw file format:
* File format for S(Q,w) (coherent and incoherent) should contain 3 numerical
* blocks, defining q axis values (vector), then energy axis values (vector),
* then a matrix with one line per q axis value, containing Sqw values for
* each energy axis value. Comments (starting with '#') and non numerical lines
* are ignored and used to separate blocks. Sampling must be regular.
*
* Example:
* # q axis values
* # vector of m values in Angstroem-1
* 0.001000 .... 3.591000
* # w axis values
* # vector of n values in meV
* 0.001391 ... 1.681391
* # sqw values (one line per q axis value)
* # matrix of S(q,w) values (m rows x n values), one line per q value,
* 9.721422 10.599145 ... 0.000000
* 10.054191 11.025244 ... 0.000000
* ...
* 0.000000 ... 3.860253
*
* See for instance file He4.sqw. Such files may be obtained from e.g. INX,
* Nathan, Lamp and IDA softwares, as well as Molecular Dynamics.
*
* Powder file format:
* Files for coherent elastic powder scattering may also be used.
* Format specification follows the same principle as in the PowderN
* component, with parameters:
*
* powder_format=Crystallographica
* or powder_format=Fullprof
* or powder_format=Lazy
* or powder_format={j,d,F2,DW,Delta_d/d,1/2d,q,F} (column indexes 1:n)
*
* or column indexes (starting from 1) given as comments in the file header
* (e.g. '#column_j 4'). Refer to the PowderN component for more details.
* Delta_d/d and Debye-Waller factor may be specified for all lines with the
* 'powder_Dd' and 'powder_DW' parameters.
*
* Additionally a special [q,Sq] format is also defined with:
* powder_format=qSq
* for which column 1 is 'q' and column 2 is 'S(q)'.
*
* Examples:
* 1- Vanadium-like incoherent elastic scattering
* Isotropic_Sqw(V_rho=1/13.827,
* sigma_abs=5.08, sigma_inc=4.935, sigma_coh=0)
*
* 2- liq-4He parameters
* Isotropic_Sqw(..., Sqw_coh="He4.sqw", T=10)
*
* 3- powder sample
* Isotropic_Sqw(..., Sqw_coh="Al.laz", save_sqw=1)
*
* %BUGS:
* When used in concentric mode, multiple bouncing scattering
* (traversing the hollow part) is not taken into account.
*
* %VALIDATION
* For Vanadium incoherent scattering mode, V_sample, Single_crystal and
* Isotropic_Sqw produce equivalent results, eventhough the two later are more
* accurate (geometry, multiple scattering).
*
* %P
* INPUT PARAMETERS:
* Sqw_coh: [str] Name of the file containing the values of Q, w and S(Q,w)
* Coherent part; Q in Angs-1, E in meV, S(q,w) in meV-1
* Use 0 or "" to disable.
* Sqw_inc: [str] Name of the file containing the values of Q, w and S(Q,w)
* Incoherent (self) part.
* Use 0 or "" to scatter isotropically (V-like).
* sigma_coh:[barns] Coherent Scattering cross-section. 0 to disable
* sigma_inc:[barns] Incoherent Scattering cross-section. 0 to disable
* sigma_abs:[barns] Absorption cross-section at 2200 m/s. 0 to disables
* V_rho: [AA-3] Density of atoms (nb atoms/unit cell V_0).
* T: [K] Temperature of sample, detailed balance
*
* Geometry parameters:
* radius_o: [m] Outer radius of sample in (x,z) plane. cylinder/sphere.
* radius_i: [m] Inner radius of sample in (x,z) plane. cylinder/sphere.
* yheight: [m] Height of sample in vertical direction for box/cylinder
* shapes
* xwidth: [m] width for a box sample shape
* zthick: [m] thickness for a bulk box sample shape
* thickness: [m] Thickness of hollow sample (overrides radius_i).
*
* OPTIONAL PARAMETERS:
* norm_sqw: [1] When positive, the value is used as the integral of S(q,w)
* Null value uses computed integral.
* Any negative value will normalize on the density.
* auto_qw: [1] When set to 1, the [q,w] range will automatically be tuned
* to optimal setting whenever required.
* concentric: [1] indicates that this component in hollow cylinder geometry
* may contain other components.
* It should be then duplicated after the inside part.
* See description for an example.
* order: [1] Limit multiple scattering up to given order
* 0:all (default), 1:single, 2:double, ...
* verbose: [1] Verbosity level (0:silent, 1:normal, 2:verbose, 3:debug).
* d_phi: [deg] scattering vertical angular spreading (usually the heigh
* of the next component/detector). Use 0 for full space.
* This is only relevant for single scattering (order=1).
* save_sqw: [1] When set to 1, saves S(q), DOS and S(q,w) as monitors.
* weight: [g/mol] atomic/molecular weight of material
* density: [g/cm^3] density of material. V_rho=density/weight/1e24*N_A
* qmin: [Angs-1] Minimum Q value to use in S(q,w).
* qmax: [Angs-1] Maximum Q value to use in S(q,w).
* wmin: [meV] Minimum Energy value to use in S(q,w).
* wmax: [meV] Maximum Energy value to use in S(q,w).
* threshold: [1] Value under which S(Q,w) is not accounted for.
* to set according to the S(Q,w) values ,i.e. not too low.
*
* POWDER ELASTIC SCATTERING PARAMETERS:
* powder_Dd: [1] global Delta_d/d spreading, or 0 if ideal.
* powder_DW: [1] global Debey-Waller factor, if not in |F2| or 1.
* powder_format: [no quotes] name or definition of column indexes in file
* powder_Vc: [AA^3] volume of the unit cell
*
* OUTPUT PARAMETERS:
* VarSqw : internal structure containing many members/info
* VarSqw.dq wavevector transfert [Angs-1]
* VarSqw.dw energy transfert [Angs-1]
* VarSqw.type interaction type of event
* 'c' (coherent), 't' (transmitted)
* 'i' (incoherent) 'v' (isotropic incoherent, Vanadium-like).
* SCATTERED: order of multiple scattering
*
* %Links
* Hugouvieux V, Farhi E, Johnson MR, Virtual neutron scattering experiments, Physica B, 350 (2004) 151.
* %L
* Hugouvieux V, PhD, University of Montpellier II, France (2004).
* %L
* Cross sections for single elements: http://www.ncnr.nist.gov/resources/n-lengths/
* %L
* Cross sections for compounds: http://www.ncnr.nist.gov/resources/sldcalc.html
* %L
* Example data file He4.sqw
* %L
* The PowderN component.
* %L
* Web Elements http://www.webelements.com/
* %E
*****************************************************************************/
DEFINE COMPONENT Isotropic_Sqw
DEFINITION PARAMETERS (Sqw_coh=0, Sqw_inc=0, powder_format=Undefined)
SETTING PARAMETERS(radius_i=0,radius_o=0,thickness=0,
xwidth=0, yheight=0, zthick=0,
qmin=0, qmax=0, wmin=0, wmax=0, auto_qw=0,
threshold=1e-10, int order=0, T=0, verbose=1, d_phi=0, int concentric=0,
V_rho=0, sigma_abs=0, sigma_coh=0, sigma_inc=0, save_sqw=0,
norm_sqw=-1, powder_Dd=0, powder_DW=0, powder_Vc=0, density=0, weight=0)
OUTPUT PARAMETERS (VarSqw, columns)
STATE PARAMETERS (x,y,z,vx,vy,vz,t,s1,s2,p)
/*****************************************************************************/
/*****************************************************************************/
SHARE
%{
#ifndef ISOTROPIC_SQW
#define ISOTROPIC_SQW
/* {j d F2 DW Dd inv2d q F} + { Sq if j == -1}*/
#ifndef Crystallographica
#define Crystallographica { 4,5,7,0,0,0,0,0 }
#define Fullprof { 4,0,8,0,0,5,0,0 }
#define Undefined { 0,0,0,0,0,0,0,0 }
#define Lazy {17,6,0,0,0,0,0,13}
#endif
/* special case for [q,Sq] table */
#define qSq {-1,0,0,0,0,0,1,0 }
%include "read_table-lib"
/* For the density of states */
struct Sqw_D_struct
{
double omega; /* omega value for the data block */
double I; /* intensity for the current DOS block */
double cumul_I; /* cumulated intensity (between 0 and 1) */
};
/* For the Q(w) probabilities */
struct Sqw_Q_struct
{
double Q; /* omega value for the data block */
double proba; /* normalized probability for the current DOS block */
double cumul_proba; /* normalized cumulated probability */
};
struct Sqw_Data_struct /* contains normalized Sqw data for probabilities */
{
struct Sqw_D_struct *DOS; /* P(w) = density of states */
struct Sqw_Q_struct **Q_proba_per_w; /* P(Q|w)= probability of each Q with w */
long *DOS_lookup;
long **QW_lookup;
double*Sq; /* S(q) = \int_w S(q,w) */
long nb_q;
long nb_w; /* length of q and w vectors/axes */
double q_min, q_max;
double w_min, w_max;
long lookup_length;
char filename[80];
double intensity;
double intensity_total;
};
struct Sqw_sample_struct {
char compname[256];
struct Sqw_Data_struct Data_inc;
struct Sqw_Data_struct Data_coh;
double s_abs, s_coh, s_inc;
double my_s;
double my_a_v;
double rho;
double T2E;
double sqSE2K;
int maxloop;
long neutron_removed;
long neutron_enter;
long neutron_pmult;
long neutron_exit;
double Dd, DWfactor;
double sqw_threshold;
char verbose_output;
char shape; /* 0:cylinder, 1:box, 2:sphere */
double dq, dw; /* q/w transfert */
char type; /* interaction type: c(coherent), i(incoherent),
V(isotropic incoherent), t(transmitted) */
double ki_x,ki_y,ki_z,kf_x,kf_y,kf_z;
double ti, tf;
double vi, vf;
double ki, kf;
double w_max, w_min, q_max, q_min;
int column_order[9]; /* column signification */
double Temperature;
double sqw_norm;
char sqw_auto;
};
#include
#include
void Sqw_Data_init(struct Sqw_Data_struct *Sqw_Data)
{
Sqw_Data->nb_q =0;
Sqw_Data->nb_w =0;
Sqw_Data->q_min =0;
Sqw_Data->q_max =0;
Sqw_Data->w_min =0;
Sqw_Data->w_max =0;
Sqw_Data->lookup_length=100; /* length of lookup tables */
Sqw_Data->intensity =0;
Sqw_Data->intensity_total=0;
strcpy(Sqw_Data->filename, "");
Sqw_Data->DOS =NULL;
Sqw_Data->Q_proba_per_w=NULL;
Sqw_Data->DOS_lookup =NULL;
Sqw_Data->QW_lookup =NULL;
Sqw_Data->Sq =NULL;
}
double Sqw_powder_gauss(double x, double mean, double rms) {
return (exp(-((x)-(mean))*((x)-(mean))/(2*(rms)*(rms)))/(sqrt(2*PI)*(rms)));
}
/*****************************************************************************
* Sqw_read_PowderN: Read PowderN data files
* Returns t_Table array or NULL in case of error
* Used in : Sqw_readfile (1)
*****************************************************************************/
t_Table *Sqw_read_PowderN(struct Sqw_sample_struct *Sqw, t_Table sqwTable)
{
struct line_data
{
double F2; /* Value of structure factor */
double q; /* Q vector */
int j; /* Multiplicity */
double DWfactor; /* Debye-Waller factor */
double w; /* Intrinsic line width */
};
struct line_data *list = NULL;
double q_count=0, j_count=0, F2_count=0;
int mult_count =0;
double q_step =FLT_MAX;
long size =0;
int i, index;
double q_min=0, q_max=0;
char flag=0;
int list_count=0;
double q_step_cur;
char flag_qSq = 0;
t_Table *retTable;
flag_qSq = (Sqw->column_order[8]>0 && Sqw->column_order[6]>0);
size = sqwTable.rows;
Table_Info(sqwTable);
if (Sqw->verbose_output > 0)
printf("Isotropic_sqw: Converting %d powder lines from %s into S(q,w) data\n",
size, sqwTable.filename);
/* allocate line_data array */
list = (struct line_data*)malloc(size*sizeof(struct line_data));
for (i=0; iDd >= 0) w = Sqw->Dd;
if (Sqw->DWfactor > 0) DWfactor = Sqw->DWfactor;
/* get data from table using columns {j d F2 DW Dd inv2d q} + { Sq }*/
/* column indexes start at 1, thus need to substract 1 */
if (Sqw->column_order[0]>0)
j = Table_Index(sqwTable, i, Sqw->column_order[0]-1);
if (Sqw->column_order[1]>0)
d = Table_Index(sqwTable, i, Sqw->column_order[1]-1);
if (Sqw->column_order[2]>0)
F2 = Table_Index(sqwTable, i, Sqw->column_order[2]-1);
if (Sqw->column_order[3]>0)
DWfactor = Table_Index(sqwTable, i, Sqw->column_order[3]-1);
if (Sqw->column_order[4]>0)
w = Table_Index(sqwTable, i, Sqw->column_order[4]-1);
if (Sqw->column_order[5]>0) {
d = Table_Index(sqwTable, i, Sqw->column_order[5]-1); if (d) d = 1/d/2; }
if (Sqw->column_order[6]>0)
q = Table_Index(sqwTable, i, Sqw->column_order[6]-1);
if (Sqw->column_order[7]>0 && !F2)
{F2= Table_Index(sqwTable, i, Sqw->column_order[7]-1); F2 *= F2;}
if (Sqw->column_order[8]>0)
Sq= Table_Index(sqwTable, i, Sqw->column_order[8]-1);
if (q > 0 && Sq >= 0) F2 = Sq;
if (d > 0 && q <= 0) q = 2*PI/d;
/* assign and check values */
j = (j > 0 ? j : 0);
DWfactor = (DWfactor > 0 ? DWfactor : 1);
w = (w>0 ? w : 0);
F2 = (F2 >= 0 ? F2 : 0);
d = (q > 0 ? 2*PI/d : 0);
if (j == 0 || d == 0 || q == 0) {
printf("Isotropic_sqw: %s: line %i has invalid definition\n"
" (mult=0 or q=0 or d=0)\n", Sqw->compname, i);
continue;
}
list[list_count].j = j;
list[list_count].q = q;
list[list_count].DWfactor = DWfactor;
list[list_count].w = w;
list[list_count].F2= F2; /* or S(q) if flag_qSq */
if (q_max < d) q_max = q;
if (q_min > d) q_min = q;
if (list_count > 1) {
q_step_cur = fabs(list[list_count].q - list[list_count-1].q);
if (q_step_cur > 0 && (!q_step || q_step_cur < q_step))
q_step = q_step_cur;
}
/* adjust multiplicity if j-column + multiple d-spacing lines */
/* if d = previous d, increase line duplication index */
if (!q_count) q_count = q;
if (!j_count) j_count = j;
if (!F2_count) F2_count = F2;
if (fabs(q_count-q) < 0.0001*fabs(q)
&& fabs(F2_count-F2) < 0.0001*fabs(F2) && j_count == j) {
mult_count++; flag=0; }
else flag=1;
if (i == size-1) flag=1;
/* else if d != previous d : just passed equivalent lines */
if (flag) {
if (i == size-1) list_count++;
/* if duplication index == previous multiplicity */
/* set back multiplicity of previous lines to 1 */
if (mult_count == list[list_count-1].j
|| (mult_count == list[list_count].j && i == size-1)) {
printf("Isotropic_Sqw: %s: Set multiplicity to 1 for lines [%i:%i]\n"
" (d-spacing %g is duplicated %i times)\n",
Sqw->compname, list_count-mult_count, list_count-1, list[list_count-1].q, mult_count);
for (index=list_count-mult_count; indexverbose_output > 0)
printf("Isotropic_sqw: q range [%g:%g], creating %li elements vector\n",
q_min, q_max, size);
retTable = (t_Table*)calloc(4, sizeof(t_Table));
if (!retTable) printf("Isotropic_Sqw: ERROR: Cannot allocate PowderN->Sqw table.\n");
else {
char *header;
if (!Table_Init(&retTable[0], size, 1))
{ printf("Isotropic_Sqw: Cannot allocate q-axis from Powder lines.\n"); return(NULL); }
if (!Table_Init(&retTable[1], 1, 1))
{ printf("Isotropic_Sqw: Cannot allocate w-axis from Powder lines.\n"); return(NULL); }
if (!Table_Init(&retTable[2], size, 1))
{ printf("Isotropic_Sqw: Cannot allocate Sqw from Powder lines.\n"); return(NULL); }
Table_Init(&retTable[3], 0,0);
header = malloc(64); if (header)
{ retTable[0].header = header; strcpy(retTable[0].header, "q"); }
header = malloc(64); if (header)
{ retTable[1].header = header; strcpy(retTable[1].header, "w"); }
header = malloc(64); if (header)
{ retTable[2].header = header; strcpy(retTable[2].header, "Sqw"); }
for (i=0; i < 4; i++) {
retTable[i].array_length = 3;
retTable[i].block_number = i+1;
}
if (!flag_qSq)
for (i=0; i 0 && !flag_qSq) {
peak_qmin = list[i].q*(1 - list[i].w*3);
peak_qmax = list[i].q*(1 + list[i].w*3);
} else { /* Dirac peak, no width */
peak_qmin = peak_qmax = list[i].q;
}
factor = 2*(2*PI)*(2*PI)*(2*PI)*(2*PI)*Sqw->rho
*list[i].j*(list[i].DWfactor ? list[i].DWfactor : 1)
/(Sqw->type == 'c' ? Sqw->s_coh : Sqw->s_inc)*list[i].F2;
for (q=peak_qmin; q <= peak_qmax; q += q_step) {
index = (long)floor(size*(q - q_min)/(q_max - q_min));
if (index < 0) index=0;
else if (index >= size) index = size-1;
if (flag_qSq) {
retTable[2].data[index] += list[i].F2;
retTable[0].data[index] = list[i].q;
} else if (list[i].w <=0 || list[i].w*q < q_step) /* step function */
retTable[2].data[index] += factor/q_step;
else /* gaussian */
retTable[2].data[index] += factor
* Sqw_powder_gauss(q, list[i].q, list[i].w*list[i].q);
}
} /* end for i */
Table_Stat(&retTable[0]); Table_Stat(&retTable[1]); Table_Stat(&retTable[2]);
}
return(retTable);
} /* Sqw_read_PowderN */
/*****************************************************************************
* Sqw_readfile: Read Sqw data files
* Returns Sqw_Data_struct or NULL in case of error
* Used in : Sqw_init (2)
*****************************************************************************/
struct Sqw_Data_struct *Sqw_readfile(
struct Sqw_sample_struct *Sqw, char *file, struct Sqw_Data_struct *Sqw_Data)
{
t_Table *Table_Array= NULL;
t_Table *newTable;
long nblocks = 0;
t_Table Sqw_restrict;
long i, j;
char flag=1; /* exit_flag */
double q, w, val, sum;
double mat_density=0, mat_weight=0;
double alpha=1;
long *DOS_lookup;
long **QW_lookup;
char **parsing;
/* setup default */
Sqw_Data_init(Sqw_Data);
if (!file || !strlen(file)) return(Sqw_Data);
Table_Array = Table_Read_Array(file, &nblocks);
strncpy(Sqw_Data->filename, file, 80);
if (!Table_Array) return(NULL);
/* parsing of header */
parsing = Table_ParseHeader(Table_Array[0].header,
"Vc","V_0",
"sigma_abs","sigma_a ",
"sigma_inc","sigma_i ",
"column_j", /* 6 */
"column_d",
"column_F2",
"column_DW",
"column_Dd",
"column_inv2d", "column_1/2d", "column_sintheta_lambda",
"column_q", /* 14 */
"sigma_coh","sigma_c ",
"Temperature",
"column_Sq",
"column_F ", /* 19 */
"V_rho",
"density",
"weight",
NULL);
if (parsing) {
if (parsing[0] && !Sqw->rho) Sqw->rho =1/atof(parsing[0]);
if (parsing[1] && !Sqw->rho) Sqw->rho =1/atof(parsing[1]);
if (parsing[2] && !Sqw->s_abs) Sqw->s_abs = atof(parsing[2]);
if (parsing[3] && !Sqw->s_abs) Sqw->s_abs = atof(parsing[3]);
if (parsing[4] && !Sqw->s_inc) Sqw->s_inc = atof(parsing[4]);
if (parsing[5] && !Sqw->s_inc) Sqw->s_inc = atof(parsing[5]);
if (parsing[6]) Sqw->column_order[0]=atoi(parsing[6]);
if (parsing[7]) Sqw->column_order[1]=atoi(parsing[7]);
if (parsing[8]) Sqw->column_order[2]=atoi(parsing[8]);
if (parsing[9]) Sqw->column_order[3]=atoi(parsing[9]);
if (parsing[10]) Sqw->column_order[4]=atoi(parsing[10]);
if (parsing[11]) Sqw->column_order[5]=atoi(parsing[11]);
if (parsing[12]) Sqw->column_order[5]=atoi(parsing[12]);
if (parsing[13]) Sqw->column_order[5]=atoi(parsing[13]);
if (parsing[14]) Sqw->column_order[6]=atoi(parsing[14]);
if (parsing[15] && !Sqw->s_coh) Sqw->s_coh=atof(parsing[15]);
if (parsing[16] && !Sqw->s_coh) Sqw->s_coh=atof(parsing[16]);
if (parsing[17] && !Sqw->Temperature) Sqw->Temperature=atof(parsing[17]);
if (parsing[18]) Sqw->column_order[8]=atoi(parsing[18]);
if (parsing[19]) Sqw->column_order[7]=atoi(parsing[19]);
if (parsing[20] && !Sqw->rho) Sqw->rho =atof(parsing[20]);
if (parsing[21] && !Sqw->rho) mat_density =atof(parsing[21]);
if (parsing[22] && !Sqw->rho) mat_weight =atof(parsing[22]);
for (i=0; i<=22; i++) if (parsing[i]) free(parsing[i]);
free(parsing);
}
if (!Sqw->rho && mat_density > 0 && mat_weight > 0) {
/* molar volume [cm^3/mol] = weight [g/mol] / density [g/cm^3] */
/* atom density per Angs^3 = [mol/cm^3] * N_Avogadro *(1e-8)^3 */
Sqw->rho = mat_density/mat_weight/1e24*6.02214199e23;
if (Sqw->verbose_output) printf("Isotropic_Sqw: %s: Computing atom density V_rho=%g [AA^-3] \n",NAME_CURRENT_COMP, Sqw->rho);
}
if (nblocks) {
if (nblocks == 1) {
/* import Powder file */
newTable = Sqw_read_PowderN(Sqw, Table_Array[0]);
if (!newTable) {
printf("Isotropic_Sqw: %s: ERROR importing powder line file %s.\n"
" Check format definition.\n",
Sqw->compname, file);
exit(-1);
} else flag=0;
Table_Free_Array(Table_Array);
Table_Array = newTable;
} else if (nblocks != 3) {
printf("Isotropic_Sqw: %s: "
"File %s contains %i block%s instead of 3.\n",
Sqw->compname, file, nblocks, (nblocks == 1 ? "" : "s"));
} else flag=0;
}
/* print some info about Sqw files */
if (flag) Sqw->verbose_output = 2;
if (nblocks && Sqw->verbose_output > 1) {
printf("Isotropic_Sqw: %s file read, analysing...\n", file);
Table_Info_Array(Table_Array);
}
if (flag) {
if (nblocks) printf("ERROR Wrong file format.\n"
" Disabling contribution.\n"
" File must contain 3 blocks for [q,w,sqw]\n");
return(Sqw_Data);
}
/* restrict gridding range to q and omega min/max */
for (i=0; i< 2; i++) { /* i=[q,w] */
long index;
double val_min, val_max;
double *usr_min,*usr_max;
t_Table Table;
Table = Table_Array[i];
/* make q,w axes a single row */
if (Table.columns == 1) { Table.columns = Table.rows; Table.rows = 1; }
index = Table.rows*Table.columns; /* full length of q,w */
val_min= Table.min_x;
val_max= Table.max_x;
/* update min/max accroding to input parameters */
if (i == 0) { /* q */
val_min = (val_min < Sqw->q_min ? Sqw->q_min : val_min);
val_max = (val_max > Sqw->q_max ? Sqw->q_max : val_max);
} else {
val_min = (val_min < Sqw->w_min ? Sqw->w_min : val_min);
val_max = (val_max > Sqw->w_max ? Sqw->w_max : val_max);
}
if (i == 0) Sqw_Data->nb_q = index; else Sqw_Data->nb_w = index;
/* set restricted range */
usr_min = (i == 0 ? &(Sqw_Data->q_min) : &(Sqw_Data->w_min));
usr_max = (i == 0 ? &(Sqw_Data->q_max) : &(Sqw_Data->w_max));
if (!*usr_min && !*usr_max) {
*usr_min = val_min; *usr_max = val_max;
}
}
if (!Sqw_Data->nb_q || !Sqw_Data->nb_w) {
printf("Isotropic_Sqw: %s: Data file %s has null q or omega information (%lix%li).\n"
"ERROR Exiting.\n",
Sqw->compname, file, Sqw_Data->nb_w, Sqw_Data->nb_q);
return(NULL);
}
/* build the 'total' = Sqw table in restricted range */
Table_Init(&Sqw_restrict, Sqw_Data->nb_q, Sqw_Data->nb_w);
Sqw_restrict.block_number = 1;
if (!Sqw_restrict.data || !Sqw_restrict.rows*Sqw_restrict.columns) {
printf("Isotropic_Sqw: %s: Cannot allocate Sqw_restrict Table (%lix%li).\n"
"ERROR Exiting.\n",
Sqw->compname, Sqw_Data->nb_q, Sqw_Data->nb_w);
return(NULL);
}
sprintf(Sqw_restrict.filename, "S(q,w) from %s", file);
/* now compute S(q) and normalize */
Sqw_Data->Sq =
(double *)calloc(Sqw_Data->nb_q, sizeof(double));
if (!Sqw_Data->Sq) {
printf("Isotropic_Sqw: %s: Cannot allocate S(Q) array (%li bytes).\n"
"ERROR Exiting.\n",
Sqw->compname, Sqw_Data->nb_q*sizeof(double));
return(NULL);
}
sum = val = alpha = 0;
for (i=0; i < (Sqw_Data->nb_q) ; i++) {
double sqw=0, sq=0;
q = Table_Index(Table_Array[0], 0, i);
for (j=0; j < (Sqw_Data->nb_w) ; j++) {
flag = 1;
if (nblocks) sqw = Table_Index(Table_Array[2], i, j);
if (sqw < Sqw->sqw_threshold) flag = 0; /* Sqw lower than threshold */
/* check for q,w within min/max */
w = Table_Index(Table_Array[1], 0, j);
if (q < Sqw_Data->q_min || q > Sqw_Data->q_max) flag = 0; /* within q range ? */
if (w < Sqw_Data->w_min || w > Sqw_Data->w_max) flag = 0; /* within w range ? */
if (flag) val += sqw; /* only on restricted range */
sum += sqw; /* on total Sqw range */
if (!Table_SetElement(&Sqw_restrict, i, j, (flag ? sqw : 0) ))
printf("Isotropic_Sqw: %s: "
"Error when setting Sqw[%i,%i]=%g from file %s\n",
Sqw->compname, i,j, sqw, file);
sq += sqw;
}
Sqw_Data->Sq[i] = sq;
alpha += q*q*sq;
}
if (Sqw->verbose_output > 1)
printf("Isotropic_Sqw: %s: Generated S(Q)[%i]\n",
Sqw->compname, Sqw_Data->nb_q);
alpha /= Sqw_Data->nb_q; /* this is \int q^2 S(q) dq */
if (alpha) { /* compute theoretical |S| norm */
alpha = (Sqw_Data->q_max*Sqw_Data->q_max*Sqw_Data->q_max/3 - 2*PI*PI*Sqw->rho)/alpha;
val *= alpha;
sum *= alpha;
for (i=0; i < (Sqw_Data->nb_q) ; Sqw_Data->Sq[i++] *= alpha);
}
val /= Sqw_Data->nb_q*Sqw_Data->nb_w;
sum /= Sqw_Data->nb_q*Sqw_Data->nb_w;
if (!alpha) {
printf("Isotropic_Sqw: %s: Can not compute S(q) norm\n"
"WARNING Using raw norm |S|=%g (not safe)\n",
Sqw->compname, sum);
alpha=1;
} else {
if (Sqw->verbose_output > 1
|| fabs(log10(fabs(alpha))) > 1 )
printf("Isotropic_Sqw: %s: Theoretical %s Norm |S| = %g\n",
Sqw->compname,
(Sqw->type=='c' ? "coherent" : "incoherent"),
sum);
if (Sqw->sqw_norm>0 && fabs(log10(fabs(sum/Sqw->sqw_norm))) > 0.1)
printf("WARNING The specified norm %g is probably wrong\n"
" Multiply intensistes by factor %g\n",
Sqw->sqw_norm, sum/Sqw->sqw_norm);
}
if (!val || !sum) {
printf("Isotropic_Sqw: %s: selected (Q,w) range is outside\n"
"ERROR available Sqw data. |S|=%g\n",
Sqw->compname, sum);
printf(" q=[%g:%g] w=[%g:%g] size=[%ix%i]\n",
Sqw_Data->q_min, Sqw_Data->q_max,
Sqw_Data->w_min, Sqw_Data->w_max,
Sqw_Data->nb_q, Sqw_Data->nb_w);
return(NULL);
}
Table_Stat(&Sqw_restrict);
Sqw_Data->intensity = val;
Sqw_Data->intensity_total = sum;
if (Sqw->verbose_output > 0) {
printf("Isotropic_Sqw: %s: Generated %s normalized %scoherent Sqw\n"
" q=[%g:%g] w=[%g:%g] |S|=%g size=[%ix%i]\n",
Sqw->compname, file, (Sqw->type == 'i' ? "in" : ""),
Sqw_Data->q_min, Sqw_Data->q_max,
Sqw_Data->w_min, Sqw_Data->w_max, val,
Sqw_Data->nb_q, Sqw_Data->nb_w);
if (Sqw_Data->w_min == Sqw_Data->w_max && Sqw_Data->nb_w == 1)
printf(" Elastic scattering only.\n");
}
/* set up density of states and index_wconst */
/* uses: Sqw_restrict and w axes */
Sqw_Data->DOS =
(struct Sqw_D_struct*)calloc(Sqw_Data->nb_w, sizeof(struct Sqw_D_struct));
if (!Sqw_Data->DOS) {
printf("Isotropic_Sqw: %s: Cannot allocate DOS (%li bytes).\n"
"ERROR Exiting.\n",
Sqw->compname, Sqw_Data->nb_w*sizeof(struct Sqw_D_struct));
return(NULL);
}
val = 0;
for (j=0; j < (Sqw_Data->nb_w) ; j++) {
double local_val=0;
w = Table_Index(Table_Array[1], 0, j);
for (i=0; i < (Sqw_Data->nb_q) ; i++) { /* integrate on all q values */
local_val += Table_Index(Sqw_restrict, i, j)*alpha;
}
Sqw_Data->DOS[j].omega = w;
Sqw_Data->DOS[j].I = local_val;
val += local_val; /* total intensity */
}
/* normnalize and compute cumulated probability */
for (j=0; j < (Sqw_Data->nb_w) ; j++) {
Sqw_Data->DOS[j].I /= val;
Sqw_Data->DOS[j].cumul_I = Sqw_Data->DOS[j].I;
/* cumulated probabilities for the random choice */
if (j) Sqw_Data->DOS[j].cumul_I += Sqw_Data->DOS[j-1].cumul_I;
}
if (Sqw->verbose_output > 1)
printf("Isotropic_Sqw: %s: Generated normalized DOS[%i]\n",
Sqw->compname, Sqw_Data->nb_w);
/* set up Q probability table per w bin */
/* uses: Sqw_restrict */
Sqw_Data->Q_proba_per_w =
(struct Sqw_Q_struct**)calloc(Sqw_Data->nb_w, sizeof(struct Sqw_Q_struct*));
if (!Sqw_Data->Q_proba_per_w) {
printf("Isotropic_Sqw: %s: Cannot allocate P(Q|w) array (%li bytes).\n"
"ERROR Exiting.\n",
Sqw->compname, Sqw_Data->nb_w*sizeof(struct Sqw_Q_struct*));
return(NULL);
}
for (j=0; j < (Sqw_Data->nb_w) ; j++) {
double intensity=0;
Sqw_Data->Q_proba_per_w[j]=
(struct Sqw_Q_struct*)calloc(Sqw_Data->nb_q, sizeof(struct Sqw_Q_struct));
if (!Sqw_Data->Q_proba_per_w[j]) {
printf("Isotropic_Sqw: %s: Cannot allocate P(Q|w)[%i] (%li bytes).\n"
"ERROR Exiting.\n",
Sqw->compname, j, Sqw_Data->nb_q*sizeof(struct Sqw_Q_struct));
return(NULL);
}
/* set P(Q|W) and compute total intensity */
for (i=0; i < (Sqw_Data->nb_q) ; i++) {
q = Table_Index(Table_Array[0], 0, i);
Sqw_Data->Q_proba_per_w[j][i].Q = q;
Sqw_Data->Q_proba_per_w[j][i].proba = Table_Index(Sqw_restrict, i, j)*alpha;
intensity += Sqw_Data->Q_proba_per_w[j][i].proba;
}
for (i=0; i < (Sqw_Data->nb_q) ; i++) {
Sqw_Data->Q_proba_per_w[j][i].cumul_proba =
Sqw_Data->Q_proba_per_w[j][i].proba/intensity;
if (i>0) /* cumulate */
Sqw_Data->Q_proba_per_w[j][i].cumul_proba +=
Sqw_Data->Q_proba_per_w[j][i-1].cumul_proba;
}
}
if (Sqw->verbose_output > 1)
printf("Isotropic_Sqw: %s: Generated P(Q|w)\n",
Sqw->compname);
/* generate quick lookup tables for DOS and Q_proba_per_w */
Sqw_Data->DOS_lookup = NULL;
DOS_lookup = (long*)calloc(Sqw_Data->lookup_length, sizeof(long));
if (!DOS_lookup) {
printf("Isotropic_Sqw: %s: Cannot allocate DOS_lookup (%li bytes).\n"
"Warning Will be slower.\n",
Sqw->compname, Sqw_Data->lookup_length*sizeof(long));
}
if (DOS_lookup) {
for (i=0; i < Sqw_Data->lookup_length; i++) {
w = (double)i/(double)Sqw_Data->lookup_length;
DOS_lookup[i] = Sqw_search_DOS(*Sqw_Data, w);
}
Sqw_Data->DOS_lookup = DOS_lookup;
}
Sqw_Data->QW_lookup = NULL;
QW_lookup=(long**)calloc(Sqw_Data->nb_w, sizeof(long*));
if (!QW_lookup) {
printf("Isotropic_Sqw: %s: Cannot allocate QW_lookup (%li bytes).\n"
"Warning Will be slower.\n",
Sqw->compname, Sqw_Data->nb_w*sizeof(long*));
}
if (QW_lookup) {
for (j=0; j < (Sqw_Data->nb_w) ; j++) {
QW_lookup[j] = (long*)calloc(Sqw_Data->lookup_length, sizeof(long));
if (!QW_lookup[j]) {
printf("Isotropic_Sqw: %s: Cannot allocate QW_lookup[%i] (%li bytes).\n"
"Warning Will be slower.\n",
Sqw->compname, j, Sqw_Data->lookup_length*sizeof(long));
free(QW_lookup); QW_lookup = NULL;
} else {
for (i=0; i < Sqw_Data->lookup_length; i++) {
w = (double)i/(double)Sqw_Data->lookup_length;
QW_lookup[j][i] = Sqw_search_Q_proba_per_w(*Sqw_Data, w, j);
}
}
}
Sqw_Data->QW_lookup = QW_lookup;
}
if ((Sqw_Data->QW_lookup || Sqw_Data->DOS_lookup) && Sqw->verbose_output > 1)
printf("Isotropic_Sqw: %s: Generated lookup tables with %i entries\n",
Sqw->compname, Sqw_Data->lookup_length);
Table_Free_Array(Table_Array);
Table_Free(&Sqw_restrict);
return(Sqw_Data);
} /* end Sqw_readfile */
/*****************************************************************************
* Sqw_init_read: Read coherent/incoherent Sqw data files
* Returns Sqw total intensity, or 0 (error)
* Used in : INITIALIZE (1)
*****************************************************************************/
double Sqw_init(struct Sqw_sample_struct *Sqw, char *file_coh, char *file_inc)
{
double ret=0;
struct Sqw_Data_struct *d_inc, *d_coh;
Sqw->type = 'i';
d_inc = Sqw_readfile(Sqw, file_inc, &(Sqw->Data_inc));
if (d_inc && !d_inc->intensity && Sqw->s_inc) {
if (Sqw->verbose_output > 0)
printf("Isotropic_Sqw: %s: Using Isotropic elastic incoherent scattering\n"
" (Vanadium like)\n", Sqw->compname);
ret=1;
}
Sqw->type = 'c';
d_coh = Sqw_readfile(Sqw, file_coh, &(Sqw->Data_coh));
if (!d_inc || !d_coh) return(0);
if (d_coh && !d_coh->intensity && Sqw->s_coh)
printf("Isotropic_Sqw: %s: Coherent scattering Sqw intensity is null.\n"
"Warning Disabling coherent scattering.\n", Sqw->compname);
if (d_inc && d_coh && d_inc->intensity && d_coh->intensity) {
char msg[80];
strcpy(msg, "");
/* check dimensions/limits for Q, Energy in coh and inc Tables */
if (d_inc->nb_q != d_coh->nb_q)
strcpy(msg, "Q axis size");
if (d_inc->nb_w != d_coh->nb_w)
strcpy(msg, "Energy axis size");
if ((d_inc->q_min != d_coh->q_min)
|| (d_inc->q_max != d_coh->q_max))
strcpy(msg, "Q axis limits");
if ((d_inc->w_min != d_coh->w_min)
|| (d_inc->w_max != d_coh->w_max))
strcpy(msg, "Energy axis limits");
if (strlen(msg)) {
printf("Isotropic_Sqw: %s: Sqw data from files %s and %s do not match\n"
"ERROR wrong %s\n",
Sqw->compname, file_coh, file_inc, msg);
return(0);
}
}
if (!ret) ret=d_inc->intensity+d_coh->intensity;
return(ret);
}
/*****************************************************************************
* Sqw_search_root: Search for the roots of A*x**2 + B*x + C
* The roots are returned as x1 and x2
* Used in : TRACE (1)
*****************************************************************************/
int Sqw_search_root(double A, double B, double C,
double *x1, double *x2)
{
double delta, sqrtdelta, inv_2A;
int OK = 0;
if (fabs(A) < 1E-10) {
if (B) { *x1 = -C/B; *x2 = *x1; OK=3; }
} else {
delta = B*B - 4*A*C;
if (delta < 0) {
/* Imaginary roots */
OK=0;
} else {
/* Real roots */
sqrtdelta = sqrt(delta);
inv_2A = 1/(2*A);
*x1 = inv_2A*(-B - sqrtdelta);
*x2 = inv_2A*(-B + sqrtdelta);
OK = 1;
}
return(OK);
}
}
/*****************************************************************************
* Sqw_search_DOS: For a given random number 'randnum', search for the bin
* containing the corresponding Sqw->DOS
* Used in : TRACE (1)
*****************************************************************************/
int Sqw_search_DOS(struct Sqw_Data_struct Sqw, double randnum)
{
int i=1;
if (Sqw.DOS_lookup) {
i = Sqw.DOS_lookup[(long)floor(randnum*Sqw.lookup_length)];
}
while (i < Sqw.nb_w && (&(Sqw.DOS[i]) != NULL) && (randnum > Sqw.DOS[i].cumul_I))
i++;
if (i >= Sqw.nb_w) i = Sqw.nb_w;
if (&(Sqw.DOS[i]) == NULL)
{
fprintf(stderr, "Isotropic_Sqw: No corresponding value in the DOS. randnum too big.\n");
fprintf(stderr, " i=%i ; randnum=%f ; Sqw.DOS[i-1].cumul_I=%f (Sqw_search_DOS)\n",
i, randnum, Sqw.DOS[i-1].cumul_I);
return i-1;
}
else
return (i < Sqw.nb_w ? i : Sqw.nb_w-1);
}
/*****************************************************************************
* Sqw_search_Q_proba_per_w: For a given random number randnum, search for
* the bin containing the corresponding Sqw.DOS in the Q probablility grid
* Used in : TRACE (1)
*****************************************************************************/
int Sqw_search_Q_proba_per_w(struct Sqw_Data_struct Sqw,
double randnum, int index)
{
int i=0;
if (Sqw.QW_lookup && Sqw.QW_lookup[index]) {
i = Sqw.QW_lookup[index][(long)floor(randnum*Sqw.lookup_length)];
}
while (i < Sqw.nb_q && (&(Sqw.Q_proba_per_w[index][i]) != NULL)
&& (randnum > Sqw.Q_proba_per_w[index][i].cumul_proba)) {
i++;
}
if (&(Sqw.Q_proba_per_w[index][i]) == NULL)
return -1;
else
return i;
}
#endif
%}
/*****************************************************************************/
/*****************************************************************************/
DECLARE
%{
struct Sqw_sample_struct VarSqw;
int columns[8] = powder_format;
/* end DECLARE */
%}
/*****************************************************************************/
/*****************************************************************************/
INITIALIZE
%{
int i;
/* check for parameters */
VarSqw.verbose_output= verbose;
if (radius_o>0) {
if (thickness>0 && thickness < radius_o) radius_i=radius_o-thickness;
if (yheight>0) VarSqw.shape = 0; else VarSqw.shape = 2;
if (VarSqw.verbose_output)
printf("Isotropic_Sqw: %s: is a %s%s : R_i=%f R_o=%f h=%f \n",
NAME_CURRENT_COMP, (radius_i > 0 ? "hollow ":""),
(VarSqw.shape ? "Sphere" : "Cylinder"),
radius_i,radius_o,yheight);
if (radius_i >= radius_o) {
printf("Isotropic_Sqw: %s:thickness/radius_i bigger than sample (radius_o) !\n"
"ERROR Disabling\n",
NAME_CURRENT_COMP);
exit(0);
}
} else if (xwidth>0 && yheight>0 && zthick>0) {
VarSqw.shape = 1;
if (2*thickness>=xwidth || 2*thickness>=yheight || 2*thickness>zthick) {
printf("Isotropic_Sqw: %s:2*thickness bigger than sample (xwidth yheight zthick)!\n"
"ERROR Disabling\n",
NAME_CURRENT_COMP);
exit(0);
}
if (VarSqw.verbose_output)
printf("Isotropic_Sqw: %s: is a %sBox : dx=%f dy=%f dz=%f \n",
NAME_CURRENT_COMP, (thickness>0 ? "hollow" : ""), xwidth,yheight,zthick);
} else {
printf("Isotropic_Sqw: %s: ERROR sample has no dimension\n"
" set some of radius_o xwidth yheight zthick \n",
NAME_CURRENT_COMP);
exit(0);
}
if (concentric &&
!((VarSqw.shape==1 && thickness > 0) || (VarSqw.shape!=1 && radius_i > 0))) {
printf("Isotropic_Sqw: %s:Can not use concentric mode\n"
"ERROR on non hollow shape.\n",
NAME_CURRENT_COMP);
exit(0);
}
strncpy(VarSqw.compname, NAME_CURRENT_COMP, 256);
VarSqw.T2E =(1/11.605); /* Kelvin to meV */
VarSqw.sqSE2K = (V2K*SE2V)*(V2K*SE2V);
VarSqw.sqw_threshold = (threshold > 0 ? threshold : 0);
VarSqw.s_abs = sigma_abs;
VarSqw.s_coh = sigma_coh;
VarSqw.s_inc = sigma_inc; /* s_scatt member initialized in Sqw_init */
VarSqw.maxloop = 100;
VarSqw.neutron_removed = 0;
VarSqw.neutron_enter = 0;
VarSqw.neutron_pmult = 0;
VarSqw.neutron_exit = 0;
VarSqw.w_max = (wmax > 0 ? wmax : FLT_MAX);
VarSqw.w_min = (wmin > 0 ? wmin : 0);
VarSqw.q_max = (qmax > 0 ? qmax : FLT_MAX);
VarSqw.q_min = (qmin > 0 ? qmin : 0);
VarSqw.rho = V_rho;
VarSqw.sqw_norm = norm_sqw;
VarSqw.sqw_auto = auto_qw;
/* PowderN compatibility members */
VarSqw.Dd = powder_Dd;
VarSqw.DWfactor = powder_DW;
VarSqw.Temperature= T;
for (i=0; i< 9; i++) VarSqw.column_order[i] = columns[i];
VarSqw.column_order[8] = (VarSqw.column_order[0] >= 0 ? 0 : 2);
if (!Sqw_init(&VarSqw, Sqw_coh, Sqw_inc)) {
printf("Isotropic_Sqw: %s: ERROR importing data files (Sqw_init)\n",NAME_CURRENT_COMP);
exit(0);
}
/* optional ways to define rho */
if (!VarSqw.rho && powder_Vc > 0)
VarSqw.rho = 1/powder_Vc;
if (VarSqw.rho <= 0) exit(printf("Isotropic_Sqw: %s: Null density (V_rho)\n",NAME_CURRENT_COMP));
/* 100: convert from barns to fm^2 */
VarSqw.my_a_v =(VarSqw.rho*100*VarSqw.s_abs*2200);
VarSqw.my_s =(VarSqw.rho*100*(VarSqw.s_coh+VarSqw.s_inc));
if ((VarSqw.s_coh || VarSqw.s_inc) && !VarSqw.Temperature
&& (VarSqw.Data_coh.intensity || VarSqw.Data_inc.intensity))
printf("Isotropic_Sqw: %s: Sample temperature is zero (T).\n"
"Warning Disabling detailed balance.\n",NAME_CURRENT_COMP);
if (!VarSqw.s_coh && !VarSqw.s_inc) {
printf("Isotropic_Sqw: %s: Scattering cross section is zero\n"
"ERROR (sigma_coh, sigma_inc)\n",NAME_CURRENT_COMP);
exit(0);
}
if (d_phi) d_phi = fabs(d_phi)*DEG2RAD;
if (d_phi > PI) d_phi = 0; /* V_scatt on 4*PI */
if (d_phi && order != 1) {
printf("Isotropic_Sqw: %s: Focusing can only apply for single\n"
" scattering. Setting to 4*PI mode.\n",
NAME_CURRENT_COMP);
d_phi = 0;
}
VarSqw.w_max = (wmax > 0 ? wmax : 0);
VarSqw.w_min = (wmin > 0 ? wmin : FLT_MAX);
VarSqw.q_max = (qmax > 0 ? qmax : 0);
VarSqw.q_min = (qmin > 0 ? qmin : FLT_MAX);
/* end INITIALIZE */
%}
/*****************************************************************************/
/*****************************************************************************/
TRACE
%{
int intersect; /* flag to continue/stop */
double t0, t1, t2, t3; /* times for intersections */
double dt0, dt1, dt2, dt; /* time intervals */
double k=0, kf, kf1, kf2;
double v=0, vf;
double d_path; /* total path length for straight trajectory */
double my_a; /* absorption cross-section scaled to velocity (2200) */
double ws, wi; /* probability for scattering/absorption and for */
/* interaction along d_path */
double tmp; /* temporary var */
double omega=0; /* energy transfert */
double q=0; /* wavevector transfert */
long index_w; /* energy index for table look-up DOS */
long index_q; /* Q index for table look-up P(Q|w) */
double c1, R,theta, costheta, sintheta; /* for the choice of kf direction */
double u1x,u1y,u1z;
double u2x,u2y,u2z;
double u0x,u0y,u0z;
int index_counter;
int flag=0;
int flag_concentric=0;
double my_t=0;
double p_mult=1;
double alpha, alpha0;
struct Sqw_Data_struct Data_sqw;
/* Store Initial neutron state */
VarSqw.ki_x = V2K*vx;
VarSqw.ki_y = V2K*vy;
VarSqw.ki_z = V2K*vz;
VarSqw.ti = t;
VarSqw.vi = 0;
VarSqw.ki = 0;
VarSqw.type = '\0';
do { /* Main interaction loop. Ends with intersect=0 */
/* Intersection neutron trajectory / sample (sample surface) */
if (VarSqw.shape==1)
intersect=box_intersect (&t0,&t3, x,y,z,vx,vy,vz, xwidth,yheight,zthick);
else if (VarSqw.shape==0)
intersect=cylinder_intersect(&t0,&t3, x,y,z,vx,vy,vz, radius_o,yheight);
else
intersect=sphere_intersect(&t0,&t3, x,y,z,vx,vy,vz, radius_o);
/* Computing the intermediate times */
if (intersect) {
tmp = 0;
if (radius_i>0) {
if ((VarSqw.shape==0 && cylinder_intersect(&t1,&t2, x,y,z,vx,vy,vz,
radius_i,yheight))
|| (VarSqw.shape==2 && sphere_intersect(&t1,&t2, x,y,z,vx,vy,vz,
radius_i)))
tmp = 1; /* we also take into account the hollow part */
} else if (thickness>0 && VarSqw.shape==1 && box_intersect(&t1,&t2, x,y,z,vx,vy,vz,
xwidth-2*thickness, yheight-2*thickness, zthick-2*thickness))
tmp = 1;
if (!tmp) t1 = t2 = t3; /* no empty space inside */
} else break; /* neutron does not hit sample: transmitted */
tmp = 0;
if (intersect) { /* the neutron hits the sample */
if (!v) {
v = vx*vx+vy*vy+vz*vz;
v = sqrt(v);
}
k = V2K*v;
if (!VarSqw.vi) VarSqw.vi = v;
if (!VarSqw.ki) VarSqw.ki = k;
if (t0 > 0) { /* we are before the sample */
PROP_DT(t0); /* propagates neutron to the entry of the sample */
} else if (t1 > 0 && t1 > t0) { /* we are inside first part of the sample */
/* no propagation, stay inside */
} else if (t2 > 0 && t2 > t1) { /* we are in the hole */
PROP_DT(t2); /* propagate to inner surface of 2nd part of sample */
} else if (t3 > 0 && t3 > t2) { /* we are in the 2nd part of sample */
/* no propagation, stay inside */
}
dt0=t1-(t0 > 0 ? t0 : 0); /* Time in first part of hollow/cylinder/box */
dt1=t2-(t1 > 0 ? t1 : 0); /* Time in hole */
dt2=t3-(t2 > 0 ? t2 : 0); /* Time in 2nd part of hollow cylinder */
if (dt0 < 0) dt0 = 0;
if (dt1 < 0) dt1 = 0;
if (dt2 < 0) dt2 = 0;
/* initialize concentric mode */
if (concentric && !flag_concentric && t0 >= 0
&& VarSqw.shape==0 && radius_i > 0) {
flag_concentric=1;
}
if (flag_concentric == 1) {
dt1=dt2=0; /* force exit when reaching hole/2nd part */
}
if (!dt0 && !dt2) {
intersect = 0; /* the sample was passed entirely */
break;
}
VarSqw.neutron_enter++;
if (!v) {
printf("Isotropic_Sqw: %s: ERROR: Null velocity\n",NAME_CURRENT_COMP);
VarSqw.neutron_removed++;
ABSORB; /* should never occur */
}
/* check for scattering event */
d_path = v*( dt0 +dt2 ); /* total path lenght in sample */
my_a = VarSqw.my_a_v / v; /* absorption 'mu' */
my_t = my_a + VarSqw.my_s; /* total scattering Xsect (tmp var) */
if (!my_t) {
printf("Isotropic_Sqw: %s: ERROR: Null Scattering cross section\n",NAME_CURRENT_COMP);
VarSqw.neutron_removed++;
ABSORB; /* should never occur */
}
/* Proba of scattering vs absorption */
ws = VarSqw.my_s/my_t; /* (inc+coh)/(inc+coh+abs) */
/* Proba of interacing along length d_path */
wi = 1 - exp(-my_t*d_path);
flag = 0; /* flag used for propagation to exit point before ending */
/* are we next to the exit ? probably no scattering */
if (VarSqw.my_s*d_path <= 4e-7) {
flag = 1;
p_mult *= 1 - wi; /* No interaction before the exit */
}
/* MC choice: Interaction or transmission ? */
if (!flag && wi > 0 && (wi == 1 || (tmp=rand01()) < wi)) { /* Interaction neutron/sample */
p_mult *= ws; /* Update weight ; account for absorption and scatter */
} else {
flag = 1; /* Transmission : no interaction neutron/sample */
if (!VarSqw.type) VarSqw.type = 't';
}
if (flag) { /* propagate to exit of sample and finish */
intersect = 0;
break;
}
} /* end if intersect the neutron hits the sample */
else break;
if (intersect) { /* Multiple scattering event */
/* Decaying exponential distribution of the path length before scattering */
/* Select a point at which to scatter the neutron, taking
secondary extinction into account. */
if (my_t*d_path < 1e-6)
/* For very weak scattering, use simple uniform sampling of scattering
point to avoid rounding errors. */
dt = rand0max(d_path); /* length */
else
dt = -log(1 - rand0max((1 - exp(-my_t*d_path)))) / my_t; /* length */
dt /= v; /* Time from present position to scattering point */
/* If t0 is in hole, propagate to next part of the hollow cylinder */
if (dt1 > 0 && dt0 > 0 && dt > dt0) dt += dt1;
/* Neutron propagation to the scattering point */
PROP_DT(dt);
/* choice between coherent/incoherent scattering */
if (!tmp) tmp = rand01();
else { /* re-use preceeding random shot */
tmp /= wi;
}
tmp *= (VarSqw.s_coh+VarSqw.s_inc);
flag=1;
if (VarSqw.s_inc && tmp < VarSqw.s_inc) {
/* CASE 1: incoherent case */
if (!VarSqw.Data_inc.intensity) {
/* CASE 1a: no incoherent Sqw from file, use isotropic V-like */
if (d_phi && order == 1) {
randvec_target_rect_angular(&u1x, &u1y, &u1z, &tmp,
vx, vy, vz, 2*PI, d_phi, ROT_A_CURRENT_COMP);
p_mult *= tmp/4*PI; /* weighted by focused range to total range */
} else
randvec_target_circle(&u1x, &u1y, &u1z, NULL, vx, vy, vz, 0);
vx = u1x; vy = u1y; vz = u1z;
vf = v; kf = k;
flag = 0;
if (!VarSqw.type) VarSqw.type = 'v';
SCATTER;
} else {
/* CASE 1b: incoherent Sqw from file */
if (VarSqw.Data_inc.intensity)
Data_sqw = VarSqw.Data_inc;
else flag=0;
if (!VarSqw.type) VarSqw.type = 'i';
}
} else if (VarSqw.s_coh && tmp > VarSqw.s_inc) {
if (VarSqw.Data_coh.intensity) {
/* CASE2: coherent case */
Data_sqw = VarSqw.Data_coh;
} else flag=0;
if (!VarSqw.type) VarSqw.type = 'c';
}
if (flag) { /* not true when V-like scattering */
/* give us a limited number of tries for scattering */
for (index_counter=VarSqw.maxloop; index_counter > 0 ; index_counter--) {
/* MC choice: energy transfer in the DOS */
index_w = Sqw_search_DOS(Data_sqw, rand01());
if (&(Data_sqw.DOS[index_w]) != NULL)
omega = Data_sqw.DOS[index_w].omega;
else omega=0;
/* MC choice: detailed balance: choice between w and -w */
if (omega && VarSqw.Temperature
&& rand01() > 1/(1+exp(-omega/(VarSqw.Temperature*VarSqw.T2E))) ) {
omega = -omega; /* anti Stokes */
} /* else Stokes */
/* MC choice: momentum transfer Q in P(Q|w) */
index_q = Sqw_search_Q_proba_per_w(Data_sqw, rand01(), index_w);
if (index_q == -1) {
if (VarSqw.verbose_output >= 3)
printf("Isotropic_Sqw: %s: Warning: No suitable P(q|w) transfert\n"
" for w=%g index_w=%i\n",
NAME_CURRENT_COMP, omega, index_w);
VarSqw.neutron_removed++;
continue; /* no Q value for this w choice */
}
if (&(Data_sqw.Q_proba_per_w[index_w]) != NULL)
q = Data_sqw.Q_proba_per_w[index_w][index_q].Q;
else {
if (VarSqw.verbose_output >= 3)
printf("Isotropic_Sqw: %s: Warning: No suitable q transfert\n"
" for w=%g\n",
NAME_CURRENT_COMP, omega);
VarSqw.neutron_removed++;
ABSORB;
}
/* Search for length of final wave vector kf */
/* kf is such that : hbar*w = hbar*hbar/2/m*(k*k - kf*kf) */
/* acceptable values for kf are kf1 and kf2 */
if (!Sqw_search_root(1, 0, -k*k + VarSqw.sqSE2K*omega, &kf1, &kf2)) {
if (VarSqw.verbose_output >= 3)
printf("Isotropic_Sqw: %s: Warning: imagimary root for\n"
" w=%g q=%g (triangle canm not close)\n",
NAME_CURRENT_COMP, omega, q);
VarSqw.neutron_removed++;
continue; /* all roots are imaginary */
}
/* MC choice: between kf1 and kf2 */
kf = fabs(rand01() < 0.5 ? kf1 : kf2);
vf = K2V*kf;
/* Search of the direction of kf such that : q = ki - kf */
/* ks = c1*ki/norm(ki)^2 + u0 */
/* u0 is in the plane orthogonal to ki */
c1= (k*k+kf*kf-q*q)/(2*kf); /* unit is [k] */
/* u1 and u2 : basis of the plane orthogonal to ki */
u1x=0; u1y=-vz; u1z= vy; /* in vertical plane */
u2x=vy*vy+vz*vz; u2y=-vx*vy; u2z=-vx*vz;
/* circle radius = intersection with the plane defined by u1 and u2 */
R = 1-c1*c1/(k*k); /* unit is [1] */
if (R >= 0) R = sqrt(R);
else {
continue; /* No solution for R */
}
NORM(u1x, u1y, u1z);
NORM(u2x, u2y, u2z);
p_mult *= kf/k/4/PI;
break; /* exit for loop on success */
} /* end for index_counter */
if (!index_counter) { /* for loop ended: failure for scattering */
intersect=0; /* Could not scatter: finish multiple scattering loop */
if (VarSqw.verbose_output >= 2)
printf("Isotropic_Sqw: %s: Warning: No scattering [q,w] conditions\n"
" last try (%i): type=%c w=%g q=%g R=%g\n",
NAME_CURRENT_COMP, VarSqw.maxloop, (VarSqw.type ? VarSqw.type : '-'), omega, q, R);
VarSqw.neutron_removed++;
if (order && SCATTERED != order) ABSORB;
break; /* finish multiple scattering loop */
}
/* Choose point on Debye-Scherrer cone */
if (order == 1 && d_phi)
{ /* relate height of detector to the height on DS cone */
tmp = sin(d_phi*DEG2RAD/2)/sin(2*theta);
/* If full Debye-Scherrer cone is within d_phi, don't focus */
if (tmp < -1 || tmp > 1) d_phi = 0;
/* Otherwise, determine alpha to rotate from scattering plane
into d_phi focusing area*/
else alpha = 2*asin(tmp);
} /* else d_phi=0 from INIT */
if (d_phi) { /* Focusing */
alpha = fabs(alpha);
/* Trick to get scattering for pos/neg theta's */
alpha0= 2*rand01()*alpha;
if (alpha0 > alpha) {
alpha0=PI+(alpha0-1.5*alpha);
} else {
alpha0=alpha0-0.5*alpha;
}
p_mult *= alpha/PI;
}
else
alpha0 = PI*randpm1();
costheta = cos(alpha0); sintheta = sin(alpha0);
/* u0 = component of ks which is in the plane orthogonal to ki */
u0x=R*(costheta*u1x + sintheta*u2x);
u0y=R*(costheta*u1y + sintheta*u2y);
u0z=R*(costheta*u1z + sintheta*u2z);
tmp = v*k;
u0x=c1*vx/tmp + u0x; /* normalized output velocity */
u0y=c1*vy/tmp + u0y;
u0z=c1*vz/tmp + u0z;
/* Final velocity */
vx = vf*u0x;
vy = vf*u0y;
vz = vf*u0z;
v = vf; k = kf;
/* nornalize to \int S or norm_sqw in selected [q,w] range */
p_mult *= (norm_sqw > 0 ? norm_sqw : Data_sqw.intensity);
if (p_mult > 1) VarSqw.neutron_pmult++;
p *= p_mult;
SCATTER;
if (fabs(omega) > VarSqw.w_max) VarSqw.w_max = fabs(omega);
else if (fabs(omega) < VarSqw.w_min) VarSqw.w_min = fabs(omega);
if (fabs(q) > VarSqw.q_max) VarSqw.q_max = fabs(q);
else if (fabs(q) < VarSqw.q_min) VarSqw.q_min = fabs(q);
if (VarSqw.neutron_exit > VarSqw.maxloop
&& VarSqw.neutron_removed && VarSqw.sqw_auto) {
if (VarSqw.verbose_output)
printf("Isotropic_Sqw: %s: %li events removed out of %li.\n"
"INFO: Optimizing q=[%g:%g] w=[%g:%g]\n",
NAME_CURRENT_COMP, VarSqw.neutron_removed, VarSqw.neutron_exit,
VarSqw.q_min, VarSqw.q_max, VarSqw.w_min, VarSqw.w_max);
VarSqw.Data_inc.q_min = VarSqw.Data_coh.q_min = VarSqw.q_min;
VarSqw.Data_inc.q_max = VarSqw.Data_coh.q_max = VarSqw.q_max;
VarSqw.Data_inc.w_min = VarSqw.Data_coh.w_min = VarSqw.w_min;
VarSqw.Data_inc.w_max = VarSqw.Data_coh.w_max = VarSqw.w_max;
if (!Sqw_init(&VarSqw, Sqw_coh, Sqw_inc)) {
printf("Isotropic_Sqw: %s: ERROR re-importing data files (Sqw_init from TRACE)\n",NAME_CURRENT_COMP);
exit(0);
}
VarSqw.sqw_auto = 0;
}
} /* end if (flag) */
/* test for a given multiple order */
if (order && SCATTERED >= order) {
intersect=0; /* reached required number of SCATTERing */
break; /* finish multiple scattering loop */
}
} /* end if (intersect) Multiple scattering event */
} while (intersect); /* end do (intersect) (multiple scattering loop) */
/* Store Final neutron state */
VarSqw.kf_x = V2K*vx;
VarSqw.kf_y = V2K*vy;
VarSqw.kf_z = V2K*vz;
VarSqw.tf = t;
VarSqw.vf = v;
VarSqw.kf = k;
if (SCATTERED) {
VarSqw.dq = sqrt((VarSqw.kf_x-VarSqw.ki_x)*(VarSqw.kf_x-VarSqw.ki_x)
+(VarSqw.kf_y-VarSqw.ki_y)*(VarSqw.kf_y-VarSqw.ki_y)
+(VarSqw.kf_z-VarSqw.ki_z)*(VarSqw.kf_z-VarSqw.ki_z));
VarSqw.dw = VS2E*(VarSqw.vf*VarSqw.vf - VarSqw.vi*VarSqw.vi);
VarSqw.neutron_exit++;
} else VarSqw.dq=VarSqw.dw=0;
/* end TRACE */
%}
FINALLY
%{
int k;
for (k=0; k < 2; k++) {
struct Sqw_Data_struct Data_sqw;
char tmp[1024];
char type_ci[64];
char ext[20];
char filename[256];
strcpy(type_ci, (k == 0 ? "coherent" : "incoherent"));
strcpy(ext, (k == 0 ? "coh" : "inc"));
Data_sqw = (k == 0 ? VarSqw.Data_coh : VarSqw.Data_inc);
if (save_sqw && Data_sqw.intensity) {
double DOS[Data_sqw.nb_w];
double SQW[Data_sqw.nb_q][Data_sqw.nb_w];
long i,j;
if (VarSqw.verbose_output >= 1)
printf("Isotropic_Sqw: %s: Writting %s S(q), DOS and S(q,w) files as monitors.\n", NAME_CURRENT_COMP, type_ci);
for (j=0; j < (Data_sqw.nb_w) ; j++) {
DOS[j] = Data_sqw.DOS[j].I;
for (i=0; i < (Data_sqw.nb_q) ; i++)
SQW[i][j] = Data_sqw.Q_proba_per_w[j][i].proba;
}
sprintf(tmp, "S(q) from %s (%s)", Data_sqw.filename, type_ci);
sprintf(filename, "%s_%s.sq", NAME_CURRENT_COMP, ext);
if (Data_sqw.nb_q > 1)
DETECTOR_OUT_1D(tmp,"Wavevector [Angs-1]","S(q)","q",
Data_sqw.q_min, Data_sqw.q_max,
Data_sqw.nb_q,NULL,Data_sqw.Sq,NULL,
filename);
sprintf(tmp, "Normalized DOS from %s (%s)", Data_sqw.filename, type_ci);
sprintf(filename, "%s_%s.dos", NAME_CURRENT_COMP, ext);
if (Data_sqw.nb_w > 1)
DETECTOR_OUT_1D(tmp,"Energy [meV]","g(w)","w",
Data_sqw.w_min, Data_sqw.w_max,
Data_sqw.nb_w,NULL,DOS,NULL,
filename);
sprintf(tmp, "S(q,w) from %s (%s), Norm=%g", Data_sqw.filename, type_ci,
(norm_sqw > 0 ? norm_sqw : Data_sqw.intensity));
sprintf(filename, "%s_%s.sqw", NAME_CURRENT_COMP, ext);
if (Data_sqw.nb_q > 1 && Data_sqw.nb_w > 1)
DETECTOR_OUT_2D(tmp,"Wavevector [Angs-1]","Energy [meV]",
Data_sqw.q_min, Data_sqw.q_max, Data_sqw.w_min, Data_sqw.w_max,
Data_sqw.nb_q,Data_sqw.nb_w,NULL,&SQW[0][0],NULL,
filename);
}
/* test if Sqw data is larger than used [q,w] range */
if (VarSqw.neutron_removed && Data_sqw.nb_q*Data_sqw.nb_w > 1)
if ((VarSqw.w_max < Data_sqw.w_max || VarSqw.w_min > Data_sqw.w_min)
|| (VarSqw.q_max < Data_sqw.q_max || VarSqw.q_min > Data_sqw.q_min))
printf("Isotropic_Sqw: %s: Data from %s file %s in the range\n"
" q=[%g:%g] w=[%g:%g]\n"
" is LARGER than accessible beam range.\n"
" This may result in neutron events removed.\n"
"WARNING Set qmin=%g, qmax=%g, wmin=%g, wmax=%g\n"
" to Focus simulation optimally.\n",
NAME_CURRENT_COMP, type_ci, Data_sqw.filename,
Data_sqw.q_min, Data_sqw.q_max, Data_sqw.w_min, Data_sqw.w_max,
VarSqw.q_min, VarSqw.q_max, VarSqw.w_min, VarSqw.w_max);
if (Data_sqw.DOS) free(Data_sqw.DOS);
if (Data_sqw.Q_proba_per_w) free(Data_sqw.Q_proba_per_w);
if (Data_sqw.DOS_lookup) free(Data_sqw.DOS_lookup);
if (Data_sqw.QW_lookup) free(Data_sqw.QW_lookup);
if (Data_sqw.Sq) free(Data_sqw.Sq);
}
if (VarSqw.neutron_removed)
printf("Isotropic_Sqw: %s: %li neutron events (out of %li) that should have\n"
" scattered were removed because scattering conditions\n"
"WARNING could not be satisfied after %i tries.\n",
NAME_CURRENT_COMP, VarSqw.neutron_removed, VarSqw.neutron_exit, VarSqw.maxloop);
if (VarSqw.neutron_pmult)
printf("Isotropic_Sqw: %s: %li neutron events (out of %li) reached\n"
" unrealistic weight. The S(q,w) norm is probably too high\n"
"WARNING could not be satisfied after %i tries.\n",
NAME_CURRENT_COMP, VarSqw.neutron_pmult, VarSqw.neutron_exit, VarSqw.maxloop);
/* end FINALLY */
%}
/*****************************************************************************/
/*****************************************************************************/
MCDISPLAY
%{
magnify("xyz");
if(VarSqw.shape==1)
{
double xmin = -0.5*xwidth;
double xmax = 0.5*xwidth;
double ymin = -0.5*yheight;
double ymax = 0.5*yheight;
double zmin = -0.5*zthick;
double zmax = 0.5*zthick;
multiline(5, xmin, ymin, zmin,
xmax, ymin, zmin,
xmax, ymax, zmin,
xmin, ymax, zmin,
xmin, ymin, zmin);
multiline(5, xmin, ymin, zmax,
xmax, ymin, zmax,
xmax, ymax, zmax,
xmin, ymax, zmax,
xmin, ymin, zmax);
line(xmin, ymin, zmin, xmin, ymin, zmax);
line(xmax, ymin, zmin, xmax, ymin, zmax);
line(xmin, ymax, zmin, xmin, ymax, zmax);
line(xmax, ymax, zmin, xmax, ymax, zmax);
if (thickness>0) {
xmin = -0.5*xwidth+thickness;
xmax = -xmin;
ymin = -0.5*yheight+thickness;
ymax = -ymin;
zmin = -0.5*zthick+thickness;
zmax = -zmin;
multiline(5, xmin, ymin, zmin,
xmax, ymin, zmin,
xmax, ymax, zmin,
xmin, ymax, zmin,
xmin, ymin, zmin);
multiline(5, xmin, ymin, zmax,
xmax, ymin, zmax,
xmax, ymax, zmax,
xmin, ymax, zmax,
xmin, ymin, zmax);
line(xmin, ymin, zmin, xmin, ymin, zmax);
line(xmax, ymin, zmin, xmax, ymin, zmax);
line(xmin, ymax, zmin, xmin, ymax, zmax);
line(xmax, ymax, zmin, xmax, ymax, zmax);
}
}
else if(VarSqw.shape==0)
{
circle("xz", 0, yheight/2.0, 0, radius_i);
circle("xz", 0, yheight/2.0, 0, radius_o);
circle("xz", 0, -yheight/2.0, 0, radius_i);
circle("xz", 0, -yheight/2.0, 0, radius_o);
line(-radius_i, -yheight/2.0, 0, -radius_i, +yheight/2.0, 0);
line(+radius_i, -yheight/2.0, 0, +radius_i, +yheight/2.0, 0);
line(0, -yheight/2.0, -radius_i, 0, +yheight/2.0, -radius_i);
line(0, -yheight/2.0, +radius_i, 0, +yheight/2.0, +radius_i);
line(-radius_o, -yheight/2.0, 0, -radius_o, +yheight/2.0, 0);
line(+radius_o, -yheight/2.0, 0, +radius_o, +yheight/2.0, 0);
line(0, -yheight/2.0, -radius_o, 0, +yheight/2.0, -radius_o);
line(0, -yheight/2.0, +radius_o, 0, +yheight/2.0, +radius_o);
} else {
circle("xy",0,0,0,radius_i);
circle("xz",0,0,0,radius_i);
circle("yz",0,0,0,radius_i);
circle("xy",0,0,0,radius_o);
circle("xz",0,0,0,radius_o);
circle("yz",0,0,0,radius_o);
}
/* end MCDISPLAY */
%}
/*****************************************************************************/
/*****************************************************************************/
END