/***************************************************************************** * McStas, neutron ray-tracing package * Copyright (C) 1997-2012 Risoe National Laboratory, Roskilde, Denmark * Component: sample_nxs * * %IDENTIFICATION * Author: Mirko Boin * Date: Jan 2019 * Origin: Helmholtz-Zentrum Berlin fuer Materialien und Energie (Germany) * Version: $Revision: 1.3$ * * General powder/polycrystalline sample with neutron-matter interaction based * on neutron cross section calculations of a unit cell * * %DESCRIPTION * Features: * - coherent and incoherent scattering, absorption and transmission * - multiple scattering * - wavelength-dependent neutron cross section calculation * - unit cell definition (via input file) * - focussing option for scattered neutrons (via d_phi) * - strain gradient simulation for axial symmetric samples (...to be extended) * * Geometry is a powder filled cylinder or a box defined by radius or xwidth, * yheight, zthick respectively. The component handles coherent and incoherent * scattering (also multiple scattering), absorption and transmission. Hence, it * is suitable for diffraction (scattering) and imaging (transmission) instruments * at the same time. In order to improve the neutron statistics at the detector * these individual features can be enabled/disabled using TransOnly, IncohScat, * MultiScat. * For example, if scattering shall not be monitored (e.g. imaging), then TransOnly * can be set to 1 to let the component calculate the neutron transmission through * the sample only. Likewise, incoherent scattering can be switched on and off. * * The decision of whether a neuron shall be scattered, absorbed or transmitted is * based on the wavelength-dependent neutron cross sections (nxs), i.e. the neutron * velocity is used here. These cross sections represent the individual scattering * and absorption probabilities for each individual neutron. However, the calculation * of neutron cross sections depends on the material defined for this sample component. * Therefore, several input parameters such as the crystal structure and the atoms * involved (isotope mass, coherent scattering length, ...). This information is * provided by an input file with the following format: * *
Al.nxs
# define the unit cell parameters:
* # space_group - the space group number or Hermann or Hall symbol [string]
* # lattice_a, ...b, ...c - the lattice spacings a,b,c [angstrom]
* # lattice_alpha, ...beta, ...gamma - the lattice angles alpha,beta,gamma [degree]
* # debye_temp - the Debye temperature [K]
*
* space_group = -F 4 2 3 # space group number is also allowed (= 225)
* lattice_a = 4.049
* lattice_b = 4.049
* lattice_c = 4.049
* lattice_alpha = 90
* lattice_beta = 90
* lattice_gamma = 90
* debye_temp = 429.0
*
* # add atoms to the unit cell:
* # notation is "atom_number = name b_coh sigma_inc sigma_abs_2200 molar_mass x y z"
* # name - labels the current atom/isotope [string]
* # b_coh - the coherent scattering length [fm]
* # sigma_inc - the incoherent scattering cross section [barns]
* # sigma_abs_2200 - the absorption cross sect. at 2200 m/s [barns]
* # molar_mass - the Molar mass [g/mol]
* # debye_temp - the Debye temperature [K]
* # x y z - the Wyckoff postion of the atom inside the unit cell
*
* [atoms]
* add_atom = Al 3.449 0.008 0.23 26.98 0.0 0.0 0.0
*
* The above example defines a pure (fcc) aluminium. Other example files are provided. * Alternatively, a fallback mechanism exists to simulate an alpha-Fe (bcc) sample * if an input file is missing. But have a look for warning messages during simulation to * make sure you are not running the fallback alpha-Fe simulation accidently! * * CHANGELOG: * v1.0 - first official release with McStas 2.0 * v1.1 - enabled strain distribution * v1.2 - use updated libnxs (global Debye temp -> parameter files changed) * - corrected sigma incoherent * - updated towards McStas 2.1 conventions * v1.3 - bugfixes * For details, please contact the author and visit the nxs website and/or the repository. * NXS Website: http://www.helmholtz-berlin.de/people/boin/nxs_en.html * NXS Repository: http://bitbucket.org/mirkoboin/nxs * * * %PARAMETERS * * INPUT PARAMETERS * * TransOnly: enable/disable (=1/=0) the transmission only option * IncohScat: enable/disable (=1/=0) incoherent scattering * MultiScat: enable/disable (=1/=0) multiple scattering * xwidth: horizontal dimension of sample, as a width [m] * yheight: vertical dimension of sample, as a height [m] * zthick: thickness of sample [m] * radius: radius of sample in (x,z) plane [m] * nxsFileName: filename of the unit cell definition * max_hkl: maximum (hkl) indices to consider for the nxs calculation, e.g. max_hkl=3 means (hkl)=333 * d_phi=0: Angle corresponding to the vertical angular range * to focus to, e.g. detector height. 0 for no focusing [deg,0-180] * strainFileName: filename for simulating strain gradients * sample_temp: sample temperature [K] * * %EXAMPLES * COMPONENT sample = sample_nxs( yheight = 0.005, radius = 0.0025, nxsFileName = "Al.nxs", * max_hkl=8, TransOnly = 0, IncohScat = 1, MultiScat = 0, d_phi = 110.0 ) * * %L * M. Boin (2012), J. Appl. Cryst. 45, 603-607, doi:10.1107/S0021889812016056 * %L * M. Boin, R.C. Wimpory, A. Hilger, N. Kardjilov, S.Y. Zhang, M. Strobl (2012), J. Phys.: Conf. Ser. 340, 012022, doi:10.1088/1742-6596/340/1/012022 * %L * M. Boin, A. Hilger, N. Kardjilov, S.Y. Zhang, E.C. Oliver, J.A. James, C. Randau, R.C. Wimpory (2011), J. Appl. Cryst. 44, 1040-1046, doi:10.1107/S0021889811025970 * * * %END *******************************************************************************/ DEFINE COMPONENT Sample_nxs DEFINITION PARAMETERS ( int TransOnly=0, int IncohScat=1, int MultiScat=1 ) SETTING PARAMETERS ( xwidth=0, yheight=0, zthick=0, radius=0, thickness=0, string geometry=0, string nxsFileName="", int max_hkl=8, d_phi=0, string strainFileName="", sample_temp=293.0 ) OUTPUT PARAMETERS (isrect,intersect,nxs_init_success,p_transmit,xsect_total,xsect_coherent, xsect_incoherent,xsect_absorption, lambda,velocity,fullpath,t1,t2,uc,A,mu_factor,sd,V2L) DEPENDENCY "-I@MCCODE_LIB@/libs/libnxs/ @MCCODE_LIB@/libs/libnxs/libnxs.a" SHARE %{ %include "nxs.h" %include "read_table-lib" %include "interoff-lib" enum StrainDistribution { AxialSym1D, /* 1D axial symmetric, i.e. cylinder sample with strain gradient along radius */ AxialSym2D /* 2D axial symmetric, i.e. cylinder sample with strain gradient along radius and along height */ /*NoSym1D*/ }; typedef struct { /** values from FILE **/ /* strains in the sample represented by lattice spacing */ int n_entries; /* number of entries for hoop, radial, axial */ double *pos_x; /* lists of all positions */ double *pos_y; double *pos_z; double *strains_hoop; /* lists of all strain values */ double *strains_radial; double *strains_axial; double d0_hoop; /* reference values */ double d0_radial; double d0_axial; enum StrainDistribution strain_distr; /* strain distribution mode */ } StrainData; int readStrainFile( char *fileName, StrainData *sd ) { /* read the file */ t_Table dataTable; Table_Read(&dataTable, fileName, 1); /* read 1st block data from file into table */ int i; char **parsing; /* some default values to check if input file is consistent */ int column_radius = 0; int column_height = 0; int column_hoop = 0; int column_radial = 0; int column_axial = 0; sd->d0_hoop = sd->d0_radial = sd->d0_axial = 0.0; /* parsing of header for sample parameters */ parsing = Table_ParseHeader( dataTable.header,"d0_hoop","d0_radial","d0_axial",NULL ); if (parsing) { if( parsing[0] ) sd->d0_hoop = atof(parsing[0]); else { fprintf(stderr, "%s: Warning: No value for 'd0_hoop' found in file %s\n", NAME_CURRENT_COMP, fileName); } if( parsing[1] ) sd->d0_radial = atof(parsing[1]); else { fprintf(stderr, "%s: Warning: No value for 'd0_radial' found in file %s\n", NAME_CURRENT_COMP, fileName); } if( parsing[2] ) sd->d0_axial = atof(parsing[2]); else { fprintf(stderr, "%s: Warning: No value for 'd0_axial' found in file %s\n", NAME_CURRENT_COMP, fileName); } } else { fprintf(stderr, "%s: Warning: parsing file %s\n", NAME_CURRENT_COMP, fileName); return -1; } /* parsing of header for column assignment */ parsing = Table_ParseHeader(dataTable.header, "column_radius", "column_height","column_hoop","column_radial","column_axial",NULL); /* assign columns */ if (parsing) { if (parsing[0]) column_radius = atoi(parsing[0]); if (parsing[1]) column_height = atoi(parsing[1]); if (parsing[2]) column_hoop = atoi(parsing[2]); if (parsing[3]) column_radial = atoi(parsing[3]); if (parsing[4]) column_axial = atoi(parsing[4]); for (i=0; i<5; i++) if (parsing[i]) free(parsing[i]); free(parsing); /* decide which strain distribution is stored in the file */ int minimum_columns = 4; if( column_radius ) { sd->strain_distr = AxialSym1D; minimum_columns = 4; } if( column_radius && column_height ) { sd->strain_distr = AxialSym2D; minimum_columns = 5; } /* check if data exists (at least minimum number of columns) */ if( dataTable.columns < minimum_columns ) { fprintf(stderr, "%s: Error: There are too few table columns in '%s' to process strain data - using default values now\n", NAME_CURRENT_COMP, fileName); sd->n_entries = 0; return -1; } /* allocate data array */ sd->pos_x = (double*)malloc(dataTable.rows*sizeof(double)); if( sd->strain_distr == AxialSym2D ) sd->pos_y = (double*)malloc(dataTable.rows*sizeof(double)); sd->strains_hoop = (double*)malloc(dataTable.rows*sizeof(double)); sd->strains_radial = (double*)malloc(dataTable.rows*sizeof(double)); sd->strains_axial = (double*)malloc(dataTable.rows*sizeof(double)); sd->n_entries = dataTable.rows; /* get data from table */ struct list_struct{ double pos_x; double pos_y; double hoop; double radial; double axial; }; struct list_struct* dataList = (struct list_struct*)malloc( sizeof(struct list_struct)*sd->n_entries ); for (i=0; in_entries; i++) { dataList[i].pos_x = Table_Index(dataTable, i, column_radius-1); if( sd->strain_distr == AxialSym2D ) dataList[i].pos_y = Table_Index(dataTable, i, column_height-1); dataList[i].hoop = Table_Index(dataTable, i, column_hoop-1); dataList[i].radial = Table_Index(dataTable, i, column_radial-1); dataList[i].axial = Table_Index(dataTable, i, column_axial-1); } /* sort list by position */ int pos_x_compare( const void *par1, const void *par2) { return ( ((struct list_struct*)par1)->pos_x<((struct list_struct*)par2)->pos_x ) ? 0 : 1; } /* (re-)sort the data set to evaluate it in the TRACE section */ qsort( dataList, dataTable.rows, sizeof(struct list_struct), pos_x_compare ); if( sd->strain_distr == AxialSym2D ) { int pos_y_compare( const void *par1, const void *par2) { return ( ((struct list_struct*)par1)->pos_y<((struct list_struct*)par2)->pos_y ) ? 0 : 1; } qsort( dataList, dataTable.rows, sizeof(struct list_struct), pos_y_compare ); } for (i=0; in_entries; i++) { sd->pos_x[i] = dataList[i].pos_x; if( sd->strain_distr == AxialSym2D ) sd->pos_y[i] = dataList[i].pos_y; sd->strains_hoop[i] = dataList[i].hoop; sd->strains_radial[i] = dataList[i].radial; sd->strains_axial[i] = dataList[i].axial; } /* data set is now sorted */ } return 0; } %} DECLARE %{ int shape; off_struct offdata; int nxs_init_success; /* cross sections */ double xsect_coherent; double xsect_incoherent; double xsect_absorption; /* sample */ NXS_UnitCell uc; StrainData sd; /* velocity to lambda conversion */ double V2L; %} INITIALIZE %{ fprintf(stderr, "%s: Using libnxs version %s\n", NAME_CURRENT_COMP, nxs_version()); shape = -1; /* -1:no shape, 0:cyl, 1:box, 2:sphere, 3:any-shape */ if( geometry && strlen(geometry) && strcmp(geometry, "NULL") && strcmp(geometry, "0") ) { if( off_init(geometry, xwidth, yheight, zthick, 0, &offdata) ) { shape = 3; /* arbitrary */ thickness=0; //concentric=0; } } else if( xwidth && yheight && zthick ) shape = 1; /* box */ else if (radius > 0 && yheight) shape = 0; /* cylinder */ else if (radius > 0 && !yheight) shape = 2; /* sphere */ if( shape < 0 ) exit( fprintf(stderr,"Sample_nxs %s has invalid dimensions.\n" "ERROR: Please check parameter values (xwidth, yheight, zthick, radius).\n", NAME_CURRENT_COMP) ); if( readStrainFile( strainFileName, &sd ) ) { fprintf(stderr, "%s: No strain data found! Recalculation of d-values not used.\n", NAME_CURRENT_COMP); sd.n_entries = 0; } if (TransOnly) { fprintf(stderr, "%s: Performing neutron transmission only (no scattering).\n", NAME_CURRENT_COMP); if( sd.n_entries>0 ) fprintf(stderr, "%s: WARNING: Cannot use strain data in TransOnly mode!\n", NAME_CURRENT_COMP); } else { if (IncohScat) fprintf(stderr, "%s: Enabling incoherent scattering.\n", NAME_CURRENT_COMP); if (MultiScat) { fprintf(stderr, "%s: Enabling multiple scattering.\n", NAME_CURRENT_COMP); if (d_phi) { fprintf(stderr, "%s: WARNING: No focussing possible in multiple scattering mode. Setting d_phi=0.\n", NAME_CURRENT_COMP); d_phi = 0; } } } /* read unit cell parameters from file and initialise hkl */ uc = nxs_newUnitCell(); NXS_AtomInfo *atomInfoList; int numAtoms = nxs_readParameterFile( nxsFileName, &uc, &atomInfoList); if( numAtoms < 1 ) { /* fallback solution: if no file exists, use alpha_iron */ fprintf(stderr, "%s: WARNING: nxs parameter file %s NOT found! Using default values...\n", NAME_CURRENT_COMP, nxsFileName); strncpy(uc.spaceGroup,"229",MAX_CHARS_SPACEGROUP); uc.a = 2.866; uc.alpha = 90.0; uc.debyeTemp = 464.0; NXS_AtomInfo ai; strncpy(ai.label,"Fe",MAX_CHARS_ATOMLABEL); ai.b_coherent = 9.45; ai.sigmaIncoherent = 0.4; ai.sigmaAbsorption = 2.56; ai.molarMass = 55.85; ai.x[0] = ai.y[0] = ai.z[0] = 0.0; atomInfoList = (NXS_AtomInfo*)realloc( atomInfoList, sizeof(NXS_AtomInfo) ); atomInfoList[0] = ai; numAtoms = 1; } unsigned int i; if( NXS_ERROR_OK != nxs_initUnitCell(&uc) ) { fprintf(stderr, "%s: WARNING: No nxs parameters set! Sample will be transparent!\n", NAME_CURRENT_COMP); nxs_init_success = 0; } else { nxs_init_success = 1; uc.temperature = sample_temp; for( i=0; i0 ) { if( sd.d0_hoop<=1E-6 ) sd.d0_hoop = uc.a; if( sd.d0_radial<=1E-6 ) sd.d0_radial = uc.a; if( sd.d0_axial<=1E-6 ) sd.d0_axial = uc.a; } V2L = 2.0 * PI / V2K; } %} TRACE %{ int i; double strain_axial; double strain_hoop; double strain_radial; int intersect = 0; double p_transmit; double lambda; double velocity; double fullpath; /* intersection times */ double t1; double t2; double xsect_total; /* box or cylinder? */ if ( shape == 0 ) intersect = cylinder_intersect(&t1,&t2,x,y,z,vx,vy,vz,radius, yheight); else if( shape == 1 ) intersect = box_intersect(&t1,&t2,x,y,z,vx,vy,vz,xwidth, yheight, zthick); //else if( shape == 2 ) // intersect = sphere_intersect(&t0, &t3, x, y, z, vx, vy, vz, radius); else if( shape == 3 ) intersect = off_intersect(&t1, &t2, NULL, NULL, x, y, z, vx, vy, vz, offdata ); /* neutron intersects? */ if( intersect && (t2>0) && nxs_init_success ) { /* get current velocity and wavelength */ velocity = sqrt( vx*vx + vy*vy + vz*vz ); lambda = V2L / velocity; xsect_coherent = nxs_CoherentElastic(lambda, &uc ); xsect_incoherent = nxs_IncoherentElastic(lambda, &uc ) + nxs_IncoherentInelastic(lambda, &uc ) + nxs_CoherentInelastic(lambda, &uc ); xsect_absorption = nxs_Absorption(lambda, &uc ); xsect_total = xsect_coherent + xsect_incoherent + xsect_absorption; /* Handle transmission only (imaging mode) */ if (TransOnly) { /* path through the sample */ fullpath = velocity * (t2-t1); /* change the neutron weight */ p_transmit = exp( -xsect_total * fullpath*1E2 / uc.volume ); p *= p_transmit; } /* ...also handle scattering events */ else { int ms_loop = 1; if( MultiScat ) ms_loop = 10; double path; while( ms_loop-- ) { /* go to an event point (randomly) */ path = rand01() * (t2-t1); PROP_DT( path + t1 ); // printf("%i: t1=%f t2=%f\n",ms_loop, t1*1000,t2*1000); path = path * velocity; /******** consider strain data... ********/ if( sd.n_entries>0) { /* take care of neutron position to find the correct (micro)strain value */ double pos = sqrt( x*x + z*z ); i = 0; while( pos>sd.pos_x[i] && sd.n_entries>i ) { i++; } if( i==0 ) { strain_hoop = sd.strains_hoop[i]; strain_radial = sd.strains_radial[i]; strain_axial = sd.strains_axial[i]; pos = sd.pos_x[i]; } else if( i==sd.n_entries ) { strain_hoop = sd.strains_hoop[i-1]; strain_radial = sd.strains_radial[i-1]; strain_axial = sd.strains_axial[i-1]; pos = sd.pos_x[i-1]; } else { /* get lattice strains from current neutron position */ /* use linear interpolation between two given positions */ strain_axial = (sd.strains_axial[i] - sd.strains_axial[i-1]) / (sd.pos_x[i] - sd.pos_x[i-1]) * (pos - sd.pos_x[i-1]) + sd.strains_axial[i-1]; strain_hoop = (sd.strains_hoop[i] - sd.strains_hoop[i-1]) / (sd.pos_x[i] - sd.pos_x[i-1]) * (pos - sd.pos_x[i-1]) + sd.strains_hoop[i-1]; strain_radial = (sd.strains_radial[i] - sd.strains_radial[i-1]) / (sd.pos_x[i] - sd.pos_x[i-1]) * (pos - sd.pos_x[i-1]) + sd.strains_radial[i-1]; } /* now take care of the neutron flight direction */ /* use sin2psi calculation to determine current dvalue */ double sin2psi; double dvalue; sin2psi = sin(acos( (x*vx + z*vz) / sqrt(x*x + z*z) / sqrt(vx*vx + vz*vz) )); sin2psi = sin2psi * sin2psi; double d_hoop = strain_hoop*sd.d0_hoop*1E-6+sd.d0_hoop; double d_radial = strain_radial*sd.d0_radial*1E-6+sd.d0_radial; dvalue = (d_hoop-d_radial) * sin2psi + d_radial; /* and now axial also... */ sin2psi = sin(acos( vy / sqrt(vx*vx + vy*vy + vz*vz) )); sin2psi = sin2psi * sin2psi; double d_axial = strain_axial*sd.d0_axial*1E-6+sd.d0_axial; dvalue = (dvalue-d_axial) * sin2psi + d_axial; /* re-init nxs calculation & adjust lattice spacings for all hkl...*/ for( i=0; idhkl = nxs_calcDhkl( hkl->h, hkl->k, hkl->l, &uc ); hkl->FSquare = nxs_calcFSquare( hkl, &uc ); } xsect_coherent = nxs_CoherentElastic(lambda, &uc ); xsect_incoherent = nxs_IncoherentElastic(lambda, &uc ) + nxs_IncoherentInelastic(lambda, &uc ) + nxs_CoherentInelastic(lambda, &uc ); xsect_absorption = nxs_Absorption(lambda, &uc ); xsect_total = xsect_coherent + xsect_incoherent + xsect_absorption; } /* path through the sample */ fullpath = velocity * (t2-t1); p_transmit = exp( -xsect_total * fullpath*1E2 / uc.volume ); /* check if neutron interacts with or transmits through the sample */ if( p_transmit < rand01() ) { double roulette_ball = rand01() * (xsect_total); double norm = lambda*lambda*1E-2 / 2.0 / uc.volume; /* ******************** */ /* SCATTER coherently */ /* ******************** */ if (roulette_ball <= xsect_coherent) { int j; int max_hkl = -1; double contrib = 0.0; while( max_hkl<(int)uc.nHKL-1 && 2.0*uc.hklList[max_hkl+1].dhkl-lambda > 1E-6 ) { max_hkl++; contrib += uc.hklList[max_hkl].FSquare * uc.hklList[max_hkl].multiplicity * uc.hklList[max_hkl].dhkl; } /* determine lattice plane (for scattering) */ roulette_ball = rand01() * contrib; contrib = 0.0; for( j=0; j 1) d_phi = 0; else d_phi = 2*asin(arg); } if (d_phi) { d_phi0 = 2*rand01()*fabs(d_phi); if (d_phi0 > d_phi) { d_phi0 = PI+(d_phi0-1.5*d_phi); } else { d_phi0=d_phi0-0.5*d_phi; } p *= d_phi/PI; } else d_phi0 = PI*randpm1(); vec_prod(tmp_vx,tmp_vy,tmp_vz, vx,vy,vz, 1,0,0); if (!tmp_vx && !tmp_vy && !tmp_vz) { tmp_vx=tmp_vz=0; tmp_vy=1; } /* v_out = rotate 'v' by 2*theta around tmp_v: Bragg angle */ rotate(vout_x,vout_y,vout_z, vx,vy,vz, 2.0*theta, tmp_vx,tmp_vy,tmp_vz); /* tmp_v = rotate v_out by d_phi around 'v' (Debye-Scherrer cone) */ rotate(tmp_vx,tmp_vy,tmp_vz, vout_x,vout_y,vout_z, d_phi0, vx, vy, vz); vx = tmp_vx; vy = tmp_vy; vz = tmp_vz; } /* ******************** */ /* SCATTER incoherently */ /* ******************** */ else if (roulette_ball <= xsect_coherent+xsect_incoherent) { /* check the incoherent switch */ if (IncohScat) { double solid_angle; randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle, 0, 0, 1, 2.0*PI, d_phi*DEG2RAD, ROT_A_CURRENT_COMP); vx *= velocity; vy *= velocity; vz *= velocity; if (d_phi) p *= d_phi/PI; } /* end of if (bIncohScat) */ } else { /* neutron absorption -> remove neutron from trajectory */ ms_loop = 0; ABSORB; } /* end of if-else( roulette_ball <= xsect_coherent ) */ int err = 0; if( shape==0 && !cylinder_intersect(&t1, &t2, x, y, z, vx, vy, vz, radius, yheight) || t2<0 ) err=1; else if( shape==1 && !box_intersect(&t1, &t2, x, y, z, vx, vy, vz, xwidth, yheight, zthick) || t2<0 ) err=1; else if( shape==2 && !sphere_intersect(&t1, &t2, x, y, z, vx, vy, vz, radius) || t2<0 ) err=1; else if( shape==3 && !off_intersect(&t1, &t2, NULL, NULL, x, y, z, vx, vy, vz, offdata) || t2<0 ) err=1; if( err ) { /* Strange error */ fprintf(stderr, "sample_nxs: FATAL ERROR: Did not hit sample from inside.\n, t1=%f t2=%f\n", t1, t2); ABSORB; } t1 = 0.0; SCATTER; } /* end of if( p_transmit < rand01() ) */ else { /* else let the neutron simply transmit through the sample */ /* without any interaction or neutron weight change */ ms_loop = 0; } } /* end of while( ms_loop-- ) */ } /* end of if-else(bTransOnly) */ } /* end of if(box_intersect) */ %} MCDISPLAY %{ magnify("xyz"); if( geometry && strlen(geometry) && strcmp(geometry, "NULL") && strcmp(geometry, "0") ) { /* OFF file */ off_display(offdata); } else if( radius > 0 && yheight ) { /* cylinder */ circle("xz", 0, yheight/2.0, 0, radius); circle("xz", 0, -yheight/2.0, 0, radius); line(-radius, -yheight/2.0, 0, -radius, +yheight/2.0, 0); line(+radius, -yheight/2.0, 0, +radius, +yheight/2.0, 0); line(0, -yheight/2.0, -radius, 0, +yheight/2.0, -radius); line(0, -yheight/2.0, +radius, 0, +yheight/2.0, +radius); if( thickness ) { double radius_i=radius-thickness; circle("xz", 0, yheight/2.0, 0, radius_i); circle("xz", 0, -yheight/2.0, 0, radius_i); line(-radius_i, -yheight/2.0, 0, -radius_i, +yheight/2.0, 0); line(+radius_i, -yheight/2.0, 0, +radius_i, +yheight/2.0, 0); line(0, -yheight/2.0, -radius_i, 0, +yheight/2.0, -radius_i); line(0, -yheight/2.0, +radius_i, 0, +yheight/2.0, +radius_i); } } else if( xwidth && yheight ) { /* box/rectangle XY */ double xmin = -0.5*xwidth; double xmax = 0.5*xwidth; double ymin = -0.5*yheight; double ymax = 0.5*yheight; double zmin = -0.5*zthick; double zmax = 0.5*zthick; multiline(5, xmin, ymin, zmin, xmax, ymin, zmin, xmax, ymax, zmin, xmin, ymax, zmin, xmin, ymin, zmin); multiline(5, xmin, ymin, zmax, xmax, ymin, zmax, xmax, ymax, zmax, xmin, ymax, zmax, xmin, ymin, zmax); line(xmin, ymin, zmin, xmin, ymin, zmax); line(xmax, ymin, zmin, xmax, ymin, zmax); line(xmin, ymax, zmin, xmin, ymax, zmax); line(xmax, ymax, zmin, xmax, ymax, zmax); } else if ( radius > 0 && !yheight ) { /* sphere */ circle("xy", 0, 0.0, 0, radius); circle("xz", 0, 0.0, 0, radius); circle("yz", 0, 0.0, 0, radius); } %} END