|
| 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