/******************************************************************************* * * McStas, neutron ray-tracing package * Copyright 1997-2002, All rights reserved * Risoe National Laboratory, Roskilde, Denmark * Institut Laue Langevin, Grenoble, France * * Component: FermiChopper. * * %Identification * * Written by: M. Poehlmann, C. Carbogno, H. Schober * Date: May 2002 * Origin: ILL Grenoble / TU Muenchen * Version: $Revision: 1.9 $ * Release: McStas 1.6 * Modified by: K.Lieutenant, June 2005: added phase parameter. Comp validation. * * Fermi Chopper with rotating frame. * * %D * Models a fermi chopper with optional supermirror coated blades * supermirror facilities may be disabled by setting m = 0, R0=0 * Slit packages are straight. Chopper slits are separated by an infinitely * thin absorbing material. The effective transmission (resulting from fraction * of the transparent material and its transmission) may be specified. * The chopper slit package width may be specified through the total width 'width' * of the full package or the width 'w' of each single slit. The other parameter * is calculated by: width = Nslit*w. * The chopper needs a default velocity to calculate the initial value for an * iterative calculation of TOF values. This can be specified by its velocity, * energy, wavelength or wavevector. * * Example: * FermiChopper(phase=-50.0, radius=0.04, nu=100, * ymin=-0.04, ymax=0.04, w=0.00022475, Nslit=200.0, R0=0.0, * Qc=0.02176, alpha=2.33, m=0.0, length=0.012, eff=0.95) * * Markus Poehlmann * Christian Carbogno * and Helmut Schober * * %VALIDATION * Apr 2005: extensive external test, most problems solved (cf. 'Bugs') * Validated by: K. Lieutenant * * limitations: * no blade width used * * %BUGS * - overestimates peak width for long wavelengths * - may not give the right pulse position, shape and width for slit widths below 0.1 mm * * %Parameters * INPUT PARAMETERS: * * Geometrical chopper constants: * radius: (m) chopper cylinder radius * ymin: (m) lower y bound of cylinder * ymax: (m) upper y bound of cylinder * Nslit: (1) number of chopper slits * length: (m) channel length of the Fermi chopper * w: (m) width of one chopper slit * width: (m) optional total width of slit package * nu: (Hz) chopper frequency * eff: (1) efficiency = transmission x fraction of transparent material * verbose: (1) set to 1 or 2 gives debugging information * curvature:(m-1) Curvature of slits (1/radius of curvature). * * Supermirror constants: * m: (1) m-value of material. Zero means completely absorbing. * alpha: (AA) slope of reflectivity * Qc: (AA-1) critical scattering vector * W: (AA-1) width of supermirror cut-off * R0: (1) low-angle reflectivity * * Compatibility parameters: * rad, slit, alpham,Wi,dist,Vi * * Constants to reset time of flight: * zero_time: (1) set time to zero: 0: no 1: once per half cycle * phase: (deg) chopper phase at t=0 * * OUTPUT PARAMETERS: * FCVars : (-) structure * * %L * Vitess_ChopperFermi<\a> component by * G. Zsigmond, imported from Vitess by K. Lieutenant. * * %End *****************************************************************************/ DEFINE COMPONENT FermiChopper DEFINITION PARAMETERS () SETTING PARAMETERS (phase=0, radius=0.04, nu=100, ymin=-0.04, ymax=0.04, w=0.00022475, Nslit=200, R0=0.0, Qc=0.02176, alpha=2.33, m=0.0, W=2e-3, length=0.012, eff=0.95, zero_time=0, width=0, verbose=0, rad=0,slit=0,alpham=0,Wi=0,dist=0,Vi=0,curvature=0) OUTPUT PARAMETERS(FCVars) STATE PARAMETERS (x, y, z, vx, vy, vz, t, s1, s2, p) SHARE %{ #ifndef FermiChopper_TimeAccuracy #define FermiChopper_TimeAccuracy 1e-9 #define FermiChopper_MAXITER 60 /* Definition of internal variable structure: all counters */ struct FermiChopper_struct { double omega, ph0; /* chopper rotation */ double C_slit; /* C_slit radius in [m] */ double L_slit; double sum_t; double sum_v; double sum_N; double sum_N_pass; }; /***************************************************************************** * FC_zrot: returns Z' in rotating frame, from X,Z and t,omega,ph0 ****************************************************************************/ double FC_zrot(double X, double Z, double T, struct FermiChopper_struct FCs){ double omega=FCs.omega; double ph0 =FCs.ph0; return( Z*cos(omega*T+ph0)-X*sin(omega*T+ph0) ); } /***************************************************************************** * FC_xrot: returns X' in rotating frame, from X,Z and omega,t,ph0 * additional coordinate shift in case of curved slits ****************************************************************************/ double FC_xrot(double X, double Z, double T, struct FermiChopper_struct FCs){ double omega=FCs.omega; double ph0 =FCs.ph0; double C_slit=FCs.C_slit; double ret, tmp; ret = X*cos(omega*T+ph0)+Z*sin(omega*T+ph0); if (C_slit) { tmp = fabs(FC_zrot(X, Z, T, FCs)); if (tmp < FCs.L_slit/2) { tmp = (FCs.L_slit/2 - tmp)*C_slit; ret += (1-sqrt(1-tmp*tmp))/C_slit; } } return( ret ); } /***************************************************************************** * FC_xzrot_dt(x,z,vx,vz, t,dt, type='x' or 'z', FCs) * returns X' or Z' in rotating frame, from X,Z and t,omega,ph0 * taking into account propagation with velocity during time dt ****************************************************************************/ double FC_xzrot_dt(double x, double z, double vx, double vz, double t, double dt, char type, struct FermiChopper_struct FCs) { if (dt) /* with propagation */ return( (type == 'x' ? FC_xrot(x+vx*dt, z+vz*dt, t+dt, FCs) : FC_zrot(x+vx*dt, z+vz*dt, t+dt, FCs)) ); else /* without propagation */ return( (type == 'x' ? FC_xrot(x,z,t,FCs) : FC_zrot(x,z,t,FCs)) ); } /***************************************************************************** * FC_xzridder(x,z,vx,vz, omega,t,ph0, dt, type='x' or 'z', d) * solves X'=d and Z'=d with Ridder's method in time interval [0, dt]. * Returns time within [0,dt], from NumRecip in C, chap 9, p358 * accuracy of time is 1e-9, 60 iterations max. * ERRORS: return -1 should never occur * -2 if exceed MAX iteration * -3 no sign change in range * As good as dichotomy/Picard method, but faster. Secant is often wrong. ****************************************************************************/ #ifndef SIGN #define SIGN(a,b) ((b >= 0 ? fabs(a) : -fabs(a))) #endif double FC_xzridder(double x, double z, double vx, double vz, double t, double dt, char type, double d, struct FermiChopper_struct FCs) { int j; float fl, fh, fm, fnew; float dth, dtl, dtm, dtnew, ans; float s, dtacc=FermiChopper_TimeAccuracy; dtl=0; dth=dt; fl = FC_xzrot_dt(x,z,vx,vz, t,dtl, type, FCs) - d; fh = FC_xzrot_dt(x,z,vx,vz, t,dth, type, FCs) - d; if ((fl > 0 && fh < 0) || (fl < 0 && fh > 0)) { /* sign change */ ans=-FLT_MAX; for (j=1; j<=FermiChopper_MAXITER; j++) { dtm =0.5*(dtl+dth); /* dichotomy/bisection */ fm =FC_xzrot_dt(x,z,vx,vz, t,dtm, type, FCs) - d; s =sqrt(fm*fm-fl*fh); if (!s) return(ans); dtnew=dtm+(dtm-dtl)*((fl >= fh ? fm:-fm)/s); /* Ridder's formula */ if (fabs(dtnew-ans) <= dtacc) return(ans); ans =dtnew; fnew =FC_xzrot_dt(x,z,vx,vz, t,dtnew, type, FCs) - d; if (!fnew) return(ans); /* determine next time range */ if (SIGN(fm, fnew) != fm) { dtl=dtm; fl=fm; dth=ans; fh=fnew; } else if (SIGN(fl, fnew) != fl) { dth=ans; fh=fnew; } else if (SIGN(fh, fnew) != fh) { dtl=ans; fl=fnew; } else return(-1); /* never get there */ if (fabs(dth-dtl) <= dtacc) return(ans); } /* end for */ return(-2); /* exceed MAX iteration number */ } /* end if */ else { if (!fl) return(dtl); if (!fh) return(dth); return(-3); /* no sign change in range */ } } /* end FC_xzridder */ #endif %} DECLARE %{ struct FermiChopper_struct FCVars; %} INITIALIZE %{ /************************* INITIALIZE COUNTERS ******************************/ int i; /************************ CALCULATION CONSTANTS *****************************/ FCVars.omega = 2*PI*nu; FCVars.ph0 = phase/360; FCVars.sum_t=FCVars.sum_v=FCVars.sum_N=FCVars.sum_N_pass=0; /* compatibility parameters */ if (rad) radius=rad; if (slit) length=slit; if (alpham) alpha =alpham; if (Wi) W =Wi; if (dist) printf("FermiChopper: %s: Parameter 'dist' is not valid anymore.\n" "WARNING Use 'phase' instead. Ignoring.\n", NAME_CURRENT_COMP); /* check of input parameters */ if (m <= 0) { m=0; R0=0; } if (radius <= 0) { printf("FermiChopper: %s: FATAL: unrealistic cylinder radius radius=%g [m]\n", NAME_CURRENT_COMP, radius); exit(-1); } if (width > 0 && width < radius*2 && Nslit > 0) { w = width/Nslit; } if (w <= 0) { printf("FermiChopper: %s: FATAL: Slits in the package have unrealistic width w=%g [m]\n", NAME_CURRENT_COMP, w); exit(-1); } if (Nslit*w > radius*2) { Nslit = floor(radius/w); printf("FermiChopper: %s: Too many slits to fit in the cylinder\n" " Adjusting Nslit=%f\n", NAME_CURRENT_COMP, Nslit); } if (length > radius*2) { length = sqrt(radius*radius - Nslit*w*Nslit*w/4); printf("FermiChopper: %s: Slit package is longer than the whole\n" " chopper cylinder. Adjusting length=%g [m]\n", NAME_CURRENT_COMP, length); } if (eff <= 0 || eff > 1) { eff = 0.95; printf("FermiChopper: %s: Efficiency is unrealistic\n" " Adjusting eff=%f\n", NAME_CURRENT_COMP, eff); } if (Qc <= 0) { Qc = 0.02176; m = 0; R0 = 0; } if (W <= 0) W=1e-6; if (curvature) { FCVars.C_slit = curvature; if (1 < fabs(radius*curvature)) exit(printf("FermiChopper: %s: Slit curvature is unrealistic\n", NAME_CURRENT_COMP)); } FCVars.L_slit = length; if (verbose && nu) printf("FermiChopper: %s: frequency=%g [Hz] %g [rpm], time frame=%g [s]\n" , NAME_CURRENT_COMP, nu, nu*60, 2/nu); %} TRACE %{ /** local CALCULATION VARIABLES**************************************/ /** Interaction with slit package ***************************/ double slit_input; /* length of the slits */ /** Variables for calculating interaction with blades ***************/ double xp1, zp1, xp2, zp2, vxp1, vxp2, xp3, vzp1, vzp2, zp3; /** Reflections ***********************************************/ double distance_W; double n1,n2,n3; /** variables used for calculating new velocities after reflection **/ double q; double arg; /** Multiple Reflections ******************************/ int loopcounter=0; /* How many reflections happen? */ /** Time variables *********************************/ double t3; /* interaction time */ double dt; /* interaction intervals */ double t1,t2; /* cylinder intersection time */ double time_in_cylinder; double time_in_slit; /************** test, if the neutron interacts with the cylinder ***/ if (cylinder_intersect (&t1, &t2, x, y, z, vx, vy, vz, radius, ymax-ymin)) { if (t1 <= 0) ABSORB; /* Neutron started inside the cylinder */ dt=t2-t1; /* total time of flight inside the cylinder */ time_in_cylinder = dt; PROP_DT(t1); /* Propagates neutron to entrance of the cylinder */ SCATTER; /* neutron must not enter or leave from top or bottom of cylinder. */ if (y >= 0.99*ymax || y <= 0.99*ymin ) ABSORB; vxp1 = sqrt(vx*vx+vy*vy+vz*vz); FCVars.sum_v += p*vxp1; FCVars.sum_t += p*t; FCVars.sum_N += p; /* neutron must enter the cylinder opening: |X'| < full package width*/ xp1 = FC_xrot(x,z, t,FCVars); /* X'(t) */ if (fabs(xp1) >= Nslit*w/2) ABSORB; /*********************** PROPAGATE TO SLIT PACKAGE **************************/ /* zp1 = Z' at entrance of cylinder Z'(t) */ zp1 = FC_zrot(x,z, t, FCVars); /* Checking on which side of the Chopper the Neutron enters: sign(Z') */ slit_input = (zp1 > 0 ? length/2 : -length/2); /* Checking if we are indeed in cylinder, outside slits */ if (!(zp1 > length/2 || zp1 < -length/2) ) ABSORB; /* time to reach slit package in [0,time to exit cylinder]: Z'=slit_input */ t3 = FC_xzridder(x,z,vx,vz, t,dt, 'z', slit_input, FCVars); if( (t3 < 0)||(t3 > dt) ) { if (verbose > 1) { printf("FermiChopper: %s: Can not reach entrance of slits\n" " dt=%g. Absorb on chopper aperture.\n", NAME_CURRENT_COMP, t3); if (t3 == -3) printf(" No sign change to determine intersection (FC_xzidder:1)\n"); else if (t3 == -2) printf(" Max iterations reached in (FC_xzidder:1)\n"); else if (t3 < 0) printf(" Error when solving intersection (FC_xzidder:1)\n"); } ABSORB; /* neutron can not reach slit entrance */ } /* Propagating to the slit package entrance */ PROP_DT(t3); dt -= t3; /* remaining time to exit of cylinder */ /* must have X'< slit package width at package Z'=slit_input */ xp1 = FC_xrot(x,z, t, FCVars); if (fabs(xp1) >= Nslit*w/2) ABSORB; if (mcdotrace) { zp1 = FC_zrot(x,z, t, FCVars); /* indicate position of neutron in mcdisplay */ xp2 = x; zp2 = z; x = xp1; z=zp1; SCATTER; x=xp2; z=zp2; } else SCATTER; /* solve Z'=-slit_input for time of exit of slit package */ t3 = FC_xzridder(x,z,vx,vz, t,dt, 'z', -slit_input, FCVars); if((t3 < 0)||(t3 > dt)) { if (verbose > 1) { printf("FermiChopper: %s: Can not reach exit of slits\n" " dt=%g. Ignore.\n", NAME_CURRENT_COMP, t3); if (t3 == -3) printf(" No sign change to determine intersection (FC_xzidder:2)\n"); else if (t3 == -2) printf(" Max iterations reached in (FC_xzidder:2)\n"); else if (t3 < 0) printf(" Error when solving intersection (FC_xzidder:2)\n"); } /* neutron can not reach slit output. Retain time interval */ } else dt = t3; /* reduce time interval to [0, time of slit exit] */ time_in_slit = dt; /*********************PROPAGATION INSIDE THE SLIT PACKAGE *******************/ /* index of slit hit at entrance n1=0 (xp1=-N/2) ... N-1 (xp1=+N/2) */ n1 = floor(Nslit/2 - xp1/w); if (n1 < 0) n1 = 0; if (n1 >= Nslit) n1 = Nslit-1; /******************* BEGIN LOOP FOR MULTIPLE REFLECTIONS ********************/ for (loopcounter=0; loopcounter<=FermiChopper_MAXITER; loopcounter++) { /* compute trajectory tangents: m1=Vz'+w.X'(t), m2=Vz'+w.X'(t+dt) */ xp1 = FC_xrot (x,z, t, FCVars); /* X'(t) */ xp2 = FC_xzrot_dt(x,z,vx,vz, t, dt, 'x', FCVars); /* X'(t+dt) */ vxp1= FC_xrot (vx+z*FCVars.omega,vz-x*FCVars.omega, t, FCVars); /* dX'(t)/dt */ vxp2= FC_xrot (vx+(z+vz*dt)*FCVars.omega,vz-(x+vx*dt)*FCVars.omega, t+dt,FCVars); /* dX'(t+dt)/dt */ /* time at tangent intersection */ t3 = (vxp1 - vxp2 ? ((xp1 - xp2) - (t*vxp1 - (t+dt)*vxp2))/(vxp1 - vxp2) : -1); /* If method fails, take the middle of the interval*/ if((t3 < 0)||(t3 > dt)) t3=dt*0.5; /* point coordinates at tangent intersection */ xp3 = FC_xzrot_dt(x,z,vx,vz, t, t3, 'x', FCVars); /* X'(t3) */ /* slit index at the end of the slit: */ n2 = floor(Nslit/2 - xp2/w); if (n2 < 0) n2 = 0; if (n2 >= Nslit) n2 = Nslit-1; /* slit index at the tangent intersection/middle point */ n3 = floor(Nslit/2 - xp3/w); if (n3 < 0) n3 = 0; if (n3 >= Nslit) n3 = Nslit-1; /* change slit means there is a reflection inside */ if ( (n2!=n1) || (n3!=n1) ) { /* ABSORB if no reflecting coating */ if (m == 0 || R0 == 0) ABSORB; /*Choosing the first change among [n2,n3] */ if (n3!=n1) { /* restrict search in [0,tangent point] */ n2=n3; dt = t3; } if (n2 > n1) { /* positive side of slit */ distance_W = (n1+1)*w; } else { /* negative side of slit */ distance_W = n1*w; } /* time to reach middle point in [0,dt]: X'=slit_wall */ t3 = FC_xzridder(x,z,vx,vz, t,dt, 'x', distance_W, FCVars); if((t3 < 0)||(t3 > dt)) { if (verbose) { printf("FermiChopper: %s: Can not reach slit wall\n" " dt=%g. Absorb.\n", NAME_CURRENT_COMP, t3); if (t3 == -3) printf(" No sign change to determine intersection (FC_xzidder:3)\n"); else if (t3 == -2) printf(" Max iterations reached in (FC_xzidder:3)\n"); else if (t3 < 0) printf(" Error when solving intersection (FC_xzidder:3)\n"); } /* neutron can not reach slit wall. ABSORB */ ABSORB; } zp1 = FC_xzrot_dt(x,z,vx,vz, t,t3, 'z', FCVars); /* check if point is still in the slit package, else exit loop */ if (!(zp1 > length/2 && zp1 < -length/2) ) break; /* Propagate to slit wall point */ PROP_DT(t3); dt -= t3; /* get velocity in rotating frame, on slit wall */ vxp1 = FC_xrot(vx,vz, t, FCVars); vzp1 = FC_zrot(vx,vz, t, FCVars); q = MS2AA*(fabs(vxp1)); if(q > Qc) { double arg = (q-m*Qc)/W; if(arg < 10) p *= .5*(1-tanh(arg))*(1-alpha*(q-Qc)); else ABSORB; /* Cutoff ~ 1E-10 */ } p *= R0; if (mcdotrace) { xp1 = FC_xrot(x,z, t,FCVars); zp1 = FC_zrot(x,z, t,FCVars); /* indicate position of neutron in mcdisplay */ xp2 = x; zp2 = z; x = xp1; z=zp1; SCATTER; x=xp2; z=zp2; } else SCATTER; /* reflect perpendicular velocity and compute new velocity in static frame */ vxp1 *= -1; /* apply transposed Transformation matrix */ vx = FC_xrot( vxp1,-vzp1, t,FCVars); vz = FC_xrot(-vxp1, vzp1, t,FCVars); } /* end if changed slit index */ else { /* neutron remains in the same slit: direct flight */ break; } } /* end for */ if (loopcounter >= FermiChopper_MAXITER) { if (verbose) printf("FermiChopper: %s:Max iterations %i reached inside slit. Absorb.\n", NAME_CURRENT_COMP, FermiChopper_MAXITER); ABSORB; } /********************* EXIT SLIT PACKAGE ********************************/ /* propagation times to cylinder. Should use t2 to exit */ if (!cylinder_intersect (&t1, &t2, x, y, z, vx, vy, vz, radius, ymax-ymin)) ABSORB; /* must be inside the cylinder */ if (t1 > 0) { if (verbose) printf("FermiChopper: %s: Neutrons are leaving chopper\n" " in the wrong direction. Absorb.\n", NAME_CURRENT_COMP); ABSORB; } if (t2 <= 0) { if (verbose) printf("FermiChopper: %s: Neutrons are leaving chopper\n" " without any control. Absorb.\n", NAME_CURRENT_COMP); ABSORB; } /* propagate to cylinder surface */ PROP_DT(t2); SCATTER; /* Check if the neutron left the cylinder by its top or bottom */ if (y >= 0.99*ymax || y <= 0.99*ymin ) ABSORB; /* must have X'< slit package width at package Z'=cylinder output */ xp1 = FC_xrot(x,z, t,FCVars); if (fabs(xp1) >= Nslit*w/2) ABSORB; /* Transmission coefficent */ p = p*eff; //finite cross section + transmission FCVars.sum_N_pass++; } /* end if cylinder_intersect */ /************************ TIME OF FLIGHT RESET ************************/ if (zero_time && nu) t -= floor( (t+1/(4*nu))*(2*nu) )/(2*nu); %} SAVE %{ double mean_k, mean_v, mean_t, mean_w=0, mean_L=0, ph0; if (FCVars.sum_N) { mean_v = FCVars.sum_v/FCVars.sum_N; mean_t = FCVars.sum_t/FCVars.sum_N; mean_k = V2K*mean_v; if (mean_k) mean_L = 2*PI/mean_k; mean_w = VS2E*mean_v*mean_v; if (!FCVars.sum_N_pass) printf("FermiChopper: %s: No neutron can pass the chopper.\n" " Mean velocity v = %g [m/s]\n" " Mean wavelength lambda= %g [Angs]\n" " Mean energy omega = %g [meV]\n" " Mean arrival time t = %g [s]\n" , NAME_CURRENT_COMP, mean_v, mean_L, mean_w, mean_t); } %} MCDISPLAY %{ double index_x=0; double index_z=0; double xpos, zpos; double Nz,Nx; FCVars.omega=0; FCVars.ph0 =0; Nz = (FCVars.C_slit ? 4 : 1); Nx = (Nslit > 11 ? 11 : Nslit); FCVars.C_slit *= -1; magnify("xz"); /* cylinder top/center/bottom */ circle("xz", 0,ymax,0,radius); circle("xz", 0,0 ,0,radius); circle("xz", 0,ymin,0,radius); /* vertical lines to make a kind of volume */ line( 0 ,ymin,-radius, 0 ,ymax,-radius); line( 0 ,ymin, radius, 0 ,ymax, radius); line(-radius,ymin, 0 ,-radius,ymax, 0 ); line( radius,ymin, 0 , radius,ymax, 0 ); /* slit package */ for (index_x = -Nx/2; index_x < Nx/2; index_x++) { for (index_z = -Nz/2; index_z < Nz/2; index_z++) { double xs1, zs1, zs2; double xp1, xp2, zp1, zp2; zs1 = length*index_z/Nz; zs2 = length*(index_z+1)/Nz; xs1 = w*Nslit*index_x/Nx; xp1 = FC_xrot(xs1, zs1, 0, FCVars); xp2 = FC_xrot(xs1, zs2, 0, FCVars); zp1 = FC_zrot(xs1, zs1, 0, FCVars); zp2 = FC_zrot(xs1, zs2, 0, FCVars); multiline(5, xp1, ymin, zp1, xp1, ymax, zp1, xp2, ymax, zp2, xp2, ymin, zp2, xp1, ymin, zp1); } } /* cylinder inner sides containing slit package */ xpos = Nslit*w/2; zpos = sqrt(radius*radius - xpos*xpos); multiline(5, xpos, ymin, -zpos, xpos, ymax, -zpos, xpos, ymax, +zpos, xpos, ymin, +zpos, xpos, ymin, -zpos); xpos *= -1; multiline(5, xpos, ymin, -zpos, xpos, ymax, -zpos, xpos, ymax, +zpos, xpos, ymin, +zpos, xpos, ymin, -zpos); %} END