1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
|
/*******************************************************************************
*
* McStas, neutron ray-tracing package
* Copyright (C) 1997-2008, All rights reserved
* Risoe National Laboratory, Roskilde, Denmark
* Institut Laue Langevin, Grenoble, France
*
* Component: StatisticalChopper
*
* %I
* Written by: C. Monzat/E. Farhi/S. Rozenkranz
* Date: Nov 10th, 2009
* Origin: ILL
*
* Statistical (correlation) Chopper
*
* %D
* This component is a statistical chopper based on a pseudo random sequence that the user
* can specify. Inspired from DiskChopper, it should be used with SatisticalChopper_Monitor component.
*
* Example: StatisticalChopper(nu=1487*60/255) use default sequence
*
* %P
* INPUT PARAMETERS:
*
* yheight: [m] Slit height (if = 0, equal to radius). Auto centering of beam at half height.
* radius: [m] Radius of the disc
* nu: [Hz] Frequency of the Chopper, omega=2*PI*nu
* (algebraic sign defines the direction of rotation)
* sequence: [str] Slit sequence around disk, with 0=closed, 1=open positions.
* NULL or "" will set a default sequence.
*
* Optional parameters:
* isfirst: [0/1] Set it to 1 for the first chopper position in a cw source
* (it then spreads the neutron time distribution)
* n_pulse: [1] Number of pulses (Only if isfirst)
* jitter: [s] Jitter in the time phase
* abs_out: [0/1] Absorb neutrons hitting outside of chopper radius?
* delay: [s] Time 'delay'.
* phase: [deg] Angular 'delay' (overrides delay)
* verbose: [1] Set to 1 to display Disk chopper configuration
*
* %L
* R. Von Jan and R. Scherm. The statistical chopper for neutron time-of-flight spectroscopy. Nuclear Instruments and Methods, 80 (1970) 69-76.
*
* %E
*******************************************************************************/
DEFINE COMPONENT StatisticalChopper
SETTING PARAMETERS (radius=0.5, yheight=0, nu, jitter=0, delay=0, isfirst=0, abs_out=1, phase=0, verbose=0, n_pulse=1,
string sequence="NULL")
/* Neutron parameters: (x,y,z,vx,vy,vz,t,sx,sy,sz,p) */
DECLARE
%{
double delta_y;
double height;
double omega;
int *Sequence; /* Array containing 0=closed and 1=opened*/
int *SequenceIndex; /* index of '1' in the sequence */
int nslit;
int m; /* sum of opened slits ('1' in sequence) */
%}
INITIALIZE
%{
Sequence=NULL;
SequenceIndex=NULL;
nslit=0;
m=0;
/* If slit height 'unset', assume full opening */
if (yheight == 0) {
height=radius;
} else {
height=yheight;
}
delta_y = radius-height/2; /* radius at beam center */
omega=2.0*PI*nu; /* rad/s */
if (!sequence) {
fprintf(stderr,"StatisticalChopper: %s: sequence is NULL. \n", NAME_CURRENT_COMP);
exit(-1);
}
if (strlen(sequence) <=2) {
fprintf(stderr,"StatisticalChopper: %s: Sequence is too short (length=%i). Use conventional DiskChopper instead.\n", NAME_CURRENT_COMP, nslit);
exit(-1);
}
if (!strlen(sequence) || !strcmp(sequence,"NULL"))
strcpy(sequence,
"100000010101001101110010010011110111111001011000011101001010111100101011111011010011011111000100011011100010000010001101101010100001110111001011111011001110001100011011011110101010000000110000011110011010100101011100000001001101000111100010110110001100100"
);
if (yheight && yheight>radius) {
fprintf(stderr,"StatisticalChopper: %s: yheight must be < radius\n", NAME_CURRENT_COMP);
exit(-1); }
if (isfirst && n_pulse <=0) {
fprintf(stderr,"StatisticalChopper: %s: wrong First chopper pulse number (n_pulse=%g)\n", NAME_CURRENT_COMP, n_pulse);
exit(-1); }
if (!omega) {
fprintf(stderr,"StatisticalChopper: %s WARNING: chopper frequency is 0!\n", NAME_CURRENT_COMP);
omega = 1e-15; /* We should actually use machine epsilon here... */
}
if (!abs_out) {
fprintf(stderr,"StatisticalChopper: %s WARNING: chopper will NOT absorb neutrons outside radius %g [m]\n", NAME_CURRENT_COMP, radius);
}
/* Calulate delay from phase and vice versa, 'direction' moderated by sign of omega */
delay *=omega/fabs(omega);
if (phase) {
if (delay) {
fprintf(stderr,"StatisticalChopper: %s WARNING: delay AND phase specified. Using phase setting\n", NAME_CURRENT_COMP);
}
phase*=DEG2RAD;
/* 'Delay' should always be a delay, taking rotation direction into account: */
delay=omega*phase/(omega*omega);
} else {
phase=delay*omega; /* rad */
}
if (verbose && nu) {
printf("StatisticalChopper: %s: frequency=%g [Hz] %g [rpm] time frame=%g [s] phase=%g [deg]\n",
NAME_CURRENT_COMP, nu, nu*60, fabs(1/nu), phase*RAD2DEG);
printf(" height=%g [m], slits centered at radius=%g [m]\n",
height, delta_y);
}
nslit=strlen(sequence);
if (nslit>1) {
int i=0,j=0;
double c=0;
int index=0;
Sequence = malloc(nslit * sizeof(int));
SequenceIndex = malloc(nslit * sizeof(int));
if (!Sequence){
fprintf(stderr,"StatisticalChopper: %s: Memory exhausted when allocating chopper sequence.\n", NAME_CURRENT_COMP);
exit(-1);
}
if (!SequenceIndex){
fprintf(stderr,"StatisticalChopper: %s: Memory exhausted when allocating chopper sequence table lookup.\n", NAME_CURRENT_COMP);
}
/* build sequence index (where sequence==1) */
for (i=0;i<nslit;SequenceIndex[i++]=0);
for (i=0;i<nslit;i++) {
Sequence[i]=(int)sequence[i]-'0';
if (Sequence[i]) {
m++; Sequence[i]=1;
if (SequenceIndex) SequenceIndex[index++] = i;
}
}
c = (double)(m-1)/(double)(nslit-1); /* duty cycle */
/* checking sequence */
for (j=0; j<=2*nslit; j++) {
int A = 0;
for (i=0; i<nslit; i++){
A += Sequence[i]*Sequence[(i+j) % nslit];
}
if (j==0 || j==nslit || j==2*nslit) {
if (A != m)
fprintf(stderr,"StatisticalChopper: %s: Sequence does not satisfy autocorrelation pseudorandom criteria\n"
"WARNING A_0=%i is different from m=%i\n",
NAME_CURRENT_COMP, A, m);
break;
} else {
if (A != m*(m-1)/(nslit-1))
fprintf(stderr,"StatisticalChopper: %s: Sequence does not satisfy autocorrelation pseudorandom\n"
"WARNING A=%i is different from m(m-1)/(N-1)=%i\n",
NAME_CURRENT_COMP, A, m*(m-1)/(nslit-1));
break;
}
} /* for j */
if (verbose && nu)
printf("StatisticalChopper: %s: Sequence length=%i with %i apertures; duty cycle c=%g \n",
NAME_CURRENT_COMP, nslit, m, c);
} /* if nslit */
else {
fprintf(stderr,"StatisticalChopper: %s: Sequence is too short (length=%i). Use conventional DiskChopper instead.\n", NAME_CURRENT_COMP, nslit);
exit(-1);
}
/* end INIT */
%}
TRACE
%{
double yprime;
PROP_Z0;
yprime = y+delta_y;
/* Is neutron outside the vertical slit range and should be absorbed ? */
if (abs_out && (x*x+yprime*yprime)>radius*radius) {
ABSORB;
}
/* Does neutron hit inner solid part of chopper in case of yheight!=radius ? */
if ((x*x+yprime*yprime)<(radius-height)*(radius-height)) {
ABSORB;
}
if (isfirst && SequenceIndex) {
/* choose a slit in the sequence and set time accordingly */
t = atan2(x,yprime)/omega + SequenceIndex[(int)(rand01()*m)]/nslit/nu
-delay +jitter*randnorm()+ (n_pulse > 1 ? floor(n_pulse*rand01()/fabs(nu)) : 0);
p *= (double)m/(double)nslit; /* transmission */
} else {
/* check if we pass through a slit */
int seq;
double angle = 2*PI*nu*(t-atan2(x,yprime)/omega+jitter*randnorm()) - phase;
seq=(int) floor(angle*nslit/(2*PI)) % nslit;
if ( seq < 0 ) seq += nslit;
if (Sequence[seq]) SCATTER;
else ABSORB;
}
%}
FINALLY
%{
free(Sequence); Sequence=NULL;
free(SequenceIndex); SequenceIndex=NULL;
%}
MCDISPLAY
%{
int i;
double radius_min=radius-height;
circle("xy", 0.0, -radius, 0.0, radius);
for (i=0; i<m; i++) {
/* draw a line at each slit center */
double angle=(double)SequenceIndex[i]*nslit/2*PI+phase;
line(radius_min*cos(angle), radius_min*sin(angle)-radius, 0,
radius*cos(angle), radius*sin(angle)-radius, 0);
}
%}
END
|