/* DRAT.I Yorick interface definitions for Drat transport equation solver. $Id: drat.i,v 1.1 1993/09/02 22:48:38 munro Exp $ */ /* Copyright (c) 1994. The Regents of the University of California. All rights reserved. */ /* ------------------------------------------------------------------------ */ /* MAKE-INSTRUCTIONS SRCS = bound.c track.c trans.c drat.c ydrat.c LIB = drat DEPLIBS = */ /* Note: Constructions of the following form -- { extern a, b, c; } -- throughout this file prevent Codger from seizing an extern or struct statement that is not intended for it. In one case, a local statement is similarly protected for the mkdoc function. */ /* ------------------------------------------------------------------------ */ /* First section defines the highest level user interface routines: streak -- return complete solution given dump file containing the mesh, opacity, and source functions snap -- return time integrated solution given dump file containing the mesh, opacity, and source functions streak_save -- like streak, but places result directly in an output file guess_symmetry -- given dump file containing the mesh */ func streak(f, rays, slimits) /* DOCUMENT streak(f, rays) or streak(f, rays, slimits) returns the transparency and self-emission as functions of time for the rad-hydro problem dumped in file F, on the specified RAYS, with the specified limits SLIMITS on the transport integrals. The first dimension of RAYS may be length 3, 5, or 6 to represent the ray(s) in TDG/DIRT coordinates (x,y,theta), "best" coordinates (x,y,z,theta,phi), or internal coordinates (cos,sin,y,z,x,r), respectively. The remaining dimensions of RAYS, if any, will be called "nrays" below. The SLIMITS parameter, if present, is the value of the s-coordinate -- position along the ray -- at which to start and stop the integration of the transport equation. SLIMITS may be nil, a 1-D array of length 2, or a 2-by-nrays array. Each component of SLIMITS is [s_start, s_stop]; if s_stop supplies a compression algorithm to the streak function. The drat_compress can return anything, as long as it returns the same shape array (or nil) at each time. The snap_worker and streak_saver routines are examples of compression algorithms. */ local drat_backlight; /* DOCUMENT func drat_backlight(time) { extern gb, gav; ... } or drat_backlight= or drat_backlight= supplies a backlighter for the snap function. Given ngroup-by-nrays transparency fraction transp and self-emission selfem (in specific intensity units), snap applies the backlighter using: result= backlighter*transp + selfem; where backlighter is drat_backlight(time), if drat_backlight is a function, or drat_backlight itself, if drat_backlight is an array. Note that the result (or value) of backlighter_func must be conformable with transp and selfem. Most commonly, drat_backlight will be a vector of length ngroup -- a Planckian backlighter at temperature Tr would be drat_backlight= B_nu(gav, Tr); -- but that a scalar, 1-by-nrays, or ngroup-by-nrays are all possible. Note also that if drat_backlight is a function, the gb and gav arrays read from the history file are available as external variables. SEE ALSO: snap, drat_channel, drat_gate, apply_funcs */ local drat_channel; /* DOCUMENT func drat_channel(time) { extern gb, gav; ... } or drat_channel= or drat_channel= supplies a channel response for the snap function. Use the drat_glist option to select a subset of the groups; drat_channel can be used in addition to drat_glist. Given ngroup-by-nrays specific intensity, snap applies the channel response using: result= drat_channel(..,+)*specific_intensity(+,..); if drat_channel is an array, or result= drat_channel(specific_intensity, time); if drat_channel is a function. Note that if drat_channel is an array, its final dimension must be of length ngroup. A multidimensional drat_channel represents more than one channel response function. Most drat_channel arrays will be proportional to the bin widths gb(dif). The correct way to interpolate a filter function transmission fraction known at photon enrgies efa is: drat_channel= integ(ffa, efa, f.gb)(dif) If you have more than one channel, the first dimension of drat_channel should be the channel number. The best way to generate a filter response function is to use Yorick's cold opacity library. To do this: #include "coldopac/xray.i" This will define the functions cold_opacity and cold_reflect, which you can use to build up channel response functions from filter materials and thicknesses and mirror compositions. Note also that if drat_channel is a function, the gb and gav arrays read from the history file are available as external variables. SEE ALSO: drat_glist, snap, drat_backlight, drat_gate, apply_funcs */ local drat_gate; /* DOCUMENT func drat_gate(times) { extern gb, gav; ... } or drat_gate= supplies a gate (to make gated images) for the snap function. For a simple gate, the drat_start and drat_stop options will be more efficient than drat_gate. The input to drat_gate is the list of dump times; the output should be the "effective dt" for each of these dumps. This is the product of the actual time interval and the gate transparency; the sum of the return vector is the gate time. See the default_gate and gaussian_gate functions for examples. Note that the gb and gav arrays read from the history file are available as external variables, in case the gate transparency is frequency dependent. SEE ALSO: snap, drat_backlight, drat_channel, apply_funcs drat_start, drat_stop, gaussian_gate, default_gate */ local drat_ocompute, drat_oadjust; /* DOCUMENT func drat_ocompute(file, time) { extern opac, source; ...} or drat_ocompute= and func drat_oadjust(file, time) { extern opac, source; ...} or drat_oadjust= supply opacities from a source other than the file.drat_akap and file.drat_ekap, or adjust these values. You need to be cognizant of the drat_glist option (see get_kaps source code). DRAT_OCOMPUTE must set opac and source entirely on its own; DRAT_OADJUST will be called afterwards. The default DRAT_OCOMPUTE (default_ocompute) reads drat_akap and drat_ekap from FILE, optionally extracting drat_glist, and places them in opac and source. DRAT_OADJUST is free to modify opac and source them at will; the default DRAT_OADJUST is nil, which means no adjustment. Any opacity or emissivity multipliers will be applied after DRAT_OADJUST, as will the point centering operation if necessary (DRAT_OADJUST should return zone centered opacities). */ local drat_integrate; /* DOCUMENT func drat_integrate(file, mesh, time, irays, slimits) { ... } or drat_integrate= integrate the transport equation. FILE is positioned to TIME, and MESH has already been read. IRAYS are the rays in internal format and SLIMITS is the integration limits. The return value should be ngroup-by-2-by-raydims (where irays is 6-by-raydims). The default integrator is default_integrate, which handles the drat_ocompute, drat_oadjust, drat_amult, drat_emult, drat_omult, drat_akap, drat_ekap, drat_glist, drat_linear, and drat_nomilne options. Reasons to replace the default routine include: (1) Some or all of the opacities come from a source other than the FILE, e.g.- a second post processing file. (2) The total number of zones times number of groups is debilitatingly large, even though the number of rays times the number of groups is not. */ local drat_quiet; /* DOCUMENT drat_quiet By default, Drat prints the total number of records it will process, and the number of the record it is currently processing. If drat_quiet is non-nil and non-zero, the printout is supressed. */ func default_ocompute(f, time) /* DOCUMENT default_ocompute(f, time) initial value of drat_ocompute. Extracts drat_akap and drat_ekap from file F, possibly using the subset drat_glist. TIME is unused. */ { { extern opac, source; } /* outputs -- local to caller */ { extern drat_akap, drat_ekap; } /* options */ { extern drat_glist; } /* option */ if (is_void(drat_glist)) { opac= get_member(f, drat_akap); source= get_member(f, drat_ekap); } else { /* repeat get_member calls here -- potentially reduces disk traffic */ opac= get_member(f, drat_akap)(..,drat_glist); source= get_member(f, drat_ekap)(..,drat_glist); } /* if akap and ekap have other than the expected shape, fix them */ dims= dimsof(get_member(f,drat_rt)); kmax= dims(2); lmax= dims(3); dims= dimsof(opac); ndims= dims(1); if (ndims<3 || dims(2)!=kmax || dims(3)!=lmax) { /* unfortunately, there are two possible ndims==2 cases: either both are spatial dimensions, or one is space and the other is energy -- only the latter case is handled here */ o= opac; opac= []; s= source; source= []; ngroup= (ndims>1? dims(0) : 1); opac= array(structof(o), kmax, lmax, ngroup); source= array(structof(s), kmax, lmax, ngroup); if (ndims<3) { nzones= dims(2); if (nzones==(kmax-1)*(lmax-1)) { tmp= array(0.0, kmax-1, lmax-1, ngroup); tmp(*,)= o; opac(2:0,2:0,)= tmp; tmp(*,)= s; source(2:0,2:0,)= tmp; } else if (has_ireg) { list= where(ireg); if (numberof(list)==nzones) { opac(list,1,)= o; source(list,1,)= s; } } } else if (dims(2)==kmax-1 && dims(3)==lmax-1) { opac(2:0,2:0,)= o; source(2:0,2:0,)= s; } } } func default_gate(times) /* DOCUMENT default_gate(times) initial value of drat_gate. Refer to the source code to learn how to write your own gate function, making proper use of drat_start and drat_stop options in addition to the input times. SEE ALSO: gauss_gate, drat_gate */ { dummy= use_origins(0); /* append start and stop times to ends of times list -- if none specified, use first and last times */ if (!is_void(drat_start)) t= [drat_start]; else t= [times(1)]; if (!is_void(drat_stop)) tn= [drat_stop]; else tn= [times(0)]; grow, t, times, tn; /* associate half of the previous interval and half of the next interval with each dump */ return t(dif)(zcen); } func gauss_gate(times) /* DOCUMENT gauss_gate(times) gate function used by gaussian_gate. Refer to the source code to learn how to write your own gate function, making proper use of drat_start and drat_stop options in addition to the input times. SEE ALSO: gaussian_gate, drat_gate */ { dummy= use_origins(0); /* append start and stop times to ends of times list -- if none specified, use first and last times */ if (!is_void(drat_start)) t= [drat_start]; else t= [times(1)]; if (!is_void(drat_stop)) tn= [drat_stop]; else tn= [times(0)]; grow, t, times, tn; /* The correct approach is to interpolate into the time integral of the gate transparency. If there are n times, there are n+2 values of the t array just constructed. Each of the n+1 intervals should be associated half with the preceding time and half with the following time. */ return gauss_int(t)(dif)(zcen); } func gauss_int(t) /* DOCUMENT gauss_int(t) returns time integral of Gaussian specified in call to gaussian_gate. */ { /* Algorithm from Numerical Recipes (Press, et.al.) -- fractional error less than 1.2e-7. */ { extern gauss_t0, gauss_tsigma, gauss_norm; } tn= (t-gauss_t0)/gauss_tsigma; z= abs(tn); x= 1.0/(1.0+0.5*z); /* note that the "parentheses first" order of polynomial evaluation uses far less temporary space than the usual "parentheses last" */ erfc= x*exp(-z*z+ ((((((((0.17087277*x-0.82215223)*x+1.48851587)*x- 1.13520398)*x+0.27886807)*x-0.18628806)*x+ 0.09678418)*x+0.37409196)*x+1.00002368)*x-1.26551223); x= sign(tn); erfc= (1.0-x)+x*erfc; return gauss_norm*erfc; } func gaussian_gate(t0, tsigma, max_trans) /* DOCUMENT gaussian_gate(t0, tsigma, max_trans) sets the drat_gate for the snap function to be a Gaussian centered at time T0, with sigma TSIGMA, and maximum transmission fraction MAX_TRANS. SEE ALSO: snap, drat_gate */ { { extern gauss_t0, gauss_tsigma, gauss_norm; } gauss_t0= t0; gauss_tsigma= tsigma; gauss_norm= sqrt(2.0*pi)*tsigma; drat_gate= gauss_gate; } func reset_options /* DOCUMENT reset_options resets all options for the streak, snap, and streak_save functions to their default values. */ { { extern drat_rt, drat_zt, drat_ireg, drat_akap, drat_ekap; } { extern drat_isymz, drat_khold, drat_lhold, drat_gb, drat_gav; } { extern drat_amult, drat_emult, drat_omult, drat_ireg_adj; } { extern drat_start, drat_stop; } { extern drat_symmetry, drat_glist, drat_linear, drat_nomilne; } { extern drat_compress, drat_backlight, drat_channel, drat_gate; } { extern drat_static, drat_ocompute, drat_oadjust, drat_integrate; } { extern drat_quiet; } drat_rt= "rt"; drat_zt= "zt"; drat_ireg= "ireg"; drat_akap= "akap"; drat_ekap= "ekap"; drat_isymz= "isymz"; drat_khold= "khold"; drat_lhold= "lhold"; drat_gb= "gb"; drat_gav= "gav"; drat_amult= drat_emult= drat_omult= drat_ireg_adj= drat_start= drat_stop= drat_symmetry= drat_glist= drat_linear= drat_nomilne= drat_compress= drat_backlight= drat_channel= drat_static= []; drat_gate= default_gate; drat_ocompute= default_ocompute; drat_oadjust= []; drat_integrate= default_integrate; drat_quiet= []; } reset_options; func is_present(vars, name) /* DOCUMENT is_present(get_vars(f), name) returns 1 if variable NAME is present in file F, 0 if not. */ { return (anyof(*vars(1)==name) || anyof(*vars(2)==name)); } func streak_times(f) /* DOCUMENT streak_times(f) returns the times from file F whic lie between the optional drat_start and drat_stop. SEE ALSO: drat_start, drat_stop */ { { extern drat_start, drat_stop; } /* options */ times= get_times(f); if (!is_void(drat_start)) times= times(where(times>=drat_start)); if (!is_void(drat_stop)) times= times(where(times<=drat_stop)); return times; } func get_std_limits(rays, slimits) /* DOCUMENT get_std_limits(rays, slimits) returns slimits suitable for internal routines: 2-by-nrays, with s=0 at point of closest approach to origin */ { dummy= use_origins(0); dims= dimsof(rays); if (is_void(slimits)) { slims= array([1.0,-1.0], numberof(rays)/dims(2)); } else { mask= (slimits(1,..) 0.001); bnu= mask*bnu + (!mask)*vsq*kt(-,..); return B_nu_scale*bnu; } func alloc_mesh(f, vars) { dummy= use_origins(0); { extern drat_symmetry; } /* option */ if (is_void(drat_symmetry)) zsym= guess_symmetry(f, vars); else zsym= drat_symmetry; /* note that khold and lhold must be presented to form_mesh in a manner that is independent of the origin of the rt and zt arrays -- here, use 1 origin indexing */ { extern drat_khold, drat_lhold; } /* options */ { extern drat_rt; } /* option */ if (drat_khold && is_present(vars, drat_khold)) khold= get_member(f, drat_khold); else khold= 0; if (drat_lhold && is_present(vars, drat_lhold)) lhold= get_member(f, drat_lhold); else lhold= 0; return form_mesh(zsym, khold, lhold); } func get_kaps(f, mesh, time) { /* outputs -- local to caller */ { extern opac, source; } /* retrieve from file -- optionally only those groups in drat_glist */ { extern drat_ocompute, drat_oadjust; } /* options */ drat_ocompute, f, time; if (!is_void(drat_oadjust)) drat_oadjust, f, time; /* apply absorption and emission multipliers */ { extern drat_amult, drat_emult, drat_omult; } /* options */ if (!is_void(drat_emult)) source*= drat_emult; if (!is_void(drat_amult)) { opac*= drat_amult+1.e-20; source/= drat_amult+1.e-20; } if (!is_void(drat_omult)) opac*= drat_omult; /* point center the source function (in place) if requested */ { extern drat_linear, drat_nomilne; } /* options */ if (drat_linear) { if (structof(source)!=double) source= double(source); pcen_source, opac, source, mesh, drat_nomilne; } } /* ------------------------------------------------------------------------ */ /* Low level interface */ extern form_mesh; /* DOCUMENT form_mesh(zsym, khold, lhold) returns an opaque "mesh" object, which will hold rt, zt, ireg, and a boundary edge list. This opaque mesh object is required as an input to the integ_flat and integ_linear routines. ZSYM is 2 for spherical symmetry, 1 for z=0 reflection symmetry, or 0 for no symmetry KHOLD and LHOLD are the 1-origin indices of "hold" lines in the mesh, or 0 if none. This information is used only during the pcen_source operation before integ_linear is called. SEE ALSO: update_mesh, integ_flat, integ_linear */ extern update_mesh; /* DOCUMENT update_mesh, mesh, rt, zt or update_mesh, mesh, rt, zt, ireg updates the opaque MESH object to reflect a new RT, ZT, and (optionally) IREG. The boundary edges are recomputed and stored in MESH, as well. SEE ALSO: form_mesh, integ_flat, integ_linear */ extern find_boundary; /* DOCUMENT boundary= find_boundary(mesh) or boundary= find_boundary(mesh, region, sense) returns an array of 4 pointers representing the boundary of the MESH, or of a particular REGION of the MESH, with a particular SENSE -- 0 for counterclockwise in the (r,z)-plane, 1 for clockwise. The returned arrays are: *boundary(1) zone index list -- always 1-origin values *boundary(2) side list 0, 1, 2, or 3 side 0 is from point zone to zone-1, side 1 is from zone-1 to zone-imax-1 *boundary(3) z values of boundary points *boundary(4) r values of boundary points SEE ALSO: form_mesh, update_mesh */ func integ_flat(opac, source, rays, mesh, slimits) /* DOCUMENT integ_flat(opac, source, rays, mesh, slimits) or integ_flat(opac, source, ray_paths) returns ngroup-by-2-by-nrays result, where result(,1,..) is the transparency factors, and result(,2,..) is the self-emission for each group on each ray. The input OPAC and SOURCE are the opacity (an inverse length) and the source function (a specific intensity). They are mesh-by-ngroups zone centered arrays. The result has the same units as SOURCE. In the second form, RAY_PATHS was returned by the track_rays function. SEE ALSO: integ_linear, track_rays, form_mesh, streak, snap */ { /* get various dimensions */ dims= dimsof(opac); ngroup= dims(0); kxlm= numberof(opac)/ngroup; if (anyof(dims!=dimsof(source)) || (!is_void(mesh) && kxlm<_get_msize(mesh))) error, "opac and source must be kmax-by-lmax-by-ngroup arrays"; dims= dimsof(rays); nrays= numberof(rays)/dims(2); /* form result array */ if (--dims(1)) dims(2:-1)= dims(3:0); result= array(double, ngroup, 2, dims); if (!is_void(mesh)) { /* convert slimits to standard s-coordinate used internally, based on point of closest approach of ray to origin, rather than ray reference point */ slims= get_std_limits(rays, slimits); return _raw1_flat(opac, source, kxlm, ngroup, internal_rays(rays), nrays, mesh, slims, result); } else { /* the routine internally checks that rays.zone never gets larger than kxlm */ return _raw2_flat(opac, source, kxlm, ngroup, rays, nrays, result); } } func integ_linear(opac, source, rays, mesh, slimits) /* DOCUMENT integ_linear(opac, source, rays, mesh, slimits) or integ_linear(opac, source, ray_paths) returns ngroup-by-2-by-nrays result, where result(,1,..) is the transparency factors, and result(,2,..) is the self-emission for each group on each ray. The input OPAC and SOURCE are the opacity (an inverse length) and the source function (a specific intensity). They are mesh-by-ngroups arrays; OPAC is zone centered, while SOURCE is point centered (using pcen_source). The result has the same units as SOURCE. In the second form, RAY_PATHS was returned by the track_rays function. The integ_linear routine assumes that the SOURCE function varies linearly between the entry and exit points from each zone. This assumption is poor near the turning point, and causes the result to be a discontinuous function of the ray coordinates, unlike the integ_flat result. SEE ALSO: pcen_source, integ_flat, track_rays, form_mesh, streak, snap */ { /* get various dimensions */ dims= dimsof(opac); ngroup= dims(0); kxlm= numberof(opac)/ngroup; if (anyof(dims!=dimsof(source)) || (!is_void(mesh) && kxlm<_get_msize(mesh))) error, "opac and source must be kmax-by-lmax-by-ngroup arrays"; dims= dimsof(rays); nrays= numberof(rays)/dims(2); /* form result array */ if (--dims(1)) dims(2:-1)= dims(3:0); result= array(double, ngroup, 2, dims); if (!is_void(mesh)) { /* convert slimits to standard s-coordinate used internally, based on point of closest approach of ray to origin, rather than ray reference point */ slims= get_std_limits(rays, slimits); return _raw1_linear(opac, source, kxlm, ngroup, internal_rays(rays), nrays, mesh, slims, result); } else { /* the routine internally checks that rays.zone never gets larger than kxlm */ return _raw2_linear(opac, source, kxlm, ngroup, rays, nrays, result); } } func pcen_source(opac, source, mesh, drat_nomilne) /* DOCUMENT pcen_source, opac, source, mesh, drat_nomilne point centers the SOURCE array (in place) using a complicated algorithm involving the OPAC and MESH (from form_mesh and update_mesh). If non-nil, DRAT_NOMILNE must have the same format as the drat_nomilne option. */ { /* get various dimensions */ dims= dimsof(opac); ngroup= dims(0); kxlm= numberof(opac)/ngroup; if (anyof(dims!=dimsof(source)) || kxlm<_get_msize(mesh)) error, "opac and source must be kmax-by-lmax-by-ngroup arrays"; if (!is_void(drat_nomilne)) { dims= dimsof(drat_nomilne); if (dims(1)<2 || dims(2)!=2 || dims(3)!=2) error, "drat_nomilne argument to pcen_source must be 2x2xn_edges"; nomilne= drat_nomilne-1; /* _raw_pcens needs 0-origin */ nedges= numberof(nomilne)/4; } else { nomilne= 0; nedges= 0; } _raw_pcens, opac, source, kxlm, ngroup, mesh, nomilne, nedges; } /* The Ray_Path struct is duplicated from drat.c -- if you change it here, you must change it there as well. */ struct Ray_Path { pointer zone; /* list of zones (1-origin) cut by the ray */ pointer ds; /* list of path lengths in above zones */ double fi, ff; /* fraction of 1st and last ds, respectively, outside the specified slimits */ pointer pt1, pt2; /* lists of endpoints of edges cut by ray -- ray cuts directed edge pt1->pt2 from right to left Like zone, always 1-origin values. */ pointer f; /* list of fractions -- (f+0.5) is the fraction of distance from pt1 to pt2 where ray cuts edge */ } func track_rays(rays, mesh, slimits) /* DOCUMENT ray_paths= track_rays(rays, mesh, slimits) returns array of Ray_Path structs representing the progress of RAYS through the MESH between the given SLIMITS. SEE ALSO: Ray_Path, integ_flat, get_ray-path */ { return _raw_track(numberof(rays)/dimsof(rays)(2), internal_rays(rays), mesh, get_std_limits(rays,slimits)); } func get_ray_path(path, rt, zt) /* DOCUMENT ray_info= get_ray_path(path, rt, zt) where PATH is one element of an array returned by track_rays, returns the points where the ray cut the edges of the mesh (ZT, RT). The returned RAY_INFO has two components: RAY_INFO(,1) is the z coordinates and RAY_INFO(,2) is the r coordinates. SEE ALSO: track_rays */ { pt1= *path.pt1; if (is_void(pt1)) return []; pt2= *path.pt2; f= *path.f; info= array(0.0, numberof(pt1), 2); info(,1)= zt(pt1)*(0.5-f) + zt(pt2)*(0.5+f); info(,2)= rt(pt1)*(0.5-f) + rt(pt2)*(0.5+f); if ((f=path.fi)>0.0) info(1,)= f*info(2,)+(1.-f)*info(1,); if ((f=path.ff)>0.0) info(0,)= f*info(-1,)+(1.-f)*info(0,); return info; } extern set_tolerances; /* DOCUMENT set_tolerances() or old_tols= set_tolerances([tol1, tol2, lost_tol]) returns the current tolerances for the ray tracking. Initially, these are [1.e-3, 1.e-6, 0.0]. In the second form, sets new tolerances. If any of TOL1, TOL2, or LOST_TOL is zero, that tolerance is restored to its default value. If TOL1 is less than zero, the root polishing operation which requires TOL1 and TOL2 is not done at all. SEE ALSO: track_rays, integ_flat, integ_linear, streak, snap */ extern _raw_track; /* _raw_track(nrays, rays, mesh, slimits) expects rays in internal format, and slimits as returned from get_std_limits */ extern _raw1_flat; /* xxPROTOTYPE void IntegFlat(double array opac, double array source, long kxlm, long ngroup, double array rays, long nrays, opaque mesh, double array slimits, double array result) */ extern _raw2_flat; /* xxPROTOTYPE void I2flat(double array opac, double array source, long kxlm, long ngroup, Ray_Path array rays, long nrays, double array result) */ extern _raw1_linear; /* xxPROTOTYPE void IntegLinear(double array opac, double array source, long kxlm, long ngroup, double array rays, long nrays, opaque mesh, double array slimits, double array result) */ extern _raw2_linear; /* xxPROTOTYPE void I2linear(double array opac, double array source, long kxlm, long ngroup, Ray_Path array rays, long nrays, double array result) */ extern _raw_pcens; /* xxPROTOTYPE void DoPtCenter(double array opac, double array source, long kxlm, long ngroup, opaque mesh, long array nomilne, long nedges) */ extern _get_msize; /* xxPROTOTYPE long _get_msize(opaque mesh) */ /* The initialization routine sets the Drat memory management routines to the Yorick memory management routines, and locates the Ray_Path data structure for track_rays. */ extern _init_drat; if (!is_void(_init_drat)) _init_drat; #include "../include/rays.i"