/*****************************************************************************
* McStas, neutron ray-tracing package
* Copyright 1997-2000 Risoe National Laboratory, Roskilde, Denmark
*
* Component: PowderN
*
* %I
* Written by: P. Willendrup, L. Chapon, K. Lefmann, A.B.Abrahamsen, N.B.Christensen, E.M.Lauridsen.
* Date: 4.2.98
* Version: $Revision: 1.24 $
* Origin: McStas release
* Modified by: KL, KN 20.03.98 (rewrite)
* Modified by: KL, 28.09.01 (two lines)
* Modified by: KL, 22.05.03 (background)
* Modified by: KL, PW 01.05.05 (N lines)
* Modified by: PW, LC 04.10.05 (Merge with Chapon Powder_multi)
*
* General powder sample (N lines, single scattering, incoherent scattering)
*
* %D
* General powder sample with
* many scattering vectors
* possibility for intrinsic line broadening
* incoherent backgorund ratio is specified by user.
* No multiple scattering. No secondary extinction.
*
* Based on Powder1/Powder2/Single_crystal.
* Geometry is a powder filled cylinder or a box.
* Incoherent scattering is only provided here to account for a background
* The efficient is highly improved when restricting the vertical scattering
* range on the Debye-Scherrer cone (with 'd_phi').
* The unit cell volume Vc may also be computed when giving the density,
* the atomic/molecular weight and the number of atoms per unit cell.
*
* Example: PowderN(reflections = "c60.lau", d_phi = 15 , radius = 0.01,
* yheight = 0.05, Vc = 1076.89, sigma_abs = 0, Delta_d=0, DW=1,
* format=Crystallographica)
*
* Powder definition and file format
*
* Powder structure is specified with an ascii data file 'reflections'.
* The powder data are free-text column based files.
* Lines begining by '#' are read as comments (ignored) but they may contain
* the following keywords (in the header):
* #Vc
* #sigma_abs
* #sigma_inc
* #Debye_Waller
* #Delta_d/d
* These values are not read if entered as component parameters (Vc=...)
*
* The signification of the columns in the numerical block may be
* set using the 'format' parameter. Built-in formats are:
* format=Crystallographica
* format=Fullprof
* format=Lazy
* and these specifications it is important NOT to use quotes, as shown.
*
* An other possibility to define other formats is to set directly
* the signification of the columns as a vector of indexes in the order
* format={j,d,F2,DW,Delta_d/d,1/2d,q,F}
* Signification of the symbols is given below. Indexes start at 1.
* Indexes with zero means that the column is not present, so that:
* Crystallographica={ 4,5,7,0,0,0,0,0 }
* Fullprof ={ 4,0,8,0,0,5,0,0 }
* Lazy ={17,6,0,0,0,0,0,13}
* Here again, NO quotes should be around the 'format' value.
*
* At last, the format may be overridden by direct definition of the
* column indexes in the file itself by using the following keywords
* in the header (e.g. '#column_j 4'):
* #column_j
* #column_d
* #column_F2
* #column_F
* #column_DW
* #column_Dd
* #column_inv2d
* #column_q
*
* %P
* INPUT PARAMETERS
*
* d_phi: Angle corresponding to the vertical angular range
* to focus to, e.g. detector height. 0 for no focusing [deg,0-180]
* radius: Radius of sample in (x,z) plane [m]
* yheight: Height of sample y direction [m]
* Vc: Volume of unit cell=nb atoms per cell/density of atoms [AA^3]
* sigma_abs:Absorption cross section per unit cell at 2200 m/s [fm^2]
* sigma_inc:Incoherent cross section per unit cell [fm^2]
* reflections: Input file for reflections.
* Use only incoherent scattering if NULL or "" [string]
* format: name of the format, or list of column indexes
(see Description). [no quotes]
*
* Optional parameters:
* xwidth: horiz. dimension of sample, as a width [m]
* zthick: thickness of sample [m]
* h: the same as yheight [m]
* Delta_d: global relative Delta_d/d spreading when the 'w' column
* is not available. Use 0 if ideal. [Angs]
* DW: global Debey-Waller factor when the 'DW' column
* is not available. Use 1 if included in F2 [1]
* frac: Fraction of incoherently scattered neutron rays [1]
* pack: Packing factor [1]
* weight: atomic/molecular weight of material [g/mol]
* density: density of material. V_rho=density/weight/1e24*N_A. [g/cm^3]
* nb_atoms: number of atoms per unit cell [1]
*
* %L
* See also: Powder1, Powder2 and PowderN
* %L
* See ICSD Inorganic Crystal Structure Database
* %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
* Fullprof powder refinement: http://www-llb.cea.fr/fullweb/fp2k/fp2k.htm
* %L
* Web Elements http://www.webelements.com/
*
* %E
*****************************************************************************/
DEFINE COMPONENT PowderN
DEFINITION PARAMETERS (reflections, format=Undefined)
SETTING PARAMETERS (d_phi=0, radius=0.01, yheight=0.05,
pack=1, Vc=0, sigma_abs=0, sigma_inc=0, Delta_d=0, frac=0,
xwidth=0, zthick=0, h=0, DW=0, nb_atoms=1, density=0, weight=0)
OUTPUT PARAMETERS (line_info, Nq, my_s_v2,
my_s_v2_sum, my_a_v, my_inc, q_v, w_v, isrect, columns)
STATE PARAMETERS (x,y,z,vx,vy,vz,t,s1,s2,p)
SHARE
%{
/* used for reading data table from file */
%include "read_table-lib"
/* Declare structures and functions only once in each instrument. */
#ifndef POWDERN_DECL
#define POWDERN_DECL
/* format definitions in the order {j d F2 DW Dd inv2d q F} */
#ifndef Crystallographica
#define Crystallographica { 4,5,7,0,0,0,0,0 }
#define Fullprof { 4,0,8,0,0,5,0,0 }
#define Lazy {17,6,0,0,0,0,0,0 }
#define Undefined { 0,0,0,0,0,0,0,0 }
#endif
struct line_data
{
double F2; /* Value of structure factor */
double q; /* Qvector */
int j; /* Multiplicity */
double DWfactor; /* Debye-Waller factor */
double w; /* Intrinsic line width */
};
struct line_info_struct
{
struct line_data *list; /* Reflection array */
int count; /* Number of reflections */
double Dd;
double DWfactor;
double V_0;
double rho;
double at_weight;
double at_nb;
double sigma_a;
double sigma_i;
char compname[256];
int column_order[8]; /* column signification */
};
int read_line_data(char *SC_file, struct line_info_struct *info)
{
struct line_data *list = NULL;
int size = 0;
t_Table sTable; /* sample data table structure from SC_file */
int i=0;
int mult_count =0;
char flag=0;
double q_count=0, j_count=0, F2_count=0;
char **parsing;
int list_count=0;
if (!SC_file || !strlen(SC_file)) {
printf("PowderN: %s: Using incoherent elastic scattering only\n",
info->compname);
return(0);
}
Table_Read(&sTable, SC_file, 1); /* read 1st block data from SC_file into sTable*/
/* parsing of header */
parsing = Table_ParseHeader(sTable.header,
"Vc","V_0",
"sigma_abs","sigma_a ",
"sigma_inc","sigma_i ",
"column_j",
"column_d",
"column_F2",
"column_DW",
"column_Dd",
"column_inv2d", "column_1/2d", "column_sintheta/lambda",
"column_q", /* 14 */
"DW", "Debye_Waller",
"Detla_d/d",
"column_F ",
"V_rho",
"density",
"weight",
"nb_atoms",
NULL);
if (parsing) {
if (parsing[0] && !info->V_0) info->V_0 =atof(parsing[0]);
if (parsing[1] && !info->V_0) info->V_0 =atof(parsing[1]);
if (parsing[2] && !info->sigma_a) info->sigma_a=atof(parsing[2]);
if (parsing[3] && !info->sigma_a) info->sigma_a=atof(parsing[3]);
if (parsing[4] && !info->sigma_i) info->sigma_i=atof(parsing[4]);
if (parsing[5] && !info->sigma_i) info->sigma_i=atof(parsing[5]);
if (parsing[6]) info->column_order[0]=atoi(parsing[6]);
if (parsing[7]) info->column_order[1]=atoi(parsing[7]);
if (parsing[8]) info->column_order[2]=atoi(parsing[8]);
if (parsing[9]) info->column_order[3]=atoi(parsing[9]);
if (parsing[10]) info->column_order[4]=atoi(parsing[10]);
if (parsing[11]) info->column_order[5]=atoi(parsing[11]);
if (parsing[12]) info->column_order[5]=atoi(parsing[12]);
if (parsing[13]) info->column_order[5]=atoi(parsing[13]);
if (parsing[14]) info->column_order[6]=atoi(parsing[14]);
if (parsing[15] && info->DWfactor<=0) info->DWfactor=atof(parsing[15]);
if (parsing[16] && info->DWfactor<=0) info->DWfactor=atof(parsing[16]);
if (parsing[17] && info->Dd <0) info->Dd =atof(parsing[17]);
if (parsing[18]) info->column_order[7]=atoi(parsing[18]);
if (parsing[19] && !info->V_0) info->V_0 =1/atof(parsing[19]);
if (parsing[20] && !info->rho) info->rho =atof(parsing[20]);
if (parsing[21] && !info->at_weight) info->at_weight =atof(parsing[21]);
if (parsing[22] && info->at_nb <= 1) info->at_nb =atof(parsing[22]);
for (i=0; i<=22; i++) if (parsing[i]) free(parsing[i]);
free(parsing);
}
if (!sTable.rows)
exit(fprintf(stderr, "PowderN: %s: Error: The number of rows in %s"
"should be at least %d\n", info->compname, SC_file, 1));
else size = sTable.rows;
Table_Info(sTable);
printf("PowderN: %s: Reading %d rows from %s\n",
info->compname, size, SC_file);
/* allocate line_data array */
list = (struct line_data*)malloc(size*sizeof(struct line_data));
for (i=0; iDd >= 0) w = info->Dd;
if (info->DWfactor > 0) DWfactor = info->DWfactor;
/* get data from table using columns {j d F2 DW Dd inv2d q F} */
/* column indexes start at 1, thus need to substract 1 */
if (info->column_order[0] >0)
j = Table_Index(sTable, i, info->column_order[0]-1);
if (info->column_order[1] >0)
d = Table_Index(sTable, i, info->column_order[1]-1);
if (info->column_order[2] >0)
F2 = Table_Index(sTable, i, info->column_order[2]-1);
if (info->column_order[3] >0)
DWfactor = Table_Index(sTable, i, info->column_order[3]-1);
if (info->column_order[4] >0)
w = Table_Index(sTable, i, info->column_order[4]-1);
if (info->column_order[5] >0)
{ d = Table_Index(sTable, i, info->column_order[5]-1);
d = (d > 0? 1/d/2 : 0); }
if (info->column_order[6] >0)
{ q = Table_Index(sTable, i, info->column_order[6]-1);
d = (q > 0 ? 2*PI/q : 0); }
if (info->column_order[7] >0 && !F2)
{ F2 = Table_Index(sTable, i, info->column_order[7]-1); F2 *= F2; }
/* assign and check values */
j = (j > 0 ? j : 0);
q = (d > 0 ? 2*PI/d : 0); /* this is q */
DWfactor = (DWfactor > 0 ? DWfactor : 1);
w = (w>0 ? w : 0); /* this is q and d relative spreading */
F2 = (F2 >= 0 ? F2 : 0);
if (j == 0 || q == 0) {
printf("PowderN: %s: line %i has invalid definition\n"
" (mult=0 or q=0 or d=0)\n", info->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;
/* 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("PowderN: %s: Set multiplicity to 1 for lines [%i:%i]\n"
" (d-spacing %g is duplicated %i times)\n",
info->compname, list_count-mult_count, list_count-1, list[list_count-1].q, mult_count);
for (index=list_count-mult_count; indexcompname, SC_file, list_count);
Table_Free(&sTable);
info->list = list;
info->count = list_count;
return(list_count);
}
#endif /* !POWDERN_DECL */
%}
DECLARE
%{
struct line_info_struct line_info;
int Nq=0;
double my_s_v2_sum;
double my_a_v, my_inc;
double *w_v,*q_v, *my_s_v2;
char isrect=0;
int columns[8] = format;
%}
INITIALIZE
%{
int i;
struct line_data *L;
line_info.Dd = Delta_d;
line_info.DWfactor = DW;
line_info.V_0 = Vc;
line_info.rho = density;
line_info.at_weight= weight;
line_info.at_nb = nb_atoms;
line_info.sigma_a = sigma_abs;
line_info.sigma_i = sigma_inc;
for (i=0; i< 8; i++) line_info.column_order[i] = columns[i];
strncpy(line_info.compname, NAME_CURRENT_COMP, 256);
if (!radius || !yheight) {
if (!xwidth || !yheight || !zthick) exit(fprintf(stderr,"PowderN: %s: sample has no volume (zero dimensions)\n", NAME_CURRENT_COMP));
else isrect=1; }
i = read_line_data(reflections, &line_info);
if (!line_info.V_0 && line_info.at_nb > 0
&& line_info.at_weight > 0 && line_info.rho > 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 */
line_info.V_0 = line_info.at_nb
/(line_info.rho/line_info.at_weight/1e24*6.02214199e23);
}
if (line_info.V_0 <= 0)
exit(fprintf(stderr,"PowderN: %s: density/unit cell volume is NULL (Vc)\n", NAME_CURRENT_COMP));
if (i) {
L = line_info.list;
Nq = line_info.count;
q_v = malloc(Nq*sizeof(double));
w_v = malloc(Nq*sizeof(double));
my_s_v2 = malloc(Nq*sizeof(double));
if (!q_v || !w_v || !my_s_v2)
exit(fprintf(stderr,"PowderN: %s: ERROR allocating memory (init)\n", NAME_CURRENT_COMP));
for(i=0; i0)
{
if(t0 < 0) t0=0; /* already in sample */
/* Neutron enters at t=t0. */
v = sqrt(vx*vx + vy*vy + vz*vz);
l_full = v * (t1 - t0); /* Length of full path through sample */
dt = rand01()*(t1 - t0); /* Time of scattering */
PROP_DT(dt+t0); /* Point of scattering */
l = v*dt; /* Penetration in sample */
my_s = my_s_v2_sum/(v*v)+my_inc;
/* Total attenuation from scattering */
if (frac <= 0 || (frac < 1 && rand01() >= frac))
{ /* Make coherent scattering event */
/* choose line */
if (Nq > 1) line=floor(Nq*rand01()); /* Select between Nq powder lines */
else line = 0;
if (w_v[line])
arg = q_v[line]*(1+w_v[line]*randnorm())/(2.0*v);
else
arg = q_v[line]/(2.0*v);
my_s_n = my_s_v2[line]/(v*v);
if(fabs(arg) > 1)
ABSORB; /* No bragg scattering possible*/
theta = asin(arg); /* Bragg scattering law */
/* Choose point on Debye-Scherrer cone */
if (d_phi)
{ /* relate height of detector to the height on DS cone */
arg = sin(d_phi*DEG2RAD/2)/sin(2*theta);
/* If full Debye-Scherrer cone is within d_phi, don't focus */
if (arg < -1 || arg > 1) d_phi = 0;
/* Otherwise, determine alpha to rotate from scattering plane
into d_phi focusing area*/
else alpha = 2*asin(arg);
}
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;
}
}
else
alpha0 = PI*randpm1();
/* now find a nearly vertical rotation axis:
* (v along Z) x (X axis) -> nearly Y axis
*/
vec_prod(tmp_vx,tmp_vy,tmp_vz, vx,vy,vz, 1,0,0);
/* handle case where v and aim are parallel */
if (!tmp_vx && !tmp_vy && !tmp_vz) { tmp_vx=tmp_vz=0; tmp_vy=1; }
/* v_out = rotate 'v' by 2*theta around tmp_v: Bragg angle */
rotate(vout_x,vout_y,vout_z, vx,vy,vz, 2*theta, tmp_vx,tmp_vy,tmp_vz);
/* tmp_v = rotate v_out by alpha0 around 'v' (Debye-Scherrer cone) */
rotate(tmp_vx,tmp_vy,tmp_vz, vout_x,vout_y,vout_z, alpha0, vx, vy, vz);
vx = tmp_vx;
vy = tmp_vy;
vz = tmp_vz;
flag=0;
if (isrect && !box_intersect(&t0, &t1, x, y, z, vx, vy, vz, xwidth, yheight, zthick)) flag=1;
else if(!isrect && !cylinder_intersect(&t0, &t1, x, y, z,
vx, vx, vx, radius, yheight)) flag=1;
if (flag) {
/* Strange error: did not hit cylinder */
fprintf(stderr, "PowderN: FATAL ERROR: Did not hit sample from inside.\n");
ABSORB;
}
l_1 = v*t1; /* go to exit */
pmul = Nq*l_full*my_s_n*exp(-(my_a_v/v+my_s)*(l+l_1))/(1-frac);
/* Correction in case of d_phi focusing - BUT only when d_phi != 0 */
if (d_phi) pmul *= alpha/PI;
} /* Coherent scattering event */
else
{ /* Make incoherent scattering event */
v = sqrt(vx*vx+vy*vy+vz*vz);
if(d_phi) {
randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle,
0, 0, 1,
2*PI, d_phi*DEG2RAD, ROT_A_CURRENT_COMP);
} else {
randvec_target_circle(&vx, &vy, &vz,
&solid_angle, 0, 0, 1, 0);
}
v1 = sqrt(vx*vx+vy*vy+vz*vz);
vx *= v/v1;
vy *= v/v1;
vz *= v/v1;
flag=0;
if (isrect && !box_intersect(&t0, &t1, x, y, z, vx, vy, vz, xwidth, yheight, zthick)) flag=1;
else if(!isrect && !cylinder_intersect(&t0, &t1, x, y, z,
vx, vx, vx, radius, yheight)) flag=1;
if (flag) {
/* Strange error: did not hit cylinder */
fprintf(stderr, "PowderN: FATAL ERROR: Did not hit sample from inside.\n");
ABSORB;
}
l_1 = v*t1;
pmul = l_full*my_inc*exp(-(my_a_v/v+my_s)*(l+l_1))/(frac);
pmul *= solid_angle/(4*PI);
} /* Incoherent scattering event */
p *= pmul;
SCATTER;
} /* else transmit non interacting neutrons */
%}
MCDISPLAY
%{
double h;
h=yheight;
magnify("xyz");
if (!isrect) {
circle("xz", 0, h/2.0, 0, radius);
circle("xz", 0, -h/2.0, 0, radius);
line(-radius, -h/2.0, 0, -radius, +h/2.0, 0);
line(+radius, -h/2.0, 0, +radius, +h/2.0, 0);
line(0, -h/2.0, -radius, 0, +h/2.0, -radius);
line(0, -h/2.0, +radius, 0, +h/2.0, +radius);
} else {
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);
}
%}
END