Skip to content

Commit 3ff6d88

Browse files
committed
Add TOFtrain version of MCPL instr
1 parent 3f21be1 commit 3ff6d88

34 files changed

+51680
-0
lines changed
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*******************************************************************************
2+
*
3+
* McStas, neutron ray-tracing package
4+
* Copyright (C) 1997-2008, All rights reserved
5+
* Risoe National Laboratory, Roskilde, Denmark
6+
* Institut Laue Langevin, Grenoble, France
7+
*
8+
* Component: DiskChopper
9+
*
10+
* %I
11+
* Written by: Peter Willendrup
12+
* Date: March 9 2006
13+
* Origin: Risoe
14+
* Based on Chopper (Philipp Bernhardt), Jitter and beamstop from work by
15+
* Kaspar Hewitt Klenoe (jan 2006), adjustments by Rob Bewey (march 2006)
16+
*
17+
* %D
18+
* Models a disc chopper with nslit identical slits, which are symmetrically distributed
19+
* on the disc. At time t=0, the centre of the first slit opening will be situated at the
20+
* vertical axis when phase=0, assuming the chopper centre of rotation is placed <b>BELOW</b> the beam axis.
21+
* If you want to place the chopper <b>ABOVE</b> the beam axis, please use a 180 degree rotation around Z
22+
* (otherwise unexpected beam splitting can occur in combination with the isfirst=1 setting, see
23+
* <a href="https://github.com/mccode-dev/McCode/issues/650">related bug on GitHub</a>)
24+
*
25+
* For more complicated gemometries, see component manual example of DiskChopper GROUPing.
26+
*
27+
* If the chopper is the 1st chopper of a continuous source instrument, you should use the "isfirst" parameter.
28+
* This parameter SETS the neutron time to match the passage of the chooper slit(s), taking into account the
29+
* chopper timing and phasing (thus conserving your simulated statistics).
30+
*
31+
* The isfirst parameter is ONLY relevant for use in continuous source settings.
32+
*
33+
* Example: DiskChopper(radius=0.2, theta_0=10, nu=41.7, nslit=3, delay=0, isfirst=1) First chopper
34+
* DiskChopper(radius=0.2, theta_0=10, nu=41.7, nslit=3, delay=0, isfirst=0)
35+
*
36+
* NOTA BENE wrt. GROUPing and isfirst:
37+
* When setting up a GROUP of DiskChoppers for a steady-state / reactor source, you will need
38+
* to set up
39+
* 1) An initial chopper with isfirst=1, NOT part of the GROUP - and using a "big" chopper opening
40+
* that spans the full angular extent of the openings of the subsequent GROUP
41+
* 2) Add your DiskChopper GROUP setting isfirst=0
42+
*
43+
* %P
44+
* INPUT PARAMETERS:
45+
*
46+
* theta_0: [deg] Angular width of the slits.
47+
* yheight: [m] Slit height (if = 0, equal to radius). Auto centering of beam at half height.
48+
* radius: [m] Radius of the disc
49+
* nu: [Hz] Frequency of the Chopper, omega=2*PI*nu (algebraic sign defines the direction of rotation)
50+
* nslit: [1] Number of slits, regularly arranged around the disk
51+
*
52+
* Optional parameters:
53+
* isfirst: [0/1] Set it to 1 for the first chopper position in a cw source (it then spreads the neutron time distribution)
54+
* n_pulse: [1] Number of pulses (Only if isfirst)
55+
* jitter: [s] Jitter in the time phase
56+
* abs_out: [0/1] Absorb neutrons hitting outside of chopper radius?
57+
* delay: [s] Time 'delay'
58+
* phase: [deg] Angular 'delay' (overrides delay)
59+
* xwidth: [m] Horizontal slit width opening at beam center
60+
* verbose: [1] Set to 1 to display Disk chopper configuration
61+
*
62+
* %E
63+
*******************************************************************************/
64+
65+
DEFINE COMPONENT DiskChopper
66+
67+
68+
69+
SETTING PARAMETERS (theta_0=0, radius=0.5, yheight, nu, nslit=3, jitter=0, delay=0, isfirst=0, n_pulse=1, abs_out=1, phase=0, xwidth=0, verbose=0)
70+
71+
72+
/* Neutron parameters: (x,y,z,vx,vy,vz,t,sx,sy,sz,p) */
73+
74+
DECLARE
75+
%{
76+
double Tg;
77+
double To;
78+
double delta_y;
79+
double height;
80+
double omega;
81+
%}
82+
83+
INITIALIZE
84+
%{
85+
/* If slit height 'unset', assume full opening */
86+
if (yheight == 0) {
87+
height = radius;
88+
} else {
89+
height = yheight;
90+
}
91+
delta_y = radius - height / 2; /* radius at beam center */
92+
omega = 2.0 * PI * nu; /* rad/s */
93+
if (xwidth && !theta_0 && radius)
94+
theta_0 = 2 * RAD2DEG * asin (xwidth / 2 / delta_y);
95+
96+
if (nslit <= 0 || theta_0 <= 0 || radius <= 0) {
97+
fprintf (stderr, "DiskChopper: %s: nslit, theta_0 and radius must be > 0\n", NAME_CURRENT_COMP);
98+
exit (-1);
99+
}
100+
if (nslit * theta_0 >= 360) {
101+
fprintf (stderr, "DiskChopper: %s: nslit * theta_0 exceeds 2PI\n", NAME_CURRENT_COMP);
102+
exit (-1);
103+
}
104+
if (yheight && yheight > radius) {
105+
fprintf (stderr, "DiskChopper: %s: yheight must be < radius\n", NAME_CURRENT_COMP);
106+
exit (-1);
107+
}
108+
if (isfirst && n_pulse <= 0) {
109+
fprintf (stderr, "DiskChopper: %s: wrong First chopper pulse number (n_pulse=%g)\n", NAME_CURRENT_COMP, n_pulse);
110+
exit (-1);
111+
}
112+
if (!omega) {
113+
fprintf (stderr, "DiskChopper: %s WARNING: chopper frequency is 0!\n", NAME_CURRENT_COMP);
114+
omega = 1e-15; /* We should actually use machine epsilon here... */
115+
}
116+
if (!abs_out) {
117+
fprintf (stderr, "DiskChopper: %s WARNING: chopper will NOT absorb neutrons outside radius %g [m]\n", NAME_CURRENT_COMP, radius);
118+
}
119+
120+
theta_0 *= DEG2RAD;
121+
122+
/* Calulate delay from phase and vice versa */
123+
if (phase) {
124+
if (delay) {
125+
fprintf (stderr, "DiskChopper: %s WARNING: delay AND phase specified. Using phase setting\n", NAME_CURRENT_COMP);
126+
}
127+
phase *= DEG2RAD;
128+
/* 'Delay' should always be a delay, taking rotation direction into account: */
129+
delay = phase / fabs (omega);
130+
} else {
131+
phase = delay * omega; /* rad */
132+
}
133+
134+
/* Time from opening of slit to next opening of slit */
135+
Tg = 2.0 * PI / fabs (omega) / nslit;
136+
137+
/* How long can neutrons pass the Chopper at a single point */
138+
To = theta_0 / fabs (omega);
139+
140+
if (!xwidth)
141+
xwidth = 2 * delta_y * sin (theta_0 / 2);
142+
143+
if (verbose && nu) {
144+
printf ("DiskChopper: %s: frequency=%g [Hz] %g [rpm], time frame=%g [s] phase=%g [deg]\n", NAME_CURRENT_COMP, nu, nu * 60, Tg, phase * RAD2DEG);
145+
printf (" %g slits, angle=%g [deg] height=%g [m], width=%g [m] at radius=%g [m]\n", nslit, theta_0 * RAD2DEG, height, xwidth, delta_y);
146+
}
147+
%}
148+
149+
TRACE
150+
%{
151+
double toff;
152+
double yprime;
153+
PROP_Z0;
154+
yprime = y + delta_y;
155+
156+
/* Is neutron outside the vertical slit range and should we absorb? */
157+
if (abs_out && (x * x + yprime * yprime) > radius * radius) {
158+
ABSORB;
159+
}
160+
/* Does neutron hit inner solid part of chopper in case of yheight!=radius? */
161+
if ((x * x + yprime * yprime) < (radius - height) * (radius - height)) {
162+
ABSORB;
163+
}
164+
165+
if (isfirst) {
166+
/* all events are put in the transmitted time frame */
167+
t = atan2 (x, yprime) / omega + To * randpm1 () / 2.0 + delay + (jitter ? jitter * randnorm () : 0) + (n_pulse > 1 ? floor (n_pulse * rand01 ()) * Tg : 0);
168+
/* correction: chopper slits transmission opening/full disk */
169+
p *= nslit * theta_0 / 2.0 / PI;
170+
} else {
171+
172+
// Check whether each t_offset carried by the ray make it through
173+
double weight_update = p/_particle->p_last_time_manipulation;
174+
_particle->p_last_time_manipulation = 0;
175+
176+
int train_index;
177+
int one_did_hit = 0;
178+
double this_train_t;
179+
int all_dead = 1;
180+
181+
for (train_index=0; train_index<adaptive_N; train_index++) {
182+
183+
if (_particle->p_trains[train_index] == 0) continue;
184+
all_dead = 0;
185+
186+
this_train_t = t + _particle->t_offset[train_index];
187+
toff = fabs (this_train_t - atan2 (x, yprime) / omega - delay - (jitter ? jitter * randnorm () : 0));
188+
189+
/* does neutron hit outside slit? */
190+
if (fmod (toff + To / 2.0, Tg) > To)
191+
_particle->p_trains[train_index] = 0; // T_ABSORB
192+
else {
193+
// T_TRANSMIT
194+
one_did_hit = 1;
195+
_particle->p_trains[train_index] *= weight_update;
196+
_particle->p_last_time_manipulation += _particle->p_trains[train_index];
197+
}
198+
199+
}
200+
if (!one_did_hit || all_dead) ABSORB;
201+
202+
p = _particle->p_last_time_manipulation;
203+
204+
}
205+
SCATTER;
206+
%}
207+
208+
MCDISPLAY
209+
%{
210+
211+
int j;
212+
/* Arrays for storing geometry of slit/beamstop */
213+
214+
circle ("xy", 0, -delta_y, 0, radius);
215+
216+
/* Drawing the slit(s) */
217+
for (j = 0; j < nslit; j++) {
218+
/* Angular start/end of slit */
219+
double tmin = j * (2.0 * PI / nslit) - theta_0 / 2.0 + phase;
220+
double tmax = tmin + theta_0;
221+
/* Draw lines for each slit. */
222+
223+
line (radius * sin (tmin), radius * cos (tmin) - delta_y, 0, (radius - height) * sin (tmin), (radius - height) * cos (tmin) - delta_y, 0);
224+
line ((radius - height) * sin (tmin), (radius - height) * cos (tmin) - delta_y, 0, (radius - height) * sin (tmax), (radius - height) * cos (tmax) - delta_y,
225+
0);
226+
line ((radius - height) * sin (tmax), (radius - height) * cos (tmax) - delta_y, 0, radius * sin (tmax), radius * cos (tmax) - delta_y, 0);
227+
}
228+
%}
229+
230+
END

0 commit comments

Comments
 (0)