/******************************************************************************* * * McStas, neutron ray-tracing package * Copyright 1997-2002, All rights reserved * Risoe National Laboratory, Roskilde, Denmark * Institut Laue Langevin, Grenoble, France * * Component: Source_adapt.comp * * %I * Written by: Kristian Nielsen * Date: 1999 * Version: $Revision: 1.29 $ * Origin: Risoe * Release: McStas 1.6 * Modified by: Revised by: Aaron M. Percival * Modified by: 2006 * Modified by: Queen's University Department of Physics * Modified by: Added the option of having an initial distribution that is uniform in wavelength * * Neutron source with adaptive importance sampling * * * * %D * Rectangular source with flat energy or wavelength distribution that * uses adaptive importance sampling to improve simulation efficiency. * Works together with the Adapt_check component. * * The source divides the three-dimensional phase space of (energy, * horizontal position, horizontal divergence) into a number of * rectangular bins. The probability for selecting neutrons from each * bin is adjusted so that neutrons that reach the Adapt_check * component with high weights are emitted more frequently than those * with low weights. The adjustment is made so as to attemt to make * the weights at the Adapt_check components equal. * * Focusing is achieved by only emitting neutrons towards a rectangle * perpendicular to and placed at a certain distance along the Z axis. * Focusing is only approximate (for simplicity); neutrons are also * emitted to pass slightly above and below the focusing rectangle, * more so for wider focusing. * * In order to prevent false learning, a parameter beta sets a * fraction of the neutrons that are emitted uniformly, without regard * to the adaptive distribution. The parameter alpha sets an initial * fraction of neutrons that are emitted with low weights; this is * done to prevent early neutrons with rare initial parameters but * high weight to ruin the statistics before the component adapts its * distribution to the problem at hand. Good general-purpose values * for these parameters are alpha = beta = 0.25. * * %P * INPUT PARAMETERS: * * xmin: (m) Left edge of rectangular source * xmax: (m) Right edge * ymin: (m) Lower edge * ymax: (m) Upper edge * dist: (m) Distance to target rectangle along z axis * xw: (m) Width(x) of target * yh: (m) Height(y) of target * E0: (meV) Mean energy of neutrons * dE: (meV) Energy spread (energy range is from E0-dE to E0+dE) * lambda0: (AA) Mean wavelength of neutrons (if energy not specified) * dlambda: (AA) Wavelength spread * flux: (1/(cm**2*AA**st)) Absolute source flux * N_E: (1) Number of bins in energy (or wavelength) dimension * N_xpos: (1) Number of bins in horizontal position * N_xdiv: (1) Number of bins in horizontal divergence * alpha: (1) Learning cut-off factor (0 < alpha <= 1) * beta: (1) Aggressiveness of adaptive algorithm (0 < beta <= 1) * filename: (string) Optional filename for adaptive distribution output * * OUTPUT PARAMETERS: * * p_in: Internal, holds initial neutron weight * y_0: Internal * C: Internal * r_0: Internal * count: Internal, counts neutrons emitted * adpt: Internal structure shared with the Adapt_check component * * %E *******************************************************************************/ DEFINE COMPONENT Source_adapt DEFINITION PARAMETERS (N_E=20, N_xpos=20, N_xdiv=20, alpha=0.25, beta=0.25, string filename=0) SETTING PARAMETERS (xmin,xmax,ymin,ymax, dist=2.33, xw=0.05, yh=0.1, E0=0, dE=0, lambda0=0, dlambda=0, flux=1e13) OUTPUT PARAMETERS (p_in, y_0, C, r_0, count, adpt) STATE PARAMETERS (x,y,z,vx,vy,vz,t,s1,s2,p) SHARE %{ %include "adapt_tree-lib" %} DECLARE %{ struct source_adapt { struct adapt_tree *atree; /* Adaptive search tree */ int idx; /* Index of current bin */ double *psi, *n; /* Arrays of weight sums, neutron counts */ double psi_tot; /* Total weight sum */ double pi, num; /* Initial p, number of bins in tree */ double factor; /* Adaption quality factor */ double a_beta; /* Adaption agression factor */ } adpt; double count; /* Neutron counter */ double y_0, C, r_0; double p_in; %} INITIALIZE %{ int i; double a, lambda_min, lambda_max, delta_lambda, source_area; adpt.num = N_E*N_xpos*N_xdiv; adpt.a_beta = beta; if (E0 == 0) { lambda_min = lambda0 - dlambda; /* AAngstroem */ lambda_max = lambda0 + dlambda; delta_lambda = dlambda; } else { lambda_min = sqrt(81.81/(E0+dE)); /* AAngstroem */ lambda_max = sqrt(81.81/(E0-dE)); delta_lambda = lambda_max - lambda_min; } source_area = (xmax - xmin)*(ymax - ymin)*1e4; /* cm^2 */ p_in = flux/mcget_ncount()*delta_lambda*source_area; adpt.atree = adapt_tree_init(adpt.num); adpt.psi = malloc(adpt.num*sizeof(*adpt.psi)); adpt.n = malloc(adpt.num*sizeof(*adpt.n)); if(!(adpt.psi && adpt.n)) { fprintf(stderr, "Fatal error: out of memory.\n"); exit(1); } for(i = 0; i < adpt.num; i++) { adapt_tree_add(adpt.atree, i, 1.0/adpt.num); adpt.psi[i] = adpt.n[i] = 0; } adpt.psi_tot = 0; count = 0; y_0 = adpt.num > 8 ? 2.0/adpt.num : 0.25; r_0 = 1/(double)alpha*log((1 - y_0)/y_0)/(double)mcget_ncount(); C = 1/(1 + log(y_0 + (1 - y_0)*exp(-r_0*mcget_ncount()))/(r_0*mcget_ncount())); %} TRACE %{ double thmin,thmax,phmin,phmax,theta,phi,v,r,E,lambda; double new_v; int i_E, i_xpos, i_xdiv; /* Randomly select a bin in the current distribution */ r = rand01(); adpt.idx = adapt_tree_search(adpt.atree, adpt.atree->total*r); if(adpt.idx >= adpt.num) { fprintf(stderr, "Hm, idx is %d, num is %d, r is %g, atree->total is %g\n", adpt.idx, (int)adpt.num, r, adpt.atree->total); adpt.idx = adpt.num - 1; } /* Now find the bin coordinates. */ i_xdiv = adpt.idx % (int)N_xdiv; i_xpos = (adpt.idx / (int)N_xdiv) % (int)N_xpos; i_E = (adpt.idx / (int)N_xdiv) / (int)N_xpos; /* Compute the initial neutron parameters, selecting uniformly randomly within each bin dimension. */ x = xmin + (i_xpos + rand01())*((xmax - xmin)/(double)N_xpos); y = ymin + rand01()*(ymax - ymin); z=0; thmin = atan2(-xw/2.0 - x, dist); thmax = atan2( xw/2.0 - x, dist); theta = thmin + (i_xdiv + rand01())*((thmax - thmin)/(double)N_xdiv); phmin = atan2(-yh/2.0 - y, dist); phmax = atan2( yh/2.0 - y, dist); phi = phmin + rand01()*(phmax - phmin); if(E0 == 0) { lambda = lambda0 - dlambda + (i_E + rand01())*(2.0*dlambda/(double)N_E); v = 3.956E3/lambda; vy = v*sin(phi); vx = v*cos(phi)*sin(theta); vz = v*cos(phi)*cos(theta); } else { E = E0 - dE + (i_E + rand01())*(2.0*dE/(double)N_E); v = sqrt(E)*SE2V; vy = v*sin(phi); vx = v*cos(phi)*sin(theta); vz = v*cos(phi)*cos(theta); } t = 0; /* Adjust neutron weight. */ p = p_in; adpt.factor = y_0/(y_0 + (1 - y_0)*exp(-r_0*count)); count++; p /= adpt.atree->v[adpt.idx]/(adpt.atree->total/adpt.num); p *= C*adpt.factor*(thmax - thmin)*(sin(phmax) - sin(phmin)); SCATTER; /* Update distribution, assuming absorbtion. */ if(adpt.n[adpt.idx] > 0) adpt.psi_tot -= adpt.psi[adpt.idx]/ (adpt.n[adpt.idx]*(adpt.n[adpt.idx] + 1)); adpt.n[adpt.idx]++; if(adpt.psi_tot != 0) { new_v = (1 - adpt.a_beta)*adpt.factor*adpt.psi[adpt.idx]/ (adpt.n[adpt.idx]*adpt.psi_tot) + adpt.a_beta/adpt.num; adapt_tree_add(adpt.atree, adpt.idx, new_v - adpt.atree->v[adpt.idx]); } /* Remember initial neutron weight. */ adpt.pi = p; %} FINALLY %{ double *p1 = NULL; int i; if(filename) { p1 = malloc(adpt.num*sizeof(double)); if(!p1) fprintf(stderr, "Warning: Source_adapt: " "not enough memory to write distribution.\n"); } if(p1) { for(i = 0; i < adpt.num; i++) p1[i] = adpt.atree->v[i]/adpt.atree->total; if(E0 == 0) { DETECTOR_OUT_1D("Adaptive source Wavelength distribution", "Wavelength [AA]", "Probability", "lambda", lambda0 - dlambda, lambda0 + dlambda, adpt.num, NULL, p1, NULL, filename); } else { DETECTOR_OUT_1D("Adaptive source energy distribution", "Energy [meV]", "Probability", "E", E0 - dE, E0 + dE, adpt.num, NULL, p1, NULL, filename); } free(p1); } adapt_tree_free(adpt.atree); %} MCDISPLAY %{ magnify("xy"); multiline(5, (double)xmin, (double)ymin, 0.0, (double)xmax, (double)ymin, 0.0, (double)xmax, (double)ymax, 0.0, (double)xmin, (double)ymax, 0.0, (double)xmin, (double)ymin, 0.0); %} END