Monte Carlo
simulations of light transport
in turbid
media.
https://omlc.org/classroom/ece580CLT/mc/class1/howtomcsub/index.html
Steven L. Jacques
January, 2002
1. Introduction
2. The program mcmain.c
2.1 Header
2.2 main()
2.3 Call mcsub()
2.4 Save files
3.
The subroutines
3.1 mcsub()
3.2 Rfresnel()
3.3 SaveFiles()
3.4 RandomNumber()
3.5 Memory allocation routines
4.
Listing of mcmain.c
This chapter presents a Monte Carlo program written in ANSI Standard C that simulates the penetration of light into a light-scattering medium such as biological tissue.
The program uses a simple steady-state Monte Carlo subroutine, mcsub(),that launches photons as either (1) a collimated beam of variable beam radius, (2) a focused Gaussian beam with variable beam radius, focal depth, and focal waist radius, or (3) an isotropic point source at a chosen depth. The results returned by the subroutine are the concentration of light within the tissue and the escaping light at the tissue surface.
The concentration of light within the tissue is reported as the “fluence rate” F(z,r) in units of [W/cm2] per W of incident power, which equals the energy density of light in a local volume, [J/cm3], times the speed of light, [cm/s]. The escaping light is reported as the “flux density” J(r) in units of [W/cm2] per W of incident power, which equals the power per unit surface area escaping the medium across a local area of the air/medium surface. The parameters z and r indicate depth and radial position [cm], respectively.
The program mcmain() uses the subroutine mcsub()to execute the Monte Carlo simulation. The mcsub() is a concise implementation of the Monte Carlo method. The subroutine utilizes several other subroutines which are also listed within mcmain() and explained later.
The history of this Monte Carlo simulation began with a simple Monte Carlo simulation written by the author that implemented the Henyey-Greenstein scattering function and a mismatched air/tissue boundary. The program was improved by incorporating the step size selection based on a random number as used by Wilson and Adam 1983 [[1]] who first applied a Monte Carlo method to light transport in biological tissues using isotropic scattering. Keijzer et al. 1989 [[2]] improved on the tracking of photon trajectories during simulations of transport in tissue. Keijzer et al. 1989 [[3]]applied Monte Carlo to tissue autofluorescence. Drawing from the work of Witt 1977 [[4]] who provided the basics of Monte Carlo modeling, Prahl et al. 1989 [[5]]translated Keijzer’s work from cylindrical coordinates to Cartesian coordinates that yielded simpler code, and and implemented the selection of deflection angles using random numbers for Henyey-Greenstein scattering. Wang et al. (1992) [[6]] and (1995) [[7]]converted Prahl’s work into modularized ANSI Standard C code for multilayered tissues called MCML which has been widely promulgated. Jacques (1998) [[8]] reduced the MCML code to its simplest form, called mc321.c, to provide a simple simulation of light propagation from point, line, and planar sources of light. For this chapter, the mc321.c code was converted to a subroutine called mcsub() that provides for three dimensional photon propagation from a collimated beam, a focused Gaussian beam, or an isotropic point source with a surface boundary with mismatch refractive indices such as an air/tissue surface.
In summary, this chapter illustrates the use of the Monte Carlo method to model the transport of light within a light-scattering light-absorbing tissue. A listing of mcmain.c with all the required subroutines is provided.
The overall organization of the program mcmain.c is shown below. Each part is discussed in more detail in the following sections. The main() routine within mcmain.c calls a Monte Carlo subroutine, mcsub() that encapsulates the basic Monte Carlo algorithm. The program mcmain.c sets up the desired simulation parameters and calls mcsub() to execute the desired Monte Carlo simulation.
/* mcmain.c
*/
header
declare
subroutines
main() {
Set up variables and arrays.
Call Monte
Carlo subroutine.
Save
escaping flux Jx(r) and fluence rate distribution Fx(z,r).
}
list
subroutines
The initial header portion of mcmain.c sets up the main() program. The initial #include commands incorporate supporting standard C program files that are needed by the program. The #define commands cause global substitutions of the first argument by the second argument, i.e. USERLABEL is replaced by the string “an example Monte Carlo simulation” and BINS is replaced by the value 101. Thus the user can conveniently change the number of bins used by arrays in the program, and can specify a label that prints out during the run.
The subroutine declarations inform main() of the availability of subroutines listed at the end of the mcmain.c:
mcsub() executes a modular Monte Carlo simulation,
RFresnel() evaluates the value of internal reflectance at the tissue/air surface,
SaveFile() saves the escaping flux density J(r) and fluence rate F(z,r) to a file,
RandomGen() is the random number generator,
and four subroutines for allocation of memory:
nrerror() used if error encountered during memory
allocation,
*AllocVector() allocates memory for a 1-D array,
**AllocMatrix() allocates memory for a 2-D array,
FreeVector() frees the memory allocated for a 1-D array,
FreeMatrix() frees the memory allocated for a 2-D array.
The main() program organizes the user’s problem, calls the modular subroutine mcsub(), interprets the results, and saves the results to files.
The main() program begins with declarations of the variables used in main(). The first set of variables are USER CHOICES where the user can specify the optical properties of the medium at a particular wavelength.
The parameter mcflag determines whether photons will be launched as a collimated flat-field beam (mcflag = 0), as an approximation to a focused Gaussian beam (mcflag = 1), or as an isotropic point source (mcflag ≥ 2). For flat or Gaussian beams, the parameter radius determines the radius of the beam incident at the surface. In the case of the focused Gaussian beam, radius is the 1/e point radius of the beam at the surface of the medium. For the focused Gaussian beam, an additional pair of parameters describes the focus point under conditions of matched boundary conditions. The parameter waist is the 1/e radius of the Gaussian beam at the focal point, and the parameter zfocus is the depth position of the focal point, both for a matched boundary condition. However, when a Gaussian beam is launched into a tissue with a mismatched boundary condition, mcsub() will calculate the specular reflectance and the refraction of the beam at the external medium/internal medium interface. The parameters xs, ys, zs are used when mcflag ≥ 2 to describe the position of isotropic launching. When mcflag equals 0, 1 or 2, the subroutine mcsub() will printout progress reports to the user during the Monte Carlo simulation. If mcflag > 2, then mcsub() will behave as if mcflag = 2 but will omit any printouts. The number of photons to be launched by mcsub() is set by Nphotons. In summary,
Collimated 0
+ - - - - -
Focused Gaussian 1
+ + + - - -
Isotropic point ≥2
- - - + + +
where + means “uses” and – means “ignores.”
The user chooses the number of photons to be launched by mcsub() by specifying the parameter Nphotons.
The user also chooses the size of the bins for depth, dz, and radial position, dr, appropriate for the cylindrical coordinates used to record escaping flux density J(r) and fluence rate F(z,r). The values (BINS-1)*dz and (BINS-1)*dr specify the total depth and radial extent over which results are stored. The last bins, iz = BINS and ir = BINS, are used to collect any overflow consisting of photons that migrate beyond the extet of the bins.
The remaining variables are determined by the program and the user need not specify their values. All units are in [cm] or variations such as [cm-1] or [cm2]. The declaration and memory allocation of the 1D array (Jx) and the 2D array (Fx) employ memory allocation subroutines described in section 6.5.
There is a printout of the USER CHOICES so that the user can be reminded of the conditions of a simulation when reviewing a printout. A final initialization step sets all to arrays to zero. The indices [iz][ir] specify the particular z and r bins, respectively.
The first part of program considers the penetration of excitation light into the tissue or medium. The control parameters for the Monte Carlo subroutine to be used for the excitation were already set in the USER CHOICES section above. The mcsub() routine is called. The arguments of the subroutine include the optical properties of the medium at the excitation wavelength (muax, musx, gx, n1, n2), the number of r and z bins and their bin sizes and the number of photons to be launched (NR, NZ, dr, dz, Nphotons), the control parameters (mcflag, xs, ys, zs, radius, waist, zfocus), and the array pointers Jx[] and Fx[][] which will hold the results for escaping flux density Jx(r) [W/cm2/W] and fluence rate Fx(z,r) [W/cm2/W]. The subroutine mcsub() records its results in cylindrically symmetric radial coordinates of z and r. The results have been normalized by Nphotons so the same answer is obtained regardless of the value of Nphotons used, although for a low choice of Nphotons the results are more noisy.
Finally, the flux density Jx[ir] and fluence rate Fx[iz][ir] are sent to the subroutine SaveFile() that saves the data along with the appropriate r and z positions of each bin based on the function arguments (NR, NZ, dr, dz). The parameter mcflag is also an argument of SaveFile() and specifies the name of the files to be saved. For the case of mcflag = 0 the saved files are called “J0.dat” and “F0.dat”, for the case of mcflag = 1 the saved files are called “J1.dat” and “F1.dat”, and so forth.
The Monte Carlo simulation is executed using a subroutine called mcsub() that considers a semi-infinite medium with an upper surface boundary. The subroutine has the arguments:
mua absorption coefficient
mus scattering coefficient
g anisotropy of scattering
n1 refractive index of internal medium
n2 refractive index of external medium
NR number of r bins
NZ number of z bins
dr incremental size of r bins
dz incremental size of r bins
Nphotons number of photons to be launched
mcflag control of the launch as collimated, focused, or isotropic
xs, ys, zs location of isotropic launching
radius radius of collimated beam or 1/e radius of focused Gaussian beam
waist radius of Gaussian beam at the focal point
zfocus depth position of the focal point for Gaussian beam
*J pointer to 1D array of flux density escaping at surface, J[ir]
**F pointer to 2D array of fluence rate, F[iz][ir]
The subroutine follows an algorithm that is depicted in Figure 1. The program begins with SETUP, declaring and initializing the various variables. In particular, J[ir] and F[iz][ir] are set initially to values of zero.
Figure 1: Algorithm for the Monte Carlo subroutine mcsub().
The LAUNCH do-loop proceeds to launch the requested number of photons, Nphotons. If a photon escapes at the surface or is terminated by the ROULETTE procedure, then a new photon is launched. Each photon launching sets the initial photon weight W to a value 1.0 - rsp where rsp is the specular reflectance at the air/tissue surface. The escaping flux density J[ir] returned by mcsub() does not include rsp. The photon launching is executed as either a collimated beam (mcflag = 0), a focused Gaussian beam (mcflag = 1), or an isotropic point source (mcflag ≥ 2).
For a collimated beam, the radial position r of launch is selected based on a random number, rnd, and is assigned to the coordinate x while y and z are assigned the value 0:
x = radius*sqrt(rnd);
y = 0;
z = 0;
where radius is the beam radius provided as an argument of mcsub(). The photon trajectory is specified by the cosine of the angle of the trajectory relative to each of the x, y, and z axes, and these cosine(angle) values are called ux, uy, and uz, respectively. A collimated beam would have uz equal to 1 while ux and uy are assigned the value 0:
ux = 0;
uy = 0;
uz = 1;
The value of the specular reflectance, rsp, is calculated based on the refractive indices n1 and n2:
For a focused Gaussian beam, the radial position of launch is determined:
x = radius*sqrt(-log(rnd));
y = 0;
z = 0;
where radius is the 1/e radius of the Gaussian beam at the tissue surface. Note that log() is a base e logarithm function. The focus of the Gaussian beam for a matched boundary condition is specifed by zfocus and broadness of the focus is specified waist which is the 1/e radius of the beam at zfocus. The ratio waist/radius is used to scale the launch position x at the surface to yield a radial position xfocus at the depth zfocus, and the trajectory is oriented inward toward the central axis pointing to the position (xfocus,0,zfocus):
xfocus = x*waist/radius;
The program then computes the trajectory required for launching at the surface at position (x,0,0) toward the focus at position (xfocus,0,zfocus), characterized by ux,uy,uz. This trajectory is for the case of matched boundary conditions.
temp = sqrt((x -
xfocus)*(x - xfocus) + zfocus*zfocus);
sintheta = -(x -
xfocus)/temp
costheta = zfocus/temp;
ux = sintheta;
uy = 0.0;
uz = costheta;
This incident trajectory is then modified if there is a mismatched boundary condition. The refractive indices of the internal medium (the tissue), n1, and the external medium (the air), n2, determine the amount of specular reflectance that occurs upon entry into the tissue and the refraction that changes the photon trajectory. The subroutine RFresnel()determines the rsp for the angle of launch that is selected. The angle of transmission is returned as the variable uz.
rsp = RFresnel(n2, n1,
costheta, &uz);
sintheta = sqrt(1.0 -
uz*uz);
ux = -sintheta;
uy = 0.0;
uz = costheta;
Each photon is launched at a different angle and experiences a different rsp. The refraction at the mismatched boundary where n1 > n2 causes the focal point to move deeper into the tissue. Figure 2 illustrates the launching of a Gaussian beam that would focus at zfocus = 0.0300 cm under matched boundary.
Figure 2: Illustration of launching photons as a focused Gaussian beam. Example has radius = 0.0300 cm, waist = 0.0030 cm, zfocus = 0.0300 cm, and matched boundary conditions. Ten launching trajectories are shown as lines.
For an isotropic point source, the position of launch is specified by xs, ys, zs in the argument for mcsub(). The trajectory is isotropic and so has no preferential direction, and is specified:
costheta = 1.0 - 2.0*RandomGen(1,0,NULL);
sintheta = sqrt(1.0 -
costheta*costheta);
psi =
2.0*PI*RandomGen(1,0,NULL);
cospsi = cos(psi);
if (psi < PI)
sinpsi = sqrt(1.0 - cospsi*cospsi);
else
sinpsi = -sqrt(1.0 - cospsi*cospsi);
ux = sintheta*cospsi;
uy = sintheta*sinpsi;
uz = costheta;
The value sin(psi) is calculated as sinpsi = sqrt(1.0 - cospsi*cospsi) since the sqrt() function is faster than the sin() function. Because the launch point is within the tissue, there is no specular reflectance and rsp is set equal to zero.
For each photon launched, the specular reflectance rsp is calculated by one of the three methods above. Then the initial weight of that photon is set to 1.0 – rsp. Hence, a total weight of W = 1.0 is delivered to the tissue but only 1.0 - rsp actually enters the tissue. The resulting J[] therefore does not include specular reflectance. The photon’s status is initiated as photon_status = ALIVE where ALIVE has a Boolean value of 1.
Once a photon is launched, it enters the PROPAGATION CYCLE. The HOP section sets the stepsize s of that the photon takes and updates the current photon position (x,y,z) based on the current trajectory (ux,uy,uz):
s = -log(rnd)/mut;
x += s*ux;
y += s*uy;
z += s*uz;
The
ESCAPE? step asks if (z
<= 0) and if yes then the
photon is attempting to escape out the top surface of the medium. Then the ESCAPE section checks for
total internal reflectance by calling a random number rnd and asking
if (rnd >
RFresnel(n1, n2, -uz, &uz1))
If
yes, then the photon has escaped the tissue and its weight W
is added to the current escaping flux J[ir]. The ESCAPE section resets the photon
position then takes a partial step size to just reach the surface. The radial
position r is calculated based on the x and y
positions and the choice of ir is made by the equivalent of an absolute
value function, ir =
(long)(r/dr) + 1, with the
minimum value being 1 to indicate the first bin. Then the photon is terminated
by setting photon_status =
DEAD, where DEAD has a Boolean value of 0. The photon will bypass the
following DROP-SPIN-ROULETTE section and reaches the end of the PROPAGATION
CYCLE. Because the photon_status =
DEAD a new photon is
launched.
If no, then the photon does not escape
but is internally reflected, accomplished by setting z = -z. The photon_status remains ALIVE so
the photon can enter the DROP-SPIN-ROULETTE section. The DROP section causes the current weight W to decrement by
an amount that depends on the albedo = mus/(mua + mus):
absorb = W*(1 -
albedo);
W -= absorb;
Then
the value absorb is added to the current bin F[iz][ir]. The SPIN
section causes the trajectory of the photon to deviate by an angle theta
specified as costheta = cos(theta) based on sampling the
Henyey-Greenstein scattering function using a random number rnd:
temp = (1.0 - g*g)/(1.0
- g + 2*g*rnd);
costheta = (1.0 + g*g -
temp*temp)/(2.0*g);
Also,
an azimuthal angle psi for the trajectory change is chosen:
psi =
2.0*PI*RandomGen(1,0,NULL);
These
angles of deviation are used to calculate a new trajectory assigned to (ux,uy,uz).
The
ROULETTE section provides a means of terminating a photon based on
absorption. A value THRESHOLD was set equal to 1e-4 at the beginning of the subroutine.
If the weight W drops below THRESHOLD, then the photon is either terminated or
its weight W is increased and propagation continues. A random number rnd is
obtained and compared with a value CHANCE set to
0.1 in this program. The program asks if rnd < CHANCE,
and if yes then the weight is increased by a factor 1/CHANCE or 10-fold. Propagation continues and the photon returns to
the top of the PROPAGATION CYCLE.
If no, then the photon is terminated by setting photon_status = DEAD. At the end of the PROPAGATION CYCLE a
new photon is launched. This method statistically conserves photon energy but
allows for a means to terminate photons.
After
the PROPAGATION CYCLE has launched Nphotons, the
simulation is complete. The
subroutine now normalizes the values in J[ir] by the
area of the [ir] bins to yield the escaping flux density
[W/cm2/W]. The
subroutine normalizes the values in F[iz][ir] by the
bin volumes to yield the density of power deposition [W/cm3/W].
Further normalization by the absorption coefficient mua
[cm-1] yields the fluence rate [W/cm2/W]. The subroutine
returns J[] and F[][] to
the calling program mcmain().
To
test mcsub(), an example problem was run which could
be compared with diffusion theory. The optical properties were mua
= 1 cm-1, mus = 100 cm-1, g
= 0.90. The refractive indices of the internal and external media were 1.33 and
1.00, respectively, simulating a water/air interface. An isotropic point source
was launched at a depth of zs
= 1/(mua + mus*(1‑g))
or 0.09091 cm.
Figure
3 shows the comparison of the mcsub() result
and diffusion theory as outlined by Farrel et al. (1992) [[9]]
using the extrapolated boundary condition:
where
where
ri is the total internal reflectance at the tissue surface. The
difference between Monte Carlo and diffusion theory is characterized by the
ratio (DT – MC)/MC where DT is the J(r) calculated by diffusion theory
and MC is the J(r) calculated by mcsub(). The
ratio is in the range of –0.20 to 0.10, except near r = 0 where DT is far
too low. This is basically the same behavior predicted by the MCML code [6].
Figure 3: Testing mcsub() versus diffusion theory.
(TOP) Escaping flux density J(r) versus radial position r. (BOTTOM) Ratio (DT
– MC)/MC where DT is J(r) calculated by diffusion theory and MC is J(r)
calculated by mcsub().
Optical properties: ma = 1
cm–1, ms =1
00 cm–1, g = 0.90, n1 = 1.33, n2 = 1.00.
Isotropic point source at 1 mean free path below surface, i.e. zs = 1/(ma + ms(1-g)).
Another
test was to compute the total escaping flux, Jtot [W/W],
for
the optical properties: ma = 1 cm-1, ms
= 9 cm-1, g = 0.0, yielding an albedo = 0.90. The medium was
semi-infinite and the surface boundary condition was matched. This problem has
been simulated by Prahl (1988) [[10]]
who reported in his Table 3-1 the result to be 0.4149 and that this value
matched the published value of van de Hulst (1980) [[11]]. The mcsub()
was run ten times with ten different Seed values to
yield a mean and standard deviation of 0.41507 ± 0.00018 (n = 10).
The subroutine RFresnel() was prepared by Lihong Wang as part of the MCML code [6]. The subroutine computes the Fresnel reflectance at an interface between two media with refractive indices ni and nt where ni is the medium from which the incident photon arrives and nt is the medium into which the photon transmits. The angle of incidence is specifed as ca1 = cos(angle of incidence). The angle of transmission is calculated based on Snell’s Law by the subroutine and the cos(angle of transmission) is returned as the value of the variable ca2_Ptr:
RFresnel(ni, nt, ca1,
*ca2_Ptr)
During photon launch as a focused Gaussian beam, the incident medium refractive index is assigned the value of the external medium (ni = n2) and the transmitted medium refractive index is assigned the value of the internal medium (nt = n1). The specular reflectance rsp and the cos(angle of transmission), uz, are calculated by the subroutine call:
rsp = RFresnel(n2, n1,
costheta, &uz);
During photon propagation as photons attempt to escape the medium, they are tested for the occurence of total internal reflectance. In this case, the incident medium refractive index is the value of the internal medium (ni = n1) and the transmitted medium refractive index is assigned the value of the external medium (nt = n2). The incident cos(angle) is the negative of the current value uz which is negative because the photon is escaping, so -uz is positive. The transmitted angle uz1 is not used, but does specify the cos(angle of transmission) for the escaping photon and could be used to document the angle of escape. The test for photon escape is phrased:
if (rnd >
RFresnel(n1, n2, -uz, &uz1))
and if true then there is escape and if false then there is total internal reflectance.
The SaveFiles() subroutine saves two files, J[ir] and F[iz][ir], along with the appropriate values of r[ir] and z[iz]. The names of the files depends on the values of the argument Nfile:
SaveFile(*J, **F,
Nfile, NR, NZ, dr, dz)
where NR and NZ are the number of bins and dr and dz are the incremental bin sizes. If Nfile equals 1, then the names of the files are J1.dat and F1.dat. If Nfile equals 2, then the names of the files are J2.dat and F2.dat, and so on.
The values of r[ir] and z[ir] are calculated:
r[ir] = (ir –
0.5)*dr
z[iz] = (iz –
0.5)*dz
which are the midpoints of each bin. The calculation for r[ir] is based on the expectation value for r within the [ir] bin, assuming that the general form of the J[ir] and F[iz][ir] responses versus r is 1/r. Then the expectation value is:
which is the midpoint of the [ir] bin. A more correct assignment of r[ir] would involve using the true behavior of J and F versus r, but this leads to iteratively reconsidering an assignment after an initial determination of the behavior. The user is better advised to simply run the mcsub() with smaller values of dr and dz if the user wishes to refine the estimate of behavior J and F at small r.
The file J.dat is a file with two columns and NR rows. The first column is r[ir]. The second column is J[ir]. The file F.dat is a file with NR+1 columns and NZ+1 rows. The first element (1,1) is ignored, and is set equal to 0. The first column, rows 2 to NZ+1, lists the values of z[iz]. The first row, columns 2 to NR+1, lists the values of r[ir]. The remaining array, rows 2 to NZ+1, columns 2 to NR+1, holds the values of F[iz][ir].
The random number generator subroutine was prepared by Lihong Wang as part of the MCML code [6]:
RandomGen(Type, Seed, *Status)
The generator is initialized by the call with Type set to 0 and Seed set to a long integer (0 < Seed < 32000), for example set equal to 1:
RandomGen(0,1,NULL);
Subsequently, a random number rnd is generated by the call with Type set to 1:
rnd =
RandomGen(1,0,NULL);
In some cases, such as when the a command must evaluate log(rnd), it is important to exclude the value rnd = 0. In such cases, the call is phrased:
while ((rnd =
RandomGen(1,0,NULL)) <= 0.0);
The memory allocation routines were also prepared by Lihong Wang as part of the MCML code [6]. The routines are
nrerror(error_text[]);
*AllocVector(nl, nh);
**AllocMatrix(nrl, nrh,
ncl, nch);
FreeVector(*v, nl, nh);
FreeMatrix(**m, nrl,
nrh, ncl, nch);
They are used in declaring 1D and 2D arrays at the beginning of the program mcmain() and in freeing the memory allocated for these arrays at the end of mcmain(). They allow the arrays to be addressed by indices ranging from 1 to NR and 1 to NZ. Their use is shown in mcmain().
/*************
* mcmain.c
*************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
/*************************/
/**** USER CHOICES ******/
/*************************/
#define USER_LABEL
"mcmain.c for class"
#define BINS 101
/*************************/
/*************************/
/**********************
* DECLARE SUBROUTINES
*********************/
/* The Monte Carlo subroutine */
void mcsub(double mua, double mus, double g, double n1, double n2,
long
NR, long NZ, double dr, double dz, double Nphotons,
int
mcflag, double xs, double ys, double zs,
double
radius, double waist, double zfocus,
double
*J, double **F);
/* Computes internal reflectance at tissue/air interface */
double RFresnel(double n1, double n2, double ca1, double *ca2_Ptr);
/* Saves surface escape R(r) and fluence rate distribution F(z,r)
*/
void SaveFile(double *J, double **F, int Nfile,
long NR, long NZ, double dr, double dz);
/* Random number generator
Initiate by
RandomGen(0,1,NULL)
Use as rnd =
RandomGen(1,0,NULL) */
double RandomGen(char Type, long Seed, long *Status);
/* Memory allocation routines
* from MCML ver. 1.0,
1992 L. V. Wang, S. L. Jacques,
* which are modified
versions from Numerical Recipes in C. */
void
nrerror(char error_text[]);
double *AllocVector(short nl, short nh);
double **AllocMatrix(short nrl,short nrh,short ncl,short nch);
void
FreeVector(double *v,short nl,short nh);
void
FreeMatrix(double **m,short nrl,short nrh,short ncl,short nch);
/**********************
* MAIN PROGRAM
*********************/
int main() {
/**** Declare variables ******/
double mua; /* excitation absorption coeff.
[cm^-1] */
double mus; /* excitation scattering coeff.
[cm^-1] */
double g; /* excitation
anisotropy [dimensionless] */
double n1; /* refractive index of
medium */
double n2; /* refractive index outside
medium */
short
mcflag; /* 0 = collimated, 1 = focused Gaussian,
2 = isotropic pt */
double radius;
/* used if mcflag = 0 or 1 */
double
waist; /* used if mcflag =
1 */
double zfocus;
/* used if mcflag = 1 */
double xs; /* used if mcflag = 2
*/
double ys; /* used if mcflag = 2
*/
double zs; /* used if mcflag = 2
*/
/* other parameters */
double
Nruns; /* number photons
launched = Nruns x 1e6 */
double dr; /* radial bin size
[cm] */
double dz; /* depth bin size
[cm] */
char
label[1];
double Nphotons;
long
ir, iz;
double temp; /*
dummy variables */
double
start_time, finish_time1; /* for clock() */
double timeA,
timeB;
time_t now;
double *Jx;
double **Fx;
long
NR = BINS; /* number of radial bins */
long
NZ = BINS; /* number of depth bins */
Jx = AllocVector(1,BINS);
Fx = AllocMatrix(1, BINS, 1,
BINS); /* for absorbed excitation */
strcpy(label, USER_LABEL);
printf("\n||-------------------------\n");
printf( "||
%s\n",label);
printf(
"||-------------------------\n\n");
start_time = clock();
now = time(NULL);
printf("%s\n", ctime(&now));
/*************************/
/**** USER CHOICES ******/
/*************************/
mua =
1.0; /* excitation absorption
coeff. [cm^-1] */
mus =
100.0;/* excitation scattering coeff. [cm^-1] */
g
= 0.90; /* excitation anisotropy [dimensionless] */
n1 =
1.33; /* refractive index of medium */
n2 =
1.00; /* refractive index outside medium */
mcflag = 0;
/* 0 = collimated, 1 = focused Gaussian,
2 = isotropic pt */
radius = 0.0; /* used
if mcflag = 0 or 1 */
waist = 0.0; /* used if mcflag = 1 */
zfocus = 0.0; /* used
if mcflag = 1 */
xs =
0.0; /* used if mcflag = 2 */
ys =
0.0; /* used if mcflag = 2 */
zs =
0.090909; /* used if mcflag = 2 */
/* other parameters */
Nruns = 0.01; /* number photons launched
= Nruns x 1e6 */
dr =
0.0100; /* radial bin size [cm] */
dz =
0.0100; /* depth bin size [cm] */
if (1) { /* Switch printout ON=1 or OFF=0 */
/*
print out summary of parameters to user */
printf("-----
USER CHOICES -----\n");
printf("mua = %1.3f\n",mua);
printf("mus = %1.3f\n",mus);
printf("g =
%1.3f\n",g);
printf("n1 = %1.3f\n",n1);
printf("n2 =
%1.3f\n",n2);
printf("mcflag = %d\n" ,mcflag);
printf("radius = %1.4f\n",radius);
printf("waist = %1.4f\n",waist);
printf("zfocus = %1.4f\n",radius);
printf("xs =
%1.4f\n",xs);
printf("ys =
%1.4f\n",ys);
printf("zs =
%1.4f\n",zs);
printf("OTHER\n");
printf("Nruns = %1.1e\n",Nruns);
printf("dr =
%1.4e\n",dr);
printf("dz =
%1.4e\n",dz);
printf("---------------\n\n");
}
/* Initialize arrays */
for (ir=1; ir<=NR; ir++) {
Jx[ir]
= 0.0;
for
(iz=1; iz<=NR; iz++) {
Fx[iz][ir]
= 0.0;
}
}
/* Time completion estimate */
timeA = clock();
mcsub( mua,
mus, g, n1, n2, /* CALL THE
MONTE CARLO SUBROUTINE */
NR,
NZ, dr, dz, 1000,
mcflag,
xs, ys, zs,
radius,
waist, zfocus,
Jx,
Fx);
/* returns J, F */
timeB = clock();
temp = (timeB - timeA)/CLOCKS_PER_SEC/60/1000; /* min per photon */
printf("%5.3e min/photon \n", temp);
printf("estimated completion times = %5.2f min\n",
temp*1e6*Nruns);
/*********************
* CALL mcsub()
*********************/
Nphotons = 1e6*Nruns;
mcsub( mua,
mus, g, n1, n2, /* CALL THE
MONTE CARLO SUBROUTINE */
NR,
NZ, dr, dz, Nphotons,
mcflag,
xs, ys, zs,
radius,
waist, zfocus,
Jx,
Fx);
/* returns J, F */
/* Save results to files Ji.dat and Fi.dat, where i = mcflag. */
SaveFile(Jx, Fx, mcflag, NR, NZ, dr, dz);
printf("------------------------------------------------------\n");
finish_time1 = clock();
printf("-------------------------------------------------------------\n");
printf("Elapsed Time for excitation = %5.2f min\n",
(double)(finish_time1-start_time)/CLOCKS_PER_SEC/60);
now = time(NULL);
printf("%s\n", ctime(&now));
FreeVector(Jx, 1, BINS);
FreeMatrix(Fx, 1, BINS, 1, BINS);
return(0);
}
/**********************************************************
*
SUBROUTINES
**********************************************************/
/**********************************************************
* The Monte Carlo
SUBROUTINE
**********************************************************/
void mcsub(double mua, double mus, double g, double n1, double n2,
long
NR, long NZ, double dr, double dz, double Nphotons,
int
mcflag, double xs, double ys, double zs,
double
radius, double waist, double zfocus,
double
*J, double **F)
{
/* Constants */
double PI = 3.1415926;
short ALIVE = 1; /* if
photon not yet terminated */
short DEAD =
0; /* if
photon is to be terminated */
double THRESHOLD = 0.0001; /*
used in roulette */
double CHANCE = 0.1; /*
used in roulette */
/* Variable parameters */
double mut,
albedo, absorb, rsp, Rsptot, Atot;
double rnd,
xfocus;
double x,y,z,
ux,uy,uz,uz1, uxx,uyy,uzz, s,r,W,temp;
double psi,costheta,sintheta,cospsi,sinpsi;
long iphoton, ir, iz, CNT;
short photon_status;
/**** INITIALIZATIONS *****/
RandomGen(0,1,NULL); /* initiate with seed = 1, or any long
integer. */
CNT = 0;
mut = mua
+ mus;
albedo = mus/mut;
Rsptot = 0.0; /* accumulate absorbed photon weight */
Atot = 0.0; /*
accumulate specular reflectance per photon */
/* initialize arrays to zero */
for (ir=1; ir<=NR; ir++) {
J[ir]
= 0.0;
for
(iz=1; iz<=NZ; iz++)
F[iz][ir]
= 0.0;
}
/*============================================================
======================= RUN N photons =====================
* Launch N photons,
initializing each one before progation.
============================================================*/
for (iphoton=1; iphoton<=Nphotons; iphoton++) {
/*
Print out progress for user if mcflag < 3 */
temp
= (double)iphoton;
if
((mcflag < 3) & (temp >= 1000)) {
if
(temp<10000) {
if
(fmod(temp,1000)==0)
printf("%1.0f
photons\n",temp);
}
else
if (temp<100000) {
if
(fmod(temp,10000)==0)
printf("%1.0f photons\n",temp);
}
else
if (temp<1000000) {
if
(fmod(temp,100000)==0)
printf("%1.0f photons\n",temp);
}
else
if (temp<10000000) {
if
(fmod(temp,1000000)==0)
printf("%1.0f photons\n",temp);
}
else
if (temp<100000000) {
if
(fmod(temp,10000000)==0)
printf("%1.0f
photons\n",temp);
}
}
/**** LAUNCH
Initialize
photon position and trajectory.
Implements an
isotropic point source.
*****/
if (mcflag == 0) {
/*
UNIFORM COLLIMATED BEAM INCIDENT AT SURFACE */
/*
Launch at (r,z) = (radius*sqrt(rnd), 0).
* Due to cylindrical symmetry, radial
launch position is
* assigned to x while y = 0.
* radius = radius of uniform beam. */
/*
Initial position */
rnd
= RandomGen(1,0,NULL);
x
= radius*sqrt(rnd);
y
= 0;
z
= 0;
/*
Initial trajectory as cosines */
ux
= 0;
uy
= 0;
uz
= 1;
/*
specular reflectance */
temp = n1/n2; /* refractive index
mismatch, internal/external */
temp = (1.0 - temp)/(1.0 + temp);
rsp = temp*temp; /* specular
reflectance at boundary */
}
else if (mcflag == 1) {
/*
GAUSSIAN BEAM AT SURFACE */
/*
Launch at (r,z) = (radius*sqrt(-log(rnd)), 0).
* Due to cylindrical symmetry, radial
launch position is
* assigned to x while y = 0.
* radius = 1/e radius of Gaussian beam
at surface.
* waist = 1/e radius of Gaussian focus.
* zfocus = depth of focal point. */
/*
Initial position */
while
((rnd = RandomGen(1,0,NULL)) <= 0.0); /* avoids rnd = 0 */
x
= radius*sqrt(-log(rnd));
y
= 0.0;
z
= 0.0;
/*
Initial trajectory as cosines */
/*
Due to cylindrical symmetry, radial launch trajectory is
* assigned to ux and uz while uy = 0. */
xfocus
= waist/beam*x;
temp
= sqrt((x - xfocus)*(x - xfocus) + zfocus*zfocus);
sintheta
= -(x - xfocus)/temp;
costheta
= zfocus/temp;
ux
= sintheta;
uy
= 0.0;
uz
= costheta;
/*
specular reflectance and diffraction */
rsp
= RFresnel(n2, n1, costheta, &uz); /* new uz */
ux = -sqrt(1.0 - uz*uz);
/* new ux */
}
else {
/*
ISOTROPIC POINT SOURCE AT POSITION xs,ys,zs */
/*
Initial position */
x
= xs;
y
= ys;
z
= zs;
/*
Initial trajectory as cosines */
costheta
= 1.0 - 2.0*RandomGen(1,0,NULL);
sintheta
= sqrt(1.0 - costheta*costheta);
psi
= 2.0*PI*RandomGen(1,0,NULL);
cospsi
= cos(psi);
if
(psi < PI)
sinpsi
= sqrt(1.0 - cospsi*cospsi);
else
sinpsi
= -sqrt(1.0 - cospsi*cospsi);
ux
= sintheta*cospsi;
uy
= sintheta*sinpsi;
uz
= costheta;
/*
specular reflectance */
rsp
= 0.0;
}
W
= 1.0 - rsp; /* set photon
initial weight */
Rsptot += rsp; /* accumulate
specular reflectance per photon */
photon_status = ALIVE;
/******************************************
****** HOP_ESCAPE_SPINCYCLE **************
* Propagate one photon until it dies by ESCAPE or ROULETTE.
*******************************************/
do {
/**** HOP
* Take step to new
position
* s = stepsize
* ux, uy, uz are
cosines of current photon trajectory
*****/
while
((rnd = RandomGen(1,0,NULL)) <= 0.0); /* avoids rnd = 0 */
s
= -log(rnd)/mut; /* Step
size. Note: log() is base e */
x
+= s*ux; /*
Update positions. */
y
+= s*uy;
z
+= s*uz;
/*
Does photon ESCAPE at surface? ... z <= 0? */
if (z <= 0) {
rnd
= RandomGen(1,0,NULL);
/*
Check Fresnel reflectance at surface boundary */
if
(rnd > RFresnel(n1, n2, -uz, &uz1)) {
/*
Photon escapes at external angle, uz1 = cos(angle) */
x
-= s*ux;
/* return to original position */
y
-= s*uy;
z
-= s*uz;
s = fabs(z/uz); /* calculate stepsize to
reach surface*/
x
+= s*ux;
/* partial step to reach surface */
y
+= s*uy;
r
= sqrt(x*x + y*y); /* find
radial position r */
ir
= (long)(r/dr) + 1; /* round to 1 <= ir */
if
(ir > NR) ir = NR; /* ir = NR
is overflow bin */
J[ir]
+= W; /*
increment escaping flux */
photon_status
= DEAD;
}
else
z = -z; /* Total internal
reflection. */
}
if (photon_status ==
ALIVE) {
/*********************************************
****** SPINCYCLE = DROP_SPIN_ROULETTE
******
*********************************************/
/****
DROP
* Drop photon weight (W) into local bin.
*****/
absorb
= W*(1 - albedo); /* photon weight
absorbed at this step */
W
-= absorb;
/* decrement WEIGHT by amount absorbed */
Atot
+= absorb; /*
accumulate absorbed photon weight */
/*
deposit power in cylindrical coordinates z,r */
r = sqrt(x*x + y*y); /* current
cylindrical radial position */
ir
= (long)(r/dr) + 1; /* round to 1 <= ir
*/
iz
= (long)(fabs(z)/dz) + 1; /* round
to 1 <= iz */
if
(ir >= NR) ir = NR; /* last bin is for
overflow */
if
(iz >= NZ) iz = NZ; /* last bin is for
overflow */
F[iz][ir]
+= absorb; /* DROP
absorbed weight into bin */
/****
SPIN
* Scatter photon into new trajectory
defined by theta and psi.
* Theta is specified by cos(theta),
which is determined
* based on the Henyey-Greenstein
scattering function.
* Convert theta and psi into cosines ux,
uy, uz.
*****/
/*
Sample for costheta */
rnd
= RandomGen(1,0,NULL);
if
(g == 0.0)
costheta
= 2.0*rnd - 1.0;
else
if (g == 1.0)
costheta
= 1.0;
else
{
temp
= (1.0 - g*g)/(1.0 - g + 2*g*rnd);
costheta
= (1.0 + g*g - temp*temp)/(2.0*g);
}
sintheta
= sqrt(1.0 - costheta*costheta);/*sqrt faster than sin()*/
/*
Sample psi. */
psi
= 2.0*PI*RandomGen(1,0,NULL);
cospsi
= cos(psi);
if
(psi < PI)
sinpsi
= sqrt(1.0 - cospsi*cospsi); /*sqrt faster */
else
sinpsi
= -sqrt(1.0 - cospsi*cospsi);
/*
New trajectory. */
if
(1 - fabs(uz) <= 1.0e-12) { /*
close to perpendicular. */
uxx
= sintheta*cospsi;
uyy
= sintheta*sinpsi;
uzz
= costheta*((uz)>=0 ? 1:-1);
}
else
{ /* usually use this option
*/
temp
= sqrt(1.0 - uz*uz);
uxx
= sintheta*(ux*uz*cospsi - uy*sinpsi)/temp + ux*costheta;
uyy
= sintheta*(uy*uz*cospsi + ux*sinpsi)/temp + uy*costheta;
uzz
= -sintheta*cospsi*temp + uz*costheta;
}
/*
Update trajectory */
ux
= uxx;
uy
= uyy;
uz
= uzz;
/****
CHECK ROULETTE
* If photon weight below THRESHOLD, then
terminate photon using
* Roulette technique. Photon has CHANCE
probability of having its
* weight increased by factor of
1/CHANCE,
* and 1-CHANCE probability of
terminating.
*****/
if
(W < THRESHOLD) {
rnd
= RandomGen(1,0,NULL);
if
(rnd <= CHANCE)
W
/= CHANCE;
else
photon_status = DEAD;
}
}/**********************************************
**** END of SPINCYCLE =
DROP_SPIN_ROULETTE *
**********************************************/
}
while (photon_status == ALIVE);
/******************************************
****** END of
HOP_ESCAPE_SPINCYCLE ******
****** when
photon_status == DEAD) ******
******************************************/
/* If photon dead, then launch new photon. */
} /*======================= End RUN N photons =====================
=====================================================================*/
/************************
* NORMALIZE
* J[ir] escaping flux density [W/cm^2 per W
incident]
*
where bin = 2.0*PI*r[ir]*dr [cm^2].
* F[iz][ir]
fluence rate [W/cm^2 per W incident]
*
where bin = 2.0*PI*r[ir]*dr*dz [cm^3].
************************/
temp = 0.0;
for (ir=1; ir<=NR; ir++) {
r
= (ir - 0.5)*dr;
temp
+= J[ir]; /* accumulate total escaped
photon weight */
J[ir]
/= 2.0*PI*r*dr*Nphotons;
/* flux density */
for
(iz=1; iz<=NZ; iz++)
F[iz][ir]
/= 2.0*PI*r*dr*dz*Nphotons*mua; /* fluence rate */
}
if (mcflag < 2) {
printf("Specular
= %5.6f\n", Rsptot/Nphotons);
printf("Absorbed
= %5.6f\n", Atot/Nphotons);
printf("Escaped = %5.6f\n", temp/Nphotons);
printf("total = %5.6f\n", (Rsptot +
Atot + temp)/Nphotons);
}
} /******** END SUBROUTINE **********/
/***********************************************************
* FRESNEL REFLECTANCE
* Computes reflectance
as photon passes from medium 1 to
* medium 2 with
refractive indices n1,n2. Incident
* angle a1 is
specified by cosine value ca1 = cos(a1).
* Program returns
value of transmitted angle a1 as
* value in *ca2_Ptr =
cos(a2).
****/
double RFresnel(double n1, /* incident refractive index.*/
double
n2, /* transmit
refractive index.*/
double
ca1, /* cosine of the
incident */
/* angle
a1, 0<a1<90 degrees. */
double
*ca2_Ptr) /* pointer to the cosine */
/* of the
transmission */
/* angle
a2, a2>0. */
{
double r;
if(n1==n2) { /** matched boundary. **/
*ca2_Ptr
= ca1;
r
= 0.0;
}
else if(ca1>(1.0 - 1.0e-12)) { /** normal incidence. **/
*ca2_Ptr
= ca1;
r
= (n2-n1)/(n2+n1);
r
*= r;
}
else if(ca1< 1.0e-6)
{ /** very slanted. **/
*ca2_Ptr
= 0.0;
r
= 1.0;
}
else { /**
general. **/
double
sa1, sa2; /* sine of incident and transmission angles. */
double
ca2; /*
cosine of transmission angle. */
sa1
= sqrt(1-ca1*ca1);
sa2
= n1*sa1/n2;
if(sa2>=1.0)
{
/*
double check for total internal reflection. */
*ca2_Ptr
= 0.0;
r
= 1.0;
}
else
{
double
cap, cam; /* cosines of sum ap or
diff am of the two */
/*
angles: ap = a1 + a2, am = a1 - a2. */
double
sap, sam; /* sines. */
*ca2_Ptr
= ca2 = sqrt(1-sa2*sa2);
cap
= ca1*ca2 - sa1*sa2; /* c+ = cc - ss. */
cam
= ca1*ca2 + sa1*sa2; /* c- = cc + ss. */
sap
= sa1*ca2 + ca1*sa2; /* s+ = sc + cs. */
sam
= sa1*ca2 - ca1*sa2; /* s- = sc - cs. */
r
= 0.5*sam*sam*(cam*cam+cap*cap)/(sap*sap*cam*cam);
/*
rearranged for speed. */
}
}
return(r);
} /******** END SUBROUTINE **********/
/***********************************************************
* SAVE RESULTS TO
FILES
* to files named
≥Ji.dat≤ and ≥Fi.dat≤ where i = mcflag.
* Saves
≥Ji.dat≤ in following format:
* Saves r[ir] values in first column, (2:NR,1) = (rows,cols).
* Saves Ji[ir] values in second
column, (2:NR,2) = (rows,cols).
* Saves
≥Fi.dat≤ in following format:
* The upper element (1,1) is filled
with zero, and ignored.
* Saves z[iz] values in first
column, (2:NZ,1) = (rows,cols).
* Saves r[ir] values in first
row, (1,2:NZ) =
(rows,cols).
* Saves Fi[iz][ir] in (2:NZ, 2:NR).
***********************************************************/
void SaveFile( double
*R, double **F, int Nfile,
long
NR, long NZ, double dr, double dz)
{
double r,
z, r1, r2;
long ir, iz;
char name[20];
FILE* target;
/* SAVE flux density J(r) */
sprintf(name, "mcJ%d.dat",Nfile);
target = fopen(name, "w");
for (ir=1; ir<=NR; ir++) {
r2
= ir*dr;
r1
= (ir-1)*dr;
r
= 2.0/3*(r2*r2 + r2*r1 + r1*r1)/(r1 + r2);
fprintf(target,
"%5.5f \t%5.12f\n", r, R[ir]);
}
fclose(target);
/* SAVE fluence rate F(z,r) */
sprintf(name, "mcF%d.dat",Nfile);
target = fopen(name, "w");
fprintf(target, "%5.5f", 0.0); /* ignore upperleft
element of matrix */
for (ir=1; ir<=NR; ir++) {
r2
= ir*dr;
r1
= (ir-1)*dr;
r
= 2.0/3*(r2*r2 + r2*r1 + r1*r1)/(r1 + r2);
fprintf(target,
"\t %5.5f", r);
}
fprintf(target, "\n");
for (iz=1; iz<=NZ; iz++) {
z
= (iz - 0.5)*dz; /* z values for depth position in 1st column */
fprintf(target,
"%5.5f", z);
for
(ir=1; ir<=NR; ir++)
fprintf(target,
"\t %5.12f", F[iz][ir]);
fprintf(target,
"\n");
}
fclose(target);
} /******** END SUBROUTINE **********/
/*********************************************************************
* RANDOM NUMBER
GENERATOR
* A random number
generator that generates uniformly
* distributed
random numbers between 0 and 1 inclusive.
* The algorithm
is based on:
* W.H. Press,
S.A. Teukolsky, W.T. Vetterling, and B.P.
* Flannery,
"Numerical Recipes in C," Cambridge University
* Press, 2nd
edition, (1992).
* and
* D.E. Knuth,
"Seminumerical Algorithms," 2nd edition, vol. 2
* of "The
Art of Computer Programming", Addison-Wesley, (1981).
*
* When Type is 0,
sets Seed as the seed. Make sure 0<Seed<32000.
* When Type is 1,
returns a random number.
* When Type is 2,
gets the status of the generator.
* When Type is 3,
restores the status of the generator.
*
* The status of
the generator is represented by Status[0..56].
* Make sure you
initialize the seed before you get random
* numbers.
****/
#define MBIG 1000000000
#define MSEED 161803398
#define MZ 0
#define FAC 1.0E-9
double RandomGen(char Type, long Seed, long *Status)
{
static long i1, i2, ma[56]; /* ma[0] is not used. */
long mj, mk;
short i, ii;
if (Type == 0) { /* set seed. */
mj
= MSEED - (Seed < 0 ? -Seed : Seed);
mj
%= MBIG;
ma[55]
= mj;
mk
= 1;
for
(i = 1; i <= 54; i++) {
ii
= (21 * i) % 55;
ma[ii]
= mk;
mk
= mj - mk;
if
(mk < MZ)
mk
+= MBIG;
mj = ma[ii];
}
for
(ii = 1; ii <= 4; ii++)
for
(i = 1; i <= 55; i++) {
ma[i]
-= ma[1 + (i + 30) % 55];
if
(ma[i] < MZ)
ma[i]
+= MBIG;
}
i1
= 0;
i2
= 31;
}
else if (Type == 1) { /* get a number. */
if
(++i1 == 56)
i1
= 1;
if
(++i2 == 56)
i2
= 1;
mj
= ma[i1] - ma[i2];
if
(mj < MZ)
mj
+= MBIG;
ma[i1]
= mj;
return
(mj * FAC);
}
else if (Type == 2) { /* get status. */
for
(i = 0; i < 55; i++)
Status[i]
= ma[i + 1];
Status[55]
= i1;
Status[56]
= i2;
}
else if (Type == 3) { /* restore status. */
for
(i = 0; i < 55; i++)
ma[i
+ 1] = Status[i];
i1
= Status[55];
i2
= Status[56];
}
else
puts("Wrong
parameter to RandomGen().");
return (0);
}
#undef MBIG
#undef MSEED
#undef MZ
#undef FAC
/******* end
subroutine ******/
/***********************************************************
* MEMORY ALLOCATION
* REPORT ERROR MESSAGE
to stderr, then exit the program
* with signal 1.
****/
void nrerror(char error_text[])
{
fprintf(stderr,"%s\n",error_text);
fprintf(stderr,"...now exiting to system...\n");
exit(1);
}
/***********************************************************
* MEMORY ALLOCATION
* by Lihong Wang for
MCML version 1.0 code, 1992.
* ALLOCATE A 1D ARRAY with
index from nl to nh inclusive.
* Original matrix and vector
from Numerical Recipes in C
* don't initialize the elements
to zero. This will
* be accomplished by the
following functions.
****/
double *AllocVector(short nl, short nh)
{
double *v;
short i;
v=(double *)malloc((unsigned) (nh-nl+1)*sizeof(double));
if (!v) nrerror("allocation failure in vector()");
v -= nl;
for(i=nl;i<=nh;i++) v[i] = 0.0; /*
init. */
return v;
}
/***********************************************************
* MEMORY ALLOCATION
* ALLOCATE A 2D ARRAY with row
index from nrl to nrh
* inclusive, and column index
from ncl to nch inclusive.
****/
double **AllocMatrix(short nrl,short nrh,
short
ncl,short nch)
{
short i,j;
double **m;
m=(double **) malloc((unsigned) (nrh-nrl+1) *sizeof(double*));
if (!m) nrerror("allocation failure 1 in matrix()");
m
-= nrl;
for(i=nrl;i<=nrh;i++) {
m[i]=(double
*) malloc((unsigned) (nch-ncl+1) *sizeof(double));
if
(!m[i]) nrerror("allocation failure 2 in matrix()");
m[i]
-= ncl;
}
for(i=nrl;i<=nrh;i++)
for(j=ncl;j<=nch;j++)
m[i][j] = 0.0;
return m;
}
/***********************************************************
* MEMORY ALLOCATION
* RELEASE MEMORY FOR 1D ARRAY.
****/
void FreeVector(double *v,short nl,short nh)
{
free((char*) (v+nl));
}
/***********************************************************
* MEMORY ALLOCATION
* RELEASE MEMORY FOR 2D ARRAY.
****/
void FreeMatrix(double **m,short nrl,short nrh, short ncl,short
nch)
{
short i;
for(i=nrh;i>=nrl;i--) free((char*) (m[i]+ncl));
free((char*) (m+nrl));
}
[1] B.C. Wilson, G. Adam, “A Monte Carlo model fo rthe absorption and flux distributions of light in tissue,” Medical Physics 10:824-830 (1983).
[2] Keijzer M, SL Jacques, SA Prahl, AJ Welch: Light distributions in artery tissue: Monte Carlo simulations for finite-diameter laser beams. Lasers Surg.Med. 9:148-154, (1989)
[3] Keijzer M, R Richards-Kortum, SL Jacques, MS Feld: Fluorescence spectroscopy of turbid media: autofluorescence of the human aorta. Applied Optics 28:4286-4292, (1989)
[4] A.N. Witt, “Multiple scattering in reflection nebulae I. A Monte Carlo approach,” The Astrophysical Journal Supplement Series 35:1-6 (1977).
[5] S.A. Prahl, M. Keijzer, S.L. Jacques, A.J. Welch, “A Monte Carlo model of light propagation in tissue.,” in “Dosimetry of laser radiation in medicine and biology,” SPIE Institute Series Vol IS5:102-111, ed. G.J. Müller, D.H. Sliney (1989).
[6] L.-H. Wang, S.L. Jacques, “Monte Carlo modeling of light transport in multi-layered tissues in Standard C,”, publ. Univ. of Texas M. D. Anderson Cancer Center (1992). Download from http://
[7] L.-H. Wang, S. L. Jacques, L.-Q. Zheng: MCML - Monte Carlo modeling of photon transport in multi-layered tissues. Computer Methods and Programs in Biomedicine, 47, 131-146, 1995.
[8] S. L. Jacques: Light distributions from point, line, and plane sources for photochemical reactions and fluorescence in turbid biological tissues. Photochem. Photobiol. 67:23-32 (1998)
[9] T.J. Farrel, M.S. Patterson, B. Wilson, “A diffusion theory model of spatially resolved, steadh-state diffuse reflectance for the noninvasive determination of tissue optical properties in vivo,” Med. Phys. 19:881-888 (1992).
[10] S.A. Prahl, “Light Transport in Tissue,” Ph.D. dissertation, Univ. of Texas at Austin, Texas, (1988).
[11] H.C. van de Hulst, “Multiple Light Scattering Volume II,” Academic Press, New York (1980)