two questions in math

profileHonest139
ftp-uploads.zip

FTP-Uploads/.DS_Store

__MACOSX/FTP-Uploads/._.DS_Store

FTP-Uploads/ch02RayleighMie/Ch02Sec04Rayleigh.m

% Rayleigh scattering % Use SI units diameter=input('Diameter of sphere in nm(e.g., 20):')*1e-9; radius=diameter/2; lambda=input('Wavelength in nm(e.g., 400):')*1e-9; n_sphere=input('Refractive index of sphere (e.g., 1.57):'); n_background=input('Refractive index of background (e.g., 1.33):'); w_sphere=input('Specific weight of sphere(e.g., 1.05 g/cc):')*1e-3/(1e-2)^3; w_background=input('Specific weight of background(e.g., 1.0 g/cc):')*1e-3/(1e-2)^3; concentration=input('Concentration by weight (e.g., 1e-5):'); k=2*pi*n_background/lambda x=k*radius n_rel=n_sphere/n_background Qs = 8*x^4/3*abs((n_rel^2 - 1)/(n_rel^2 + 2))^2 sigma_s=Qs*pi*radius^2 vol_sphere = 4*pi/3*radius^3 N_s=concentration*w_background/(vol_sphere*w_sphere) mu_s=N_s*sigma_s % Output results {'wavelength[nm]','Qs [-]','mus [/cm]'; lambda*1e9, Qs, mu_s/1e2}

__MACOSX/FTP-Uploads/ch02RayleighMie/._Ch02Sec04Rayleigh.m

FTP-Uploads/ch02RayleighMie/Ch02Sec06Mie.m

% Mie theory % Use SI units diameter = input('Diameter of sphere in nm(e.g., 579):')*1e-9; radius = diameter/2; lambda = input('Wavelength in nm(e.g., 400):')*1e-9; n_s = input('Refractive index of sphere (e.g., 1.57):'); n_b = input('Refractive index of background (e.g., 1.33):'); w_s = input('Specific weight of sphere(e.g., 1.05 g/cc):')*1e3; w_b = input('Specific weight of background(e.g., 1.0 g/cc):')*1e3; concentration = input('Concentration by weight (e.g., 0.002):'); k = 2*pi*n_b/lambda x = k*radius n_rel = n_s/n_b y = n_rel*x % Calculate the summations err = 1e-8; Qs = 0; gQs = 0; for n = 1:100000 Snx = sqrt(pi*x/2)*besselj(n+0.5,x); Sny = sqrt(pi*y/2)*besselj(n+0.5,y); Cnx = -sqrt(pi*x/2)*bessely(n+0.5,x); Zetax = Snx+i*Cnx; % Calculate the first-order derivatives Snx_prime = - (n/x)*Snx+sqrt(pi*x/2)*besselj(n-0.5,x); Sny_prime = - (n/y)*Sny+sqrt(pi*y/2)*besselj(n-0.5,y); Cnx_prime = - (n/x)*Cnx-sqrt(pi*x/2)*bessely(n-0.5,x); Zetax_prime = Snx_prime + i*Cnx_prime; an_num = Sny_prime*Snx-n_rel*Sny*Snx_prime; an_den = Sny_prime*Zetax-n_rel*Sny*Zetax_prime; an = an_num/an_den; bn_num = n_rel*Sny_prime*Snx-Sny*Snx_prime; bn_den = n_rel*Sny_prime*Zetax-Sny*Zetax_prime; bn = bn_num/bn_den; Qs1 = (2*n+1)*(abs(an)^2+abs(bn)^2); Qs = Qs+Qs1; if n > 1 gQs1 = (n-1)*(n+1)/n*real(an_1*conj(an)+bn_1*conj(bn))... +(2*n-1)/((n-1)*n)*real(an_1*conj(bn_1)); gQs = gQs+gQs1; end an_1 = an; bn_1 = bn; if abs(Qs1)<(err*Qs) & abs(gQs1)<(err*gQs) break; end end Qs = (2/x^2)*Qs; gQs = (4/x^2)*gQs; g = gQs/Qs; vol_s = 4*pi/3*radius^3 N_s = concentration*w_b/(vol_s*w_s) sigma_s = Qs*pi*radius^2; mu_s = N_s*sigma_s mu_s_prime = mu_s*(1-g); % Output results {'wavelength[nm]','Qs [-]','g [-]','mus [/cm]','mus_prime[/cm]';... lambda*1e9,Qs,g,mu_s*1e-2,mu_s_prime*1e-2}

__MACOSX/FTP-Uploads/ch02RayleighMie/._Ch02Sec06Mie.m

FTP-Uploads/ch02RayleighMie/Ch02Sec06MieRayPlotLW.m

% Mie theory % Use SI units % n_s = input('Refractive index of sphere (e.g., 1.4):'); n_s = 1.40; % n_b = input('Refractive index of background (e.g., 1.33):'); n_b = 1.33; n_rel = n_s/n_b; Nx = 100; Qs_all = zeros(Nx); g_all = zeros(Nx); Qs_all_rayleigh = zeros(Nx); x_all = logspace(-1,3, Nx); err = 1e-11; for ix = 1:Nx x = x_all(ix); y = n_rel*x; % Calculate the summations Qs = 0; gQs = 0; for n = 1:100000 Snx = sqrt(pi*x/2)*besselj(n+0.5,x); Sny = sqrt(pi*y/2)*besselj(n+0.5,y); Cnx = -sqrt(pi*x/2)*bessely(n+0.5,x); Zetax = Snx+i*Cnx; % Calculate the first-order derivatives Snx_prime = - (n/x)*Snx+sqrt(pi*x/2)*besselj(n-0.5,x); Sny_prime = - (n/y)*Sny+sqrt(pi*y/2)*besselj(n-0.5,y); Cnx_prime = - (n/x)*Cnx-sqrt(pi*x/2)*bessely(n-0.5,x); Zetax_prime = Snx_prime + i*Cnx_prime; an_num = Sny_prime*Snx-n_rel*Sny*Snx_prime; an_den = Sny_prime*Zetax-n_rel*Sny*Zetax_prime; an = an_num/an_den; bn_num = n_rel*Sny_prime*Snx-Sny*Snx_prime; bn_den = n_rel*Sny_prime*Zetax-Sny*Zetax_prime; bn = bn_num/bn_den; Qs1 = (2*n+1)*(abs(an)^2+abs(bn)^2); Qs = Qs+Qs1; if n > 1 gQs1 = (n-1)*(n+1)/n*real(an_1*conj(an)+bn_1*conj(bn))... +(2*n-1)/((n-1)*n)*real(an_1*conj(bn_1)); gQs = gQs+gQs1; end an_1 = an; bn_1 = bn; if abs(Qs1)<(err*Qs) & abs(gQs1)<(err*gQs) {'ix = , n = ' ix, n} break; end end Qs = (2/x^2)*Qs; gQs = (4/x^2)*gQs; Qs_all(ix) = Qs; g_all(ix) = gQs/Qs; Qs_all_rayleigh(ix) = 8*x^4/3*abs((n_rel^2 - 1)/(n_rel^2 + 2))^2; end figure(1) subplot(2,1,1) i_rayleigh = find(x_all<3); loglog(x_all,Qs_all, 'k-', x_all(i_rayleigh), Qs_all_rayleigh(i_rayleigh), 'k:'); text(3, Qs_all_rayleigh(length(i_rayleigh)), 'Rayleigh theory') % legend('Mie', 'Rayleigh', 'Location', 'SouthEast') xlabel('\itx'); ylabel('Scattering efficiency \itQs'); subplot(2,1,2) semilogx(x_all,g_all, 'k-'); xlabel('\itx'); ylabel('Anisotropy \itg');

__MACOSX/FTP-Uploads/ch02RayleighMie/._Ch02Sec06MieRayPlotLW.m

__MACOSX/FTP-Uploads/._ch02RayleighMie

FTP-Uploads/ch03MonteCarlo/Authors web.url

[DEFAULT] BASEURL=http://labs.seas.wustl.edu/bme/Wang/mc.html [InternetShortcut] URL=http://labs.seas.wustl.edu/bme/Wang/mc.html Modified=308847F69B6DC701C5

__MACOSX/FTP-Uploads/ch03MonteCarlo/._Authors web.url

FTP-Uploads/ch03MonteCarlo/Mcman.pdf

Monte Carlo Modeling of Light Transport

in Multi-layered Tissues in Standard C

Lihong Wang, Ph. D.

Steven L. Jacques, Ph. D.

Laser Biology Research Laboratory

University of Texas M. D. Anderson Cancer Center

Supported by the Medical Free Electron Laser Program, the

Department of the Navy N00015-91-J-1354.

Copyright © University of Texas M. D. Anderson Cancer Center 1992

Monte Carlo Modeling of Light Transport

in Multi-layered Tissues in Standard C

Lihong Wang, Ph. D.

Steven L. Jacques, Ph. D.

Laser Biology Research Laboratory – 17

University of Texas M. D. Anderson Cancer Center

1515 Holcombe Blvd.

Houston, Texas 77030

Copyright © University of Texas

M. D. Anderson Cancer Center 1992

First printed August, 1992

Reprinted with corrections January, 1993 & November, 1995.

Abstract iii

Abstract

A Monte Carlo model of steady-state light transport in multi-layered tissue (mcml)

and the corresponding convolution program (conv) have been coded in ANSI Standard C.

The programs can therefore be executed on a variety of computers. Dynamic data

allocation is used for mcml, hence the number of tissue layers and the number of grid

elements of the grid system can be varied by users at run time as long as the total amount

of memory does not exceed what the system allows. The principle and the implementation

details of the model, and the instructions for using mcml and conv are presented here. We

have verified some of the mcml and conv computation results with those of other theories

or other investigators.

iv Acknowledgement

Acknowledgment

We would like to thank a group of people who have helped us with this package

directly or indirectly. Massoud Motamedi (University of Texas Medical Branch,

Galveston) has let us use his Sun SPARCstation 2. Scott A. Prahl (St. Vincent's Hospital,

Oregon) and Thomas J. Farrell (Hamilton Regional Cancer Center, Canada) have helped

us locate an insidious bug in the program. Craig M. Gardner (University of Texas, Austin)

provided us his Monte Carlo simulation and convolution results of a multi-layered

medium, which are compared with our results. We learned a lot from Marleen Keijzer and

Steven L. Jacques's Monte Carlo simulation program in PASCAL on Macintoshes.

Liqiong Zheng (University of Houston) has helped us greatly improve the speed of the

convolution program. They all deserve our thanks.

This work is supported by the Medical Free Electron Laser Program, the

Department of the Navy N00015-91-J-1354.

Table of Contents v

Table of Contents

Abstract ........................................................................................................... iii

Acknowledgment ............................................................................................. iv

0. Introduction ................................................................................................. 1

Part I. Description of Monte Carlo Simulation ..........................................4 1. The Problem and Coordinate Systems .......................................................... 4

2. Sampling Random Variables......................................................................... 7

3. Rules for Photon Propagation....................................................................... 11

3.1 Launching a photon packet ............................................................ 11

3.2 Photon's step size........................................................................... 12

3.3 Moving the photon packet ............................................................. 14

3.4 Photon absorption.......................................................................... 15

3.5 Photon scattering ........................................................................... 15

3.6 Reflection or transmission at boundary........................................... 17

3.7 Reflection or transmission at interface ............................................ 19

3.8 Photon termination ........................................................................ 20

4. Scored Physical Quantities ........................................................................... 22

4.1 Reflectance and transmittance ........................................................ 22

4.2 Internal photon distribution ............................................................ 24

4.3 Issues regarding grid system .......................................................... 27

5. Programming mcml ...................................................................................... 32

5.1 Programming rules and conventions ............................................... 32

5.2 Several constants ........................................................................... 33

5.3 Data structures and dynamic allocations ......................................... 34

5.4 Flowchart of photon tracing ........................................................... 38

5.5 Flow of the program mcml............................................................. 41

5.6 Multiple simulations....................................................................... 43

5.7 Timing profile of the program ........................................................ 43

6. Computation Results of mcml and Verification ............................................. 47

6.1 Total diffuse reflectance and total transmittance ............................. 47

6.2 Angularly resolved diffuse reflectance and transmittance ................ 48

6.3 Radially resolved diffuse reflectance............................................... 49

6.4 Depth resolved internal fluence ...................................................... 50

6.5 Computation times vs optical properties......................................... 52

6.6 Scored Physical Quantities of Multi-layered Tissues ....................... 62

vi Table of Contents

7. Convolution for Photon Beams of Finite Size................................................67

7.1 Principles of convolution ................................................................67

7.2 Convolution over Gaussian beams ..................................................71

7.3 Convolution over circularly flat beams............................................72

7.4 Numerical solution to the convolution ............................................73

7.5 Computation results of conv and verification ..................................80

Part II. User Manual..................................................................................87 8. Installing mcml and conv...............................................................................87

8.1 Installing on Sun workstations ........................................................87

8.2 Installing on IBM PC compatibles ..................................................88

8.3 Installing on Macintoshes ...............................................................88

8.4 Installing by Electronic Mail ...........................................................89

9. Instructions for mcml ....................................................................................91

9.1 File of input data ............................................................................91

9.2 Execution .......................................................................................93

9.3 File of output data ..........................................................................95

9.4 Subset of output data .....................................................................96

9.5 Bugs of mcml .................................................................................97

10. Instructions for conv ...................................................................................99

10.1 Start conv....................................................................................99

10.2 Main menu of conv......................................................................99

10.3 Command "i" of conv ..................................................................100

10.4 Command "b" of conv .................................................................100

10.5 Command "r" of conv..................................................................101

10.6 Command "e" of conv .................................................................101

10.7 Command "oo" of conv ...............................................................101

10.8 Command "oc" of conv ...............................................................103

10.9 Command "co" of conv ...............................................................104

10.10 Command "cc" of conv...............................................................105

10.11 Command "so" of conv...............................................................106

10.12 Command "sc" of conv ...............................................................107

10.13 Command "q" of conv ................................................................109

10.14 Bugs of conv ..............................................................................109

11. How to Modify mcml..................................................................................110

Appendices ................................................................................................113 Appendix A. Cflow Output of the Program mcml.............................................113

Table of Contents vii

Appendix B. Source Code of the Program mcml.............................................. 117

B.1 mcml.h .......................................................................................... 117

B.2 mcmlmain.c................................................................................... 121

B.3 mcmlio.c ....................................................................................... 125

B.4 mcmlgo.c ...................................................................................... 142

B.5 mcmlnr.c ....................................................................................... 154

Appendix C. Makefile for the Program mcml................................................... 156

Appendix D. A Template of mcml Input Data File ........................................... 157

Appendix E. A Sample Output Data File of mcml............................................ 158

Appendix F. Several C Shell Scripts ................................................................ 160

F.1 conv.bat for batch processing conv ................................................ 160

F.2 p1 for pasting files of 1D arrays ..................................................... 161

Appendix G. Where to Get the Programs mcml and conv ................................ 163

Appendix H. Future Developments of the Package .......................................... 164

References .................................................................................................166 Index ..........................................................................................................169

Chapter 0 Introduction 1

0. Introduction

Monte Carlo simulation has been used to solve a variety of physical problems.

However, there is no succinct well-established definition. We would like to adopt the

definition by Lux et al. (1991). In all applications of the Monte Carlo method, a

stochastic model is constructed in which the expected value of a certain random variable

(or of a combination of several variables) is equivalent to the value of a physical quantity

to be determined. This expected value is then estimated by the average of multiple

independent samples representing the random variable introduced above. For the

construction of the series of independent samples, random numbers following the

distribution of the variable to be estimated are used.

Monte Carlo simulations of photon propagation offer a flexible yet rigorous

approach toward photon transport in turbid tissues. The method describes local rules of

photon propagation that are expressed, in the simplest case, as probability distributions

that describe the step size of photon movement between sites of photon-tissue interaction,

and the angles of deflection in a photon's trajectory when a scattering event occurs. The

simulation can score multiple physical quantities simultaneously. However, the method is

statistical in nature and relies on calculating the propagation of a large number of photons

by the computer. As a result, this method requires a large amount of computation time.

The number of photons required depends largely on the question being asked, the

precision needed, and the spatial resolution desired. For example, to simply learn the total

diffuse reflectance from a tissue of specified optical properties, typically about 3,000

photons can yield a useful result. To map the spatial distribution of photons, φ(r, z), in a cylindrically symmetric problem, at least 10,000 photons are usually required to yield an

acceptable answer. To map spatial distributions in a more complex three-dimensional

problem such as a finite diameter beam irradiating a tissue with a buried blood vessel, the

required photons may exceed 100,000. The point to be remembered in these introductory

remarks is that Monte Carlo simulations are rigorous, but necessarily statistical and

therefore require significant computation time to achieve precision and resolution.

Nevertheless, the flexibility of the method makes Monte Carlo modeling a powerful tool.

Another aspect of the Monte Carlo simulations presented in this paper deserves

emphasis. The simulations described here do not treat the photon as a wave phenomenon,

and ignore such features as phase and polarization. The motivation for these simulations

2 Chapter 0 Introduction

is to predict radiant energy transport in turbid tissues. The photons are multiply scattered

by most tissues, therefore phase and polarization are quickly randomized, and play little

role in energy transport. Although the Monte Carlo simulations may be capable of

bookkeeping phase and polarization and treating wave phenomena statistically, this

manual will not consider these issues.

The Monte Carlo simulations are based on macroscopic optical properties that are

assumed to extend uniformly over small units of tissue volume. Mean free paths between

photon-tissue interaction sites typically range from 10-1000 µm, and 100 µm is a very

typical value in the visible spectrum (Cheong et al., 1990). The simulations do not treat

the details of radiant energy distribution within cells, for example.

As a simple example of the Monte Carlo simulation. We would like to present a

typical trajectory of a single photon packet in Fig. 0.1. Each step between photon positions (dots) is variable and equals –ln(ξ)/(µ a + µ s) where ξ is a random number and µa and µ s are the absorption and scattering coefficients, respectively (in this example, µa =

0.5 cm−1, µ s = 15 cm−1, g = 0.90). The value g is the anisotropy of scattering. The

weight of the photon is decreased from an initial value of 1 as it moves through the tissue, and equals an after n steps, where a is the albedo (a = µ s/(µ a + µ s)). When the photon

strikes the surface, a fraction of the photon weight escapes as reflectance and the

remaining weight is internally reflected and continues to propagate. Eventually, the

photon weight drops below a threshold level and the simulation for that photon is

terminated. In this example, termination occurred when the last significant fraction of

remaining photon weight escaped at the surface at the position indicated by the asterisk

(*). Many photon trajectories (104 to 106) are typically calculated to yield a statistical

description of photon distribution in the medium.

This manual is roughly divided into two major parts. Part I describes the principles

of Monte Carlo simulations of photon transport in tissues, how to realize the simulation in

ANSI Standard C, and some computation results and verifications. Part II provides users

detailed instructions of using mcml and modifying mcml to suit special need, where mcml

stands for Monte Carlo simulations for multi-layered tissues. The appendices furnish the

flow graph and the whole source code of mcml, and some other useful information.

Chapter 0 Introduction 3

2000

1500

1000

500

0

-1500 -1000

de pt

h z

(µ m

)

-500 0 500 1000 1500

*

position x (µm)

Figure 0.1. The movement of one photon through a homogenous medium,

as calculated by Monte Carlo simulation.

4 Chapter 1 The Problem and Coordinate Systems

Part I. Description of Monte Carlo Simulation

1. The Problem and Coordinate Systems

The Monte Carlo simulation described in this paper deals with the transport of an

infinitely narrow photon beam perpendicularly incident on a multi-layered tissue. Each

layer is infinitely wide, and is described by the following parameters: the thickness, the refractive index, the absorption coefficient µa, the scattering coefficient µs, and the

anisotropy factor g. The refractive indices of the top ambient medium (e.g., air) and

bottom ambient medium (if exists) have to be given as well. Although the real tissue can

never be infinitely wide, it can be so treated if it is much larger than the spatial extent of the photon distribution. The absorption coefficient µ a is defined as the probability of

photon absorption per unit infinitesimal pathlength, and the scattering coefficient µ s is

defined as the probability of photon scattering per unit infinitesimal pathlength. For the simplicity of notation, the total interaction coefficient µ t, which is the sum of the

absorption coefficient µa and the scattering coefficient µ s, is sometimes used.

Correspondingly, the interaction coefficient means the probability of photon interaction

per unit infinitesimal pathlength. The anisotropy g is the average of the cosine value of the

deflection angle (see Section 3.5).

Photon absorption, fluence, reflectance and transmittance are the physical

quantities to be simulated. The simulation propagates photons in three dimensions,

records photon deposition, A(x , y, z), (J/cm3 per J of delivered energy or cm−3) due to

absorption in each grid element of a spatial array, and finally calculates fluence, φ(x , y, z), (J/cm2 per J of delivered energy or cm−2) by dividing deposition by the local absorption coefficient, µa in cm−1: φ(x , y, z) = A(x , y, z)/µ a. Since the photon absorption and the

photon fluence can be converted back and forth through the local absorption coefficient of

the tissue, we only report the photon absorption in mcml (see Section 4.2 and Section

9.3). The photon fluence can be obtained by converting the photon absorption in another

program conv. The simulation also records the escape of photons at the top (and bottom)

surface as local reflectance (and transmittance) (cm–2sr–1) (see Section 4.1).

In this first version of mcml, we consider cylindrically symmetric tissue models.

Therefore, we chose to record photon deposition in a two-dimensional array, A(r, z)

although the photon propagation of this simulation is conducted in three-dimensions.

Chapter 1 The Problem and Coordinate Systems 5

Three coordinate systems are used in the Monte Carlo simulation at the same time.

A Cartesian coordinate system is used to trace photon packets. The origin of the

coordinate system is the photon incident point on the tissue surface, the z-axis is always

the normal of the surface pointing toward the inside of the tissue, and the xy-plane is

therefore on the tissue surface (Fig. 1.1). A cylindrical coordinate system is used to score

internal photon absorption A(r, z), where r and z are the radial and z axis coordinates of

the cylindrical coordinate system respectively. The Cartesian coordinate system and the

cylindrical coordinate system share the origin and the z axis. The r coordinate of the

cylindrical coordinate system is also used for the diffuse reflectance and total transmittance. They are recorded on tissue surface in Rd(r, α) and Tt(r, α) respectively,

where α is the angle between the photon exiting direction and the normal (–z axis for reflectance and z axis for transmittance) to the tissue surfaces. A moving spherical

coordinate system, whose z axis is aligned with the photon propagation direction

dynamically, is used for sampling of the propagation direction change of a photon packet.

In this spherical coordinate system, the deflection angle θ and the azimuthal angle ψ due to scattering are first sampled. Then, the photon direction is updated in terms of the

directional cosines in the Cartesian coordinate system (see Section 3.5).

For photon absorption, a two-dimensional homogeneous grid system is setup in z

and r directions. The grid line separations are ∆z and ∆r in z and r directions respectively. The total numbers of grid elements in z and r directions are Nz and Nr respectively. For

diffuse reflectance and transmittance, a two-dimensional homogeneous grid system is

setup in r and α directions. This grid system can share the r direction with the grid system for photon absorption. Therefore, we only need to set up an extra one dimensional grid

system for the diffuse reflectance and transmittance in the α direction. In our simulation, we always choose the range of α to be [0, π/2], i.e., 0 ≤ α ≤ π/2. The total number of grid elements is Nα. Therefore the grid line separation is ∆α = π/(2 Nα).

This is an appropriate time to mention that we always use cm as the basic unit of

length throughout the simulation for consistency. For example, the thickness of each layer

and the grid line separations in r and z directions are in cm. The absorption coefficient and

scattering coefficient are in cm−1.

6 Chapter 1 The Problem and Coordinate Systems

y

z

x

Photon Beam

Layer 1

Layer N

Layer 2

Fig. 1.1. A schematic of the Cartesian coordinate system set up on multi-

layered tissues. The y-axis points outward.

In some of the discussions, the arrays will simply be referenced by the location of

the grid element, e.g., (r, z) or (r, α), rather than by the indices of the grid element, although the indices are used in the program to reference array elements.

Chapter 2 Sampling Random Variables 7

2. Sampling Random Variables

The Monte Carlo method, as its name implies ("throwing the dice"), relies on the

random sampling of variables from well-defined probability distributions. Several books

(Cashew et al., 1959; Lux et al., 1991; and Kalos et al., 1986) provide good references

for the principles of Monte Carlo modeling. Let us briefly review the method for sampling

random variables in a Monte Carlo simulation.

Consider a random variable χ, which is needed by the Monte Carlo simulation of photon propagation in tissue. This variable may be the variable step size a photon will

take between photon-tissue interaction sites, or the angle of deflection a scattered photon

may experience due to a scattering event. There is a probability density function that

defines the distribution of χ over the interval (a, b). The probability density function is normalized such that:

⌡⌠ a

b p(χ) dχ = 1 (2.1)

To simulate propagation, we wish to be able to choose a value for χ repeatedly and randomly based on a pseudo-random number generator. The computer provides a

random variable, ξ, which is uniformly distributed over the interval (0, 1). The cumulative distribution function of this uniformly distributed random variable is:

Fξ(ξ) =    0 if ξ ≤ 0

ξ if 0 < ξ ≤ 1 1 if ξ > 1

(2.2)

To sample a generally non-uniformly distributed function p(χ), we assume there exists a nondecreasing function χ = f(ξ) (Kalos et al., 1986), which maps ξ ∈ (0, 1) to χ ∈ (a, b) (Fig. 2.1). The variable χ and variable ξ then have a one-to-one mapping. This subsequently leads to the following equality of probabilities:

P{f(0) < χ ≤ f(ξ1)} = P{0 < ξ ≤ ξ1} (2.3a)

or

P{a < χ ≤ χ1} = P{0 < ξ ≤ ξ1} (2.3b)

8 Chapter 2 Sampling Random Variables

According to the definition of cumulative distribution functions, Eq. 2.3b can be changed

to an equation of cumulative distribution functions:

Fχ(χ1) = Fξ(ξ1) (2.4)

Expanding the cumulative distribution function Fχ(χ1) in terms of the corresponding

probability density function for the left-hand side of Eq. 2.4 and employing Eq. 2.2 for the

right-hand side, we convert Eq. 2.4 into:

⌡⌠ a

χ1 p(χ) dχ = ξ1 for ξ1 ∈ (0, 1) (2.5)

Eq. 2.5 is then used to solve for χ1 to get the function f(ξ1). If the function χ =

f(ξ) is assumed nonincreasing, a similar derivation will lead to the counterpart of Eq. 2.5 as:

⌡⌠ a

χ1 p(χ) dχ = 1 – ξ1 for ξ1 ∈ (0, 1) (2.6)

However, since (1 − ξ1) and ξ1 have the same distribution, they can be interchanged.

Therefore, Eq. 2.5 and Eq. 2.6 are equivalent. In the following chapter, Eq. 2.5 will be

repeatedly invoked for sampling propagation variables.

The whole sampling process can be understood from Fig. 2.1. The key to the

Monte Carlo selection of χ using ξ is to equate the probability that ξ is in the interval [0, ξ1] with the probability that χ is in the interval [a, χ1]. In Fig. 2.1, we are equating the shaded area depicting the integral of p(χ) over [0, χ1] with the shaded area depicting the integral p(ξ) over [0, ξ1]. Keep in mind that the total areas under the curves p(χ) and p(ξ)

each equal unity, as is appropriate for probability density functions. The result is a one-to- one mapping between the upper boundaries ξ1 and χ1 based on the equality of the shaded areas in Fig. 2. In other words, we have equated Fχ(χ1) with Fξ(ξ1) (Eq. 2.4) which is equivalent to Eq. 2.5. The transformation process χ1 = f(ξ1) is shown by the arrows. For each ξ1, a χ1 is chosen such that the cumulative distribution functions for ξ1 and χ1 have

the same value. Correspondingly, the hatched areas are equal. It can also be seen in Fig.

2.1 that the monotonic function f(ξ) always exists because both cumulative distribution functions of ξ and χ are monotonic.

Chapter 2 Sampling Random Variables 9

p(ξ)

F (ξ)

0 a b

p(χ)

0

1

a bχ

0

1

0 1

ξ

0

1

0 1ξ

ξ F (χ)χ

χ1ξ1

χ f

Fig. 2.1. Sampling a random variable χ based on a uniformly distributed random variable ξ.

For example, consider the sampling of the step size for photon movement, s, which

is to be discussed fully in Section 3.2. The probability density function is given:

p(s) = µ t exp(–µ ts) (2.7)

where interaction coefficient µt equals µa + µ s. Using this function in Eq. 2.5 yields an

expression for a sampled value, s1, based on the random number ξ:

ξ = ⌡⌠ 0

s1 p(s) ds = ⌡⌠

0

s1 µ texp(–µ ts) ds = 1 – exp(µ ts1) (2.8)

Solving for the value s1:

s1 = –ln(1 – ξ)

µ t (2.9a)

As was explained in Eq. 2.6, the above expression is equivalent to:

10 Chapter 2 Sampling Random Variables

s1 = –ln(ξ)

µ t (2.9b)

Chapter 3 Rules for Photon Propagation 11

3. Rules for Photon Propagation

This chapter presents the rules that define photon propagation in the Monte Carlo

model as applied to tissues. The treatment is based upon Prahl et al. (1989) except that

we deal with a multi-layered tissue instead of a semi-infinite tissue.

3.1 Launching a photon packet

A simple variance reduction technique, implicit photon capture, is used to improve

the efficiency of the Monte Carlo simulation. This technique allows one to equivalently

propagate many photons as a packet along a particular pathway simultaneously. Each

photon packet is initially assigned a weight, W, equal to unity. The photon is injected

orthogonally into the tissue at the origin, which corresponds to a collimated arbitrarily

narrow beam of photons.

The current position of the photon is specified by the Cartesian coordinates (x, y,

z). The current photon direction is specified by a unit vector, r, which can be equivalently described by the directional cosines (µ x, µ y, µ z):

µ x = r • x

µ y = r • y

µ z = r • z

(3.1)

where x, y, and z are unit vectors along each axis. The photon position is initialized to (0,

0, 0,) and the directional cosines are set to (0, 0, 1). This description of photon position

and direction in a Cartesian coordinate system (Witt, 1977) turned out to be simpler than

the counterpart in a cylindrical coordinate system (Keijzer et al., 1989).

When the photon is launched, if there is a mismatched boundary at the tissue

surface, then some specular reflectance will occur. If the refractive indices of the outside medium and tissue are n1 and n2, respectively, then the specular reflectance, Rsp, is

specified (Born et al., 1986; Hecht, 1987):

Rsp = (n1 – n2)2

(n1 + n2)2 (3.2a)

12 Chapter 3 Rules for Photon Propagation

If the first layer is glass, which is on top of a layer of medium whose refractive index is n3,

multiple reflections and transmissions on the two boundaries of the glass layer are

considered. The specular reflectance is then computed by:

Rsp = r1 + (1–r1)2 r2 1–r1 r2

(3.2b)

where r1 and r2 are the Fresnel reflectances on the two boundaries of the glass layer:

r1 = (n1 – n2)2

(n1 + n2)2 (3.3)

r2 = (n3 – n2)2

(n3 + n2)2 (3.4)

Note that if the specular reflectance is defined as the probability of photons being

reflected without interactions with the tissue, then Eqs. 3.2a and 3.2b are not strictly

correct although they may be very good estimates of the real specular reflectance for thick

tissues. If we want to strictly distinguish the specular reflectance and the diffuse

reflectance, we can keep track of the number of interactions experienced by a photon

packet. When we score the reflectance, if the number of interactions is not zero, the

reflectance is diffuse reflectance. Otherwise, it is specular reflectance. The transmittances

can be distinguished similarly.

The photon weight is decremented by Rsp, and the specular reflectance Rsp will be

reported to the file of output data.

W = 1 – Rsp (3.5)

3.2 Photon's step size

The step size of the photon packet is calculated based on a sampling of the

probability distribution for photon's free path s ∈ [0, ∞), which means 0 ≤ s < ∞. According to the definition of interaction coefficient µ t, the probability of interaction per

unit pathlength in the interval (s', s' + ds') is:

µ t = – dP{s ≥ s'} P{s ≥ s'} ds'

(3.6a)

Chapter 3 Rules for Photon Propagation 13

or

d(ln(P{s ≥ s'})) = – µ t ds' (3.6b)

The above Eq. 3.6b can be integrated over s' in the range (0, s1), and lead to an

exponential distribution, where P{s ≥ 0} = 1 is used:

P{s ≥ s1} = exp(– µ t s1) (3.7)

Eq. 3.7 can be rearranged to yield the cumulative distribution function of free path s:

P{s < s1} = 1 – exp(– µ t s1) (3.8)

This cumulative distribution function can be assigned to the uniformly distributed random

number ξ as discussed in Chapter 2. The equation can be rearranged to provide a means of choosing step size:

s1 = –ln(1 –ξ)

µ t (3.9a)

or substituting ξ for (1–ξ):

s1 = –ln(ξ)

µ t (3.9b)

Eq. 3.9b gives a mean free path between photon-tissue interaction sites of 1/µ t because the

statistical average of –ln(ξ) is 1, i.e., <–ln (ξ)> = 1. There is another approach to obtain Eq. 3.9b. Employing Eq. 3.8, the probability density function of free path s is:

p(s1) = dP{s < s1}/ds1 = µ t exp(– µ t s1) (3.10)

p(s1) can be substituted into Eq. 2.5 to yield Eq. 3.9b, where the integration in Eq. 2.5 will

be recovered to Eq. 3.8.

In multi-layered turbid media, the photon packet may experience free flights over

multiple layers of media before an interaction occurs. In this case, the counterpart of Eq.

3.7 becomes:

P{s ≥ ssum} = exp(– ∑ i

µ ti si ) (3.11)

14 Chapter 3 Rules for Photon Propagation

where i is an index to a layer, the symbols µti is the interaction coefficient for the ith layer,

and si is the step size in the ith layer. The total step size ssum is:

ssum = ∑ i

si (3.12)

The summation is over all the layers in which the photon packet has traveled. Eq. 3.11

does not take photon reflection and transmission at boundaries into account because they

are processed separately. The sampling equation is obtained by equating Eq. 3.11 to ξ:

∑ i

µ ti si = – ln(ξ) (3.13)

As you may have seen, Eq. 3.9b is just a special case of Eq. 3.13. The sampling can be

interpreted as that the total dimensionless step size is –ln(ξ), where dimensionless step size is defined as the product of the dimensional step size si and the interaction coefficient µti.

Since the absorption coefficient and the scattering coefficient of a glass layer are zeros, it

does not contribute to the left hand side of Eq. 3.13. The detailed process of Eq. 3.13 will

be discussed in Section 3.6 and 3.7. Although Eq. 3.13 looks complicated, it is the

theoretical ground for the process in Section 3.6 and 3.7 which looks simpler.

From now on, we will use step size s instead of s1 or ssum for simplicity. Note that

this sampling method involves computation of a logarithm function, which is time-

consuming. This is reflected in Section 5.7. Faster methods can be used to avoid the

logarithmic computation (Ahrens et al., 1972; Marsaglia, 1961; MacLaren et al., 1964).

3.3 Moving the photon packet

Once the step size s is specified, the photon is ready to be moved in the tissue.

The position of the photon packet is updated by:

x ← x + µ x s

y ← y + µy s

z ← z + µ z s (3.14)

The arrows indicate quantity substitutions. The variables on the left hand side have the

new values, and the variables on the right hand side have the old values. In an actual

Chapter 3 Rules for Photon Propagation 15

program in C, an equal sign is used for this purpose. The simplicity of Eqs. 3.14 is a

major reason for using Cartesian coordinates.

3.4 Photon absorption

Once the photon has taken a step, some attenuation of the photon weight due to

absorption by the interaction site must be calculated. A fraction of the photon's current

weight, W, will be deposited in the local grid element. The amount of deposited photon

weight, ∆W, is calculated:

∆W = W µ a µ t

(3.15)

The total accumulated photon weight A(r, z) deposited in that local grid element is

updated by adding ∆W:

A(r, z) ← A(r, z) + ∆W (3.16)

The photon weight has to be updated as well by:

W ← W – ∆W (3.17)

The photon packet with the new weight W will suffer scattering at the interaction

site (discussed later). Note that the whole photon packet experiences interaction at the

end of the step, either absorption or scattering.

3.5 Photon scattering

Once the photon packet has been moved, and its weight decremented, the photon

packet is ready to be scattered. There will be a deflection angle, θ ∈ [0, π), and an azimuthal angle, ψ ∈ [0, 2 π) to be sampled statistically. The probability distribution for the cosine of the deflection angle, cosθ, is described by the scattering function 1) that Henyey and Greenstein (1941) originally proposed for galactic scattering:

p(cosθ) = 1 – g2

2 (1 + g2 – 2gcosθ)3/2 (3.18)

1) Note that the scattering function we defined here is a probability density function of cosθ. It has a

difference of a constant 1/2 with the phase function defined by van de Hulst (1980).

16 Chapter 3 Rules for Photon Propagation

where the anisotropy, g, equals <cosθ> and has a value between –1 and 1. A value of 0 indicates isotropic scattering and a value near 1 indicates very forward directed scattering.

Jacques et al. (1987) determined experimentally that the Henyey-Greenstein function

described single scattering in tissue very well. Values of g range between 0.3 and 0.98 for

tissues, but quite often g is ~0.9 in the visible spectrum. Applying Eq. 2.5, the choice for

cosθ can be expressed as a function of the random number, ξ:

cosθ =

   1

2g   

  

1 + g2 –   

  1 – g2

1 – g + 2g ξ

2

if g > 0

2 ξ – 1 if g = 0

(3.19)

Next, the azimuthal angle, ψ, which is uniformly distributed over the interval 0 to 2π, is sampled:

ψ = 2π ξ (3.20)

Once the deflection angle and azimuthal angle are chosen, the new direction of the

photon packet can be calculated:

µ'x = sinθ

1 – µ

2 z

(µ x µ z cosψ – µ y sinψ) + µ x cosθ

µ'y = sinθ

1 – µ

2 z

(µ y µ z cosψ + µ x sinψ) + µ y cosθ

µ'z = –sinθ cosψ

1 – µ 2 z + µ z cosθ

(3.21)

If the angle of the photon packet is too close to normal of the tissue surfaces(e.g., |µ z| >

0.99999), then the following formulas should be used:

µ'x = sinθ cosψ

µ'y = sinθ sinψ

µ'z = SIGN(µ z) cosθ

(3.22)

Chapter 3 Rules for Photon Propagation 17

where SIGN(µ z) returns 1 when µ z is positive, and it returns –1 when µ z is negative.

Finally, the current photon direction is updated: µ x = µ' x, µ y = µ' y, µ z = µ' z.

In the sampling of the two angles θ and ψ and the updating of the directional cosines, trigonometric operations are involved. Because trigonometric operations are

computation-intensive, we should try to avoid them whenever possible. The detailed

process of the sampling can be found in the function Spin() written in the file "mcmlgo.c"

(See Appendix A and Appendix B.4).

3.6 Reflection or transmission at boundary

During a step, the photon packet may hit a boundary of the tissue, which is

between the tissue and the ambient medium, where the step size s is computed by Eq.

3.9b. For example, the photon packet may attempt to escape the tissue at the air/tissue

interface. If this is the case, then the photon packet may either escape as observed

reflectance (or transmittance if a rear boundary is also included) or be internally reflected

by the boundary. There are different methods of dealing with this problem when the step

size is large enough to hit the boundary. Let us present one of the two approaches used in

the program mcml first.

First, a foreshortened step size s1 is computed:

s1 =    (z – z0)/µ z if µ z < 0 (z – z1)/µ z if µ z > 0

(3.23)

where z0 and z1 are the z coordinates of the upper and lower boundaries of the current

layer (See Fig. 1.1 for the Cartesian coordinate system). The foreshortened step size s1 is

the distance between the current photon location and the boundary in the direction of the photon propagation. Since the photon direction is parallel with the boundary when µ z is

zero, the photon will not hit the boundary. Therefore, Eq. 3.23 does not include the case when µz is zero. We move the photon packet s1 to the boundary with a flight free of

interactions with the tissue (see Section 3.3 for moving photon packet). The remaining step size to be taken in the next step is updated to s ← s – s1. The photon packet will

travel the remaining step size if being internally reflected.

Second, we compute the probability of a photon packet being internally reflected, which depends on the angle of incidence, αi, onto the boundary, where αi = 0 means orthogonal incidence. The value of αi is calculated:

18 Chapter 3 Rules for Photon Propagation

αi = cos–1(|µ z|) (3.24)

Snell's law indicates the relationship between the angle of incidence, αi, the angle of transmission, αt, and the refractive indices of the media that the photon is incident from, ni, and transmitted to, nt:

ni sinαi = nt sinαt (3.25)

The internal reflectance, R(αi), is calculated by Fresnel's formulas (Born et al., 1986;

Hecht, 1987):

R(αi) = 1 2 

 

  sin2(ai –at)

sin2(ai +at) +

tan2(ai –at)

tan2(ai +at) (3.26)

which is an average of the reflectances for the two orthogonal polarization directions.

Third, we determine whether the photon is internally reflected by generating a

random number, ξ, and comparing the random number with the internal reflectance, i.e.:

If ξ ≤ R(αi), then photon is internally reflected;

If ξ > R(αi), then photon escapes the tissue (3.27)

If the photon is internally reflected, then the photon packet stays on the surface and its directional cosines (µx, µ y, µ z) must be updated by reversing the z component:

(µ x, µ y, µ z) ← (µ x, µ y, –µ z) (3.28)

At this point, the remaining step size has to been checked again. If it is large

enough to hit the other boundary, we should repeat the above process. If it hits a

tissue/tissue interface, we will have to process it according to the following section.

Otherwise, if the step size is small enough to fit in this layer of tissue, the photon packet

will move with the small step size. At the end of this small step, the absorption and

scattering are processed correspondingly.

On the other hand, if the photon packet escapes the tissue, the reflectance or transmittance at the particular grid element (r, αt) must be incremented. The reflectance, Rd(r, αt), or transmittance, Tt(r, αt), is updated by the amount of escaped photon weight,

W:

Chapter 3 Rules for Photon Propagation 19

Rd(r, αt) ← Rd(r, αt) + W if z = 0

Tt(r, αt) ← Tt(r, αt) + W if z = the bottom of the tissue. (3.29)

Since the photon has completely escaped, the tracing of this photon packet ends here. A

new photon may be launched into the tissue and traced thereafter. Note that in our

simulation, both unscattered transmittance, if any, and diffuse transmittance are scored into Tt(r, αt) without distinction although they could be distinguished as we discussed in

Section 3.1.

An alternative approach toward modeling internal reflectance deserves mention.

Rather than making the internal reflection of the photon packet an all-or-none event, a

partial reflection approach can be used each time a photon packet strikes the surface boundary. A fraction 1 – R(αi) of the current photon weight successfully escapes the tissue, and increments the local reflectance or transmittance array, e.g., Rd(r, αt) ← Rd(r, αt) + W (1–R(αi)). All the rest of the photon weight will be reflected, and the photon weight is updated as W ← W R(αi).

Both approaches are available in the program mcml, and the users have the option

to use either approach depending on the physical quantities that they want to score. A

flag in the program can be changed to switch between these two approaches (see Section

5.2). The all-or-none approach is faster, but the partial reflection approach should be able

to reduce the variance of the reflectance or transmittance. It is uninvestigated how much

variance can be reduced by using the partial reflection approach.

Similar to Section 3.5, the number of trigonometric operations in Eqs. 3.24-3.26

should be minimized for the sake of computation speed. The detailed process of these

computations can be found in the function RFresnel() written in the file "mcmlgo.c" (See

Appendix A and Appendix B.4).

3.7 Reflection or transmission at interface

If a photon step size is large enough to hit a tissue/tissue interface, this step may

cross several layers of tissue. Consider a photon packet that attempts to make a step of size s within tissue 1 with µa1, µ s1, n1, but hits an interface with tissue 2 with µa2, µ s2, n2 after a foreshortened step s1. Similar to the discussion in the last section, the photon

packet is first moved to the interface without interactions, and the remaining photon step size to be taken in the next step is updated to s ← s – s1. Then, we have to determine

20 Chapter 3 Rules for Photon Propagation

statistically whether the photon packet should be reflected or transmitted according to the

Fresnel's formulas. If the photon packet is reflected, it is processed the same way as in the

last section. However, if the photon packet is transmitted to the next layer of tissue, it has

to continue propagation instead of being terminated. Based on Eq. 3.13, the remaining

step size has to be converted for the new tissue according to its optical properties:

s ← s µ t1 µ t2

(3.30)

where µ t1 and µ t2 are the interaction coefficients for tissue 1 and tissue 2 correspondingly.

The current step size s is again checked for another boundary or interface crossing. The

above process is repeated until the step size is small enough to fit in one layer of tissue.

At the end of this small step, the absorption and scattering are processed correspondingly.

If the photon packet is in a layer of glass, the photons are moved to the boundary

of the glass layer without updating the remaining photon step size because the path length

in the glass layer does not contribute to the left hand side of Eq. 3.13. It is important to

understand that if a photon packet traverses several layers of tissues, the use of Eq. 3.9b

for the step size and the repetitive uses of Eq. 3.30 are based on Eq. 3.13.

3.8 Photon termination

After a photon packet is launched, it can be terminated naturally by reflection or

transmission out of the tissue. For a photon packet that is still propagating inside the

tissue, if the photon weight, W, has been sufficiently decremented after many steps of

interaction such that it falls below a threshold value (e.g., Wth = 0.0001), then further

propagation of the photon yields little information unless you are interested in the very late

stage of the photon propagation. However, proper termination must be executed to

ensure conservation of energy (or number of photons) without skewing the distribution of

photon deposition. A technique called roulette is used to terminate the photon packet

when W ≤ Wth. The roulette technique gives the photon packet one chance in m (e.g., m = 10) of surviving with a weight of mW. If the photon packet does not survive the

roulette, the photon weight is reduced to zero and the photon is terminated.

W =   mW if ξ ≤ 1/m 0 if ξ > 1/m

(3.31)

Chapter 3 Rules for Photon Propagation 21

where ξ is the uniformly distributed pseudo-random number (see Chapter 2). This method conserves energy yet terminates photons in an unbiased manner. The combination of

photon roulette and splitting that is contrary to roulette, may be properly used to reduce

variance (Hendricks et al., 1985).

22 Chapter 4 Scored Physical Quantities

4. Scored Physical Quantities

As we mentioned earlier, we record the photon reflectance, transmittance, and

absorption during the Monte Carlo simulation. In this chapter, we will discuss the

process of these physical quantities in detail. The dimensions of some of the quantities are

shown in square brackets at the end of their formulas.

The last cells in z and r directions require special attention. Because photons can

propagate beyond the grid system, when the photon weight is recorded into the diffuse

reflectance or transmittance array, or absorption array, the physical location may not fit

into the grid system. In this case, the last cell in the direction of the overflow is used to

collect the photon weight. Therefore, the last cell in the z and r directions do not give the

real value at the corresponding locations. However, the angle α is always within the bound we choose for it, i.e., 0 ≤ α ≤ π/2, hence does not cause a problem in the scoring of diffuse reflectance and transmittance.

4.1 Reflectance and transmittance

When a photon packet is launched, the specular reflectance is computed

immediately. The photon weight after the specular reflection is transmitted into the tissue.

During the simulation, some photon packets may exit the media and their weights are

accordingly scored into the diffuse reflectance array or the transmittance array depending

on where the photon packet exits. After tracing multiple photon packets (N), we have two scored arrays Rd(r, α) and Tt(r, α) for diffuse reflectance and transmittance respectively. They are internally represented by Rd-rα[ir, iα] and Tt-rα[ir, iα] respectively

in the program. The coordinates of the center of a grid element are computed by:

r = (ir + 0.5) ∆r [cm] (4.1)

α = (iα + 0.5) ∆α [rad] (4.2)

where ir and iα are the indices for r and α. The raw data give the total photon weight in

each grid element in the two-dimensional grid system. To get the total photon weight in

the grid elements in each direction of the two-dimensional grid system, we sum the 2D

arrays in the other dimension:

Chapter 4 Scored Physical Quantities 23

Rd-r[ir] = ∑ iα=0

Nα–1 Rd-rα [ir, iα] (4.3)

Rd-α[iα] = ∑ ir=0

Nr–1 Rd-rα [ir, iα] (4.4)

Tt-r[ir] = ∑ iα=0

Nα–1 Tt-rα [ir, iα] (4.5)

Tt-α[iα] = ∑ ir=0

Nr–1 Tt-rα [ir, iα] (4.6)

To get the total diffuse reflectance and transmittance, we sum the 1D arrays again:

Rd = ∑ ir=0

Nr–1 Rd-r [ir] (4.7)

Tt = ∑ ir=0

Nr–1 Tt-r [ir] (4.8)

All these arrays give the total photon weight per grid element, based on N initial photon packets with weight unity. To convert Rd-rα[ir, iα] and Tt-rα[ir, iα] into photon

probability per unit area perpendicular to the photon direction per solid angle, they are

divided by the projection of the annular area onto a plane perpendicular to the photon

exiting direction (∆a cosα), the solid angle (∆Ω) spanned by a grid line separation in the α direction around an annular ring, and the total number of photon packets (N):

Rd-rα[ir, iα] ← Rd-rα[ir, iα] / (∆α cosα ∆Ω N) [cm–2 sr–1] (4.9)

Tt-rα[ir, iα] ← Tt-rα[ir, iα] / (∆α cosα ∆Ω N) [cm–2 sr–1] (4.10)

24 Chapter 4 Scored Physical Quantities

where

∆α = 2 π r ∆r = 2 π (ir + 0.5) (∆r)2 [cm2] (4.11)

∆Ω = 4 π sinα sin(∆α/2) = 4 π sin[(iα + 0.5) ∆α] sin(∆α/2) [sr] (4.12)

where r and α are computed from Eq. 4.1 and Eq. 4.2 respectively. The radially resolved diffuse reflectance Rd-r[ir] and total transmittance Tt-r[ir] are divided by the area of the

annular ring (∆a) and the total number of photon packets (N) to convert them into photon probability per unit area:

Rd-r[ir] ← Rd-r[ir] / (∆α N) [cm–2] (4.13)

Tt-r[ir] ← Tt-r[ir] / (∆α N) [cm–2] (4.14)

The angularly resolved diffuse reflectance Rd-α[iα] and total transmittance Tt-α[iα] are

divided by the solid angle (∆Ω) and the total number of photon packets (N) to convert them into photon probability per unit solid angle:

Rd-α[iα] ← Rd-α[iα] / (∆Ω N) [sr–1] (4.15)

Tt-α[iα] ← Tt-α[iα] / (∆Ω N) [sr–1] (4.16)

The total diffuse reflectance and transmittance are divided by the total number of photon

packets (N) to get the probabilities:

Rd ← Rd / N [–] (4.17)

Tt ← Tt / N [–] (4.18)

where [–] means dimensionless units.

4.2 Internal photon distribution

During the simulation, the absorbed photon weight is scored into the absorption array A(r, z). A(r, z) is internally represented by a 2D array Arz[ir, iz], where ir and iz are

the indices for grid elements in r and z directions. The coordinates of the center of a grid

element can be computed by Eq. 4.1 and the following:

Chapter 4 Scored Physical Quantities 25

z = (iz + 0.5) ∆z (4.19)

The raw data Arz[ir, iz] give the total photon weight in each grid element in the two-

dimensional grid system. To get the total photon weight in each grid element in the z

direction, we sum the 2D array in the r direction:

Az[iz] = ∑ ir=0

Nr–1 Arz [ir, iz] (4.20)

The total photon weight absorbed in each layer Al[layer] and the total photon weight

absorbed in the tissue A can be computed from Az[iz]:

Al[layer] = ∑ iz in layer

Az [iz] (4.21)

A = ∑ iz=0

Nz–1 Az [iz] (4.22)

where the summation range "iz in layer" includes all iz's that lead to a z coordinate in the

layer. Then, these quantities are scaled properly to get the densities:

Arz[ir, iz] ← Arz[ir, iz] / (∆α ∆z N) [cm–3] (4.23)

Az[iz] ← Az[iz] / (∆z N) [cm–1] (4.24)

Al[layer] ← Al[layer] / N [–] (4.25)

A ← A / N [–] (4.26)

The quantity A gives the photon probability of absorption by the tissue. The 1D array Al[layer] gives the photon probability of absorption in each layer. At this point, Arz[ir, iz]

gives the absorbed photon probability density (cm−3), and can be converted into photon fluence, φrz, (cm−2) by dividing it by the absorption coefficient µa (cm−1) of the layer

where the current location resides:

26 Chapter 4 Scored Physical Quantities

φrz[ir, iz] = Arz[ir, iz] / µa [cm–2] (4.27)

The 1D array Az[iz] gives the photon probability per unit length in the z direction (cm−1).

It can also be divided by the absorption coefficient µa (cm−1) to yield a dimensionless quantity φz[iz]:

φz[iz] = Az[iz] / µa [–] (4.28)

This quantity may seem hard to understand or redundant at first glance. However, the

summation of the raw data in Eq. 4.20 is equivalent to the convolution for an infinitely

wide flat beam in Eq. 7.15 to be discussed in Chapter 7. The equivalence can be shown as follows. According to Eqs. 4.20, 4.23 and 4.24, the final converted Az[iz] and Arz[ir, iz]

have the following relation:

Az[iz] = ∑ ir=0

Nr–1 Arz [ir, iz] ∆α(ir) (4.29)

where ∆a(ir) is computed in Eq. 4.11, but we stress that it is a function of ir in Eq. 4.29.

Employing Eqs. 4.27 and 4.28, Eq. 4.29 can be converted to:

φz[iz] = ∑ ir=0

Nr–1 frz [ir, iz] ∆α(ir) (4.30)

This is a numerical solution of the following integral:

φz(z) = ⌡⌠ 0

∞ frz(r, z) 2 π r dr (4.31)

Eq. 4.31 is essentially Eq. 7.15 for an infinitely wide flat beam with a difference of

constant S, where S is the power density of the infinitely wide flat beam. The equivalence between Eq. 4.31 and Eq. 7.15 can be seen after substituting φz(z) for F(r, z), φrz(r, z) for G(r'', z), and r for r''. Therefore, φz[iz] gives the fluence for an infinitely wide flat beam

with a difference of a constant which is the power density S.

The program mcml will only report Arz[ir, iz] and Az[iz] instead of φrz[ir, iz] and φz[iz]. The program conv will be set up to convert Arz[ir, iz] and Az[iz] into φrz[ir, iz] and φz[iz].

Chapter 4 Scored Physical Quantities 27

4.3 Issues regarding grid system

In our Monte Carlo simulation, we always set up a grid system. The computation

results will be limited by the finite grid size. This section will discuss what is the best that

one can do.

Position of average value for each grid element

The simulation provides the average value of the scored physical quantities in each

grid element. Now, the question is at what position should that averaged value be

assigned? One can argue that there is no best point because the exact answer to the

physical quantities is unknown. However, under linear approximations of the physical

quantities in each grid element, we can find the best point for each grid point. The linear

approximations can be justified for small grid size in most cases because the higher order

terms are considerably less than the linear term. Some special occasions will be discussed

subsequently.

Let us discuss the grid system in the r direction first because r is the variable over

which the convolution for photon beams of finite size will be implemented (see Chapter 7).

The grid system in the r direction uses ∆r as the grid separation with a total of N grid elements. The index to each grid element is denoted by n. The center of each grid element is denoted by rn:

rn = (n + 0.5) ∆r (4.32)

As we have mentioned, the Monte Carlo simulation actually approximates the

average of the physical quantity Y(r) in each grid element, where Y(r) can be the diffuse reflectance, diffuse transmittance, and internal fluence Arz(r, z) for a particualr z value.

<Y(r)> = 1

2 π rn ∆r

⌡   ⌠

rn – ∆r/2

rn + ∆r/2

Y(r) 2 π r dr (4.33)

where 2 π rn ∆r is the area of the ring or the circle when n = 0.

If Y(r) in each grid element is approximated linearly, we can prove that there exists a best point rb to satisfy:

28 Chapter 4 Scored Physical Quantities

<Y(r)> = Y(rb) (4.34)

where

rb = rn + ∆r

12 rn ∆r (4.35)

Proof: Y(r) is approximated by a Taylor series about rb expanded to the first order:

Y(r) = Y(rb) + (r – rb) Y'(rb) (4.36)

Substituting Eq. 4.36 into Eq. 4.33 yields:

<Y(r)> = 1

2 π rn ∆r

⌡   ⌠

rn – ∆r/2

rn + ∆r/2

[Y(rb) + (r – rb) Y'(rb)] 2 π r dr

= Y(rb)

2 π rn ∆r

⌡   ⌠

rn – ∆r/2

rn + ∆r/2

2 π r dr + Y'(rb) 2 π rn ∆r

⌡   ⌠

rn – ∆r/2

rn + ∆r/2

(r – rb) 2 π r dr

= Y(rb)

2 π rn ∆r [π r2]

rn + ∆r/2

rn – ∆r/2 +

Y'(rb) 2 π rn ∆r

(π/3) [2 r3 – 3 rb r2] rn + ∆r/2

rn – ∆r/2

= Y(rb)

2 π rn ∆r [2 π rn ∆r] +

Y'(rb) 2 π rn ∆r

(π/3) [2 ∆r (3 rn2 + (∆r)2

4 – 3 rb rn)]

= Y(rb) + Y'(rb) [rn + (∆r)2 12 rn

– rb]

or

<Y(r)> = Y(rb) + Y'(rb) [rn + (∆r)2 12 rn

– rb] (4.37)

If we set the term in the square bracket in Eq. 4.37 to zero, and solve for rb, we obtain:

Chapter 4 Scored Physical Quantities 29

rb = rn + (∆r)2 12 rn

(4.38a)

and Eq. 4.37 becomes:

<Y(r)> = Y(rb) (4.39)

Eq. 4.38a can be reformulated:

rb = rn + ∆r

12 rn ∆r (4.38b)

Q.E.D.

We can substitute Eq. 4.32 into the second term of Eq. 4.38b:

rb = [(n + 0.5) + 1

12 (n + 0.5) ] ∆r (4.38c)

When n = 0, rb = [0.5 + 1 6 ] ∆r =

2 3 ∆r

When n = 1, rb = [1.5 + 1 18 ] ∆r ≈ 1.556 ∆r

When n = 2, rb = [2.5 + 1 30 ] ∆r ≈ 2.533 ∆r

When n = 3, rb = [3.5 + 1 42 ] ∆r ≈ 3.524 ∆r

When n = 4, rb = [4.5 + 1 54 ] ∆r ≈ 4.519 ∆r

...

It is observed that the best point deviates from the center of each grid element, and

the smaller the index to the grid box, the larger the deviation. As the index n becomes

large, the best point approaches the center of the grid element. This behavior is due to the

2 π r factor in Eq. 4.33. A similar factor does not exist for the z direction, and the best points for the z direction should be the centers of each grid element.

30 Chapter 4 Scored Physical Quantities

The computation results of a Monte Carlo simulation with a selected grid system

always have finite precision which is fundamentally limited by the finite grid size. The

above theorem only gives the points where the function values are best represented by the

simulated results.

Effects of the first photon-tissue interaction

The above theorem assumed differentiability of Y(r), where Y(r) can represent the diffuse reflectance Rd(r), the diffuse transmittance Td(r), and the internal fluence φrz(r, z)

at a particular z value. This assumption should hold for the diffuse reflectance and the

diffuse transmittance. However, for the internal fluence, the on-z-axis fluence is a delta

function for an impulse response (responses to an infinitely narrow photon beam). Therefore, you cannot assume the differentiability of the function φrz(r, z) at r equal zero, i.e., φrz(r=0, z). The best solution to this problem has been provided by Gardner et al.

(1992b). They keep track of the first photon interactions with the medium, which are

always on the z-axis, separately from the rest of the interactions. Therefore, the function φrz(r, z) will not include the first photon-tissue interaction which yield a delta function. This approach ensures a differentiable φrz(r, z) at r equal zero besides its better accuracy.

In the current version (version 1.0) of mcml, Gardner et al.'s approach is not yet

implemented although we intend to add this in the later version. However, the result from

mcml will still be correct within the spatial resolution of the grid size in the r direction.

Although nobody will use Monte Carlo to simulate the responses in an absorption-only

semi-infinite medium, let us use this simple example to illustrate what we mean. In this

case, Gardner et al.'s approach will yield an exponentially decaying response on the z axis

which is a delta function of r. The version 1.0 of mcml will yield the same exponentially

decaying response in the first grid elements which is not a delta function because of the

finite volume of the grid elements. However, as the grid separation ∆r is made sufficiently small, the result approaches Gardner et al.'s.

So far the discussion has nothing to do with convolution for photon beams of finite

size which will be discussed in Chapter 7. According to Gardner et al. (1992b), the error

in the convolution caused by not scoring the first interactions separately will be small for

Gaussian beams whose radius is at least three times larger than the grid separation ∆r. As we will discuss in Section 7.4, our extended trapezoidal integration, instead of integrating

over the original grid points, in the convolution program conv should further decrease the

error. Of course, when the on-z-axis interactions are considered separately, our

Chapter 4 Scored Physical Quantities 31

convolution program conv is not subject to this limitation on the radius of the Gaussian

beam. In contrast, if the integration is approximated by computing the integrand over the

original grid points, the radius of the photon beam should still be large enough to yield

reasonable integration accuracy.

Since this theorem is a late development, it has not been implemented in the

programs (mcml and conv) yet. We plan to implement it in the next release (see Appendix

H).

32 Chapter 5 Programming mcml

5. Programming mcml

The simulation is written in ANSI Standard C (Plauger et al., 1989, Kelley et al.,

1990), which makes it possible to execute the program on any computers that support

ANSI Standard C. So far, the program has been successfully tested on Macintosh, IBM

PC compatibles, Sun SPARCstation 2, IBM RISC/6000 POWERstation 320, and Silicon

Graphics IRIS workstation. This chapter mainly describes several rules, the important

constants and the data structures used in the program, the algorithm to trace photon

packets, flow of the program, and the timing profile of the program. Prior knowledge of

C is assumed to fully understand this chapter. The flow of the program is listed in detail in

Appendix A. The complete source code is listed in Appendix B file by file. In Appendix

C, we have provided a make file used by make utilities on UNIX machines. Appendix D

and E are a template of input data file and a sample output data file respectively.

Appendix F gives several C Shell scripts for UNIX users. The information needed to

obtain the program is detailed in Appendix G.

5.1 Programming rules and conventions

When we write this program, we have followed the following rules and

conventions:

1. Conform to ANSI Standard C so that the program can be executed on a variety of

computers.

2. Avoid global variables whenever possible. This program has not defined any

global variables so far.

3. Avoid hard limits on the program. For example, we removed the limits on the

number of array elements by dynamic allocation at run time. This means that the

program can accept any number of layers or gridlines as long as the memory

permits.

4. Preprocessor names are all capital letters, e.g.,

#define PREPROCESSORS

5. For global variables, function names, or data types, first letter of each word is

capital, and words are connected without underscores, e.g.,

Chapter 5 Programming mcml 33

short GlobalVar;

6. For dummy variables, first letter of each word is capital, and words are connected

by underscores, e.g.,

void NiceFunction(char Dummy_Var);

7. Local variables are all lower cases, and words are connected by underscores, e.g.,

short local_var;

5.2 Several constants

There are several important constants defined in the header file mcml.h or the

source files mcmlmain.c and mcmlgo.c. They are listed in Table 5.1, and may be altered

according to your special need.

The constant STRLEN is used in the program to define string length. WEIGHT is the

threshold photon weight, below which the photon packet will go through a roulette. This

photon packet with small weight W has a chance of CHANCE to survive with a new weight

of W/CHANCE. COSZERO and COS90D are used for the computation of reflection with

Fresnel's formulas, and for the computation of new photon directional cosines. When

|cosα| > COSZERO, α is considered very close to 0 or 180o. When |cosα| < COS90D, α is considered very close to 90o.

When THINKCPROFILER is 1, the profiler of THINK C compiler (Symantec, 1991)

on Macintosh can be used to monitor the timing profile of the program regarding each

function (see Section 5.7). When GNUCC is 1, the code can be compiled with GNU C

compiler (gcc) from the Free Software Foundation, which does not support several

functions such as difftime() although being claimed to conform to ANSI C standards.

Therefore, the timing modules in the program will not work when GNUCC is 1, although the

program will otherwise operate normally. When STANDARDTEST is 1, the random number

generator will generate a fixed sequence of random numbers after being fed a fixed seed.

This feature is used to debug the program. STANDARDTEST should be set to 0 normally,

which makes the random number generator use the current time as the seed. When

PARTIALREFLECTION is set to 0, the all-or-none simulation mechanism of photon internal

reflection at a boundary (e.g., air/tissue boundary) described in Section 3.6 is used, where

the photon packet is either totally reflected or totally transmitted determined by the

comparison of the Fresnel reflectance and a random number. Otherwise when

PARTIALREFLECTION is set to 1, the photon packet will be partially reflected and

34 Chapter 5 Programming mcml

transmitted. Normally we set PARTIALREFLECTION to 0 because the all-or-none simulation

is faster.

Table 5.1. Important constants in the program mcml.

5.3 Data structures and dynamic allocations

Data structures are an important part of the program. Related parameters are

logically organized by structures such that the program is easier to write, read, maintain,

and modify. The parameters for a photon packet are grouped into a single structure

defined by:

typedef struct { double x, y, z; /* Cartesian coordinates.[cm] */ double ux, uy, uz;/* directional cosines of a photon. */ double w; /* weight. */ Boolean dead; /* 1 if photon is terminated. */ short layer; /* index to layer where the photon packet resides.*/ double s; /* current step size. [cm]. */ double sleft; /* step size left. dimensionless [-]. */ } PhotonStruct;

The location and the traveling direction of a photon packet are described by the Cartesian

coordinates x, y, z, and the directional cosines (ux, uy, uz) respectively. The current

weight of the photon packet is denoted by the structure member w. The member dead,

initialized to be 0 when the photon packet is launched, represents the status of a photon

packet. If the photon packet has exited the tissue or it has not survived a roulette when its

weight is below the threshold weight, then the member dead is set to 1. It is used to signal

Constants File Value Meaning

WEIGHT mcml.h 1×10 –4 threshold weight

CHANCE mcml.h 0.1 chance of surviving a roulette

STRLEN mcml.h 256 string length

COSZERO mcmlgo.c 1–1×10–12 cosine of ~ 0

COS90D mcmlgo.c 1×10–6 cosine of ~ 90o

THINKCPROFILER mcmlmain.c 1/0 switch for THINK C profiler on

Macintosh

GNUCC mcmlmain.c 1/0 switch for GNU C compiler

STANDARDTEST mcmlgo.c 1/0 switch for fixed sequence of

random numbers

PARTIAL-

REFLECTION

mcmlgo.c 1/0 switch for partial internal reflection

at boundary

Chapter 5 Programming mcml 35

the program to stop tracing the current photon packet. Note that the type Boolean is not

an internal data type in ANSI Standard C. It is defined to be type char in our header file

mcml.h. The member layer is the index to the layer where the photon packet resides.

Type short, whose value ranges between –32768 (215) and +32768 (Plauger et al., 1989),

is used for the member layer. It is defined for computation efficiency although the layer

can always be identified according to the Cartesian coordinates of the photon packet and

the geometric structure of the media. The member layer is updated only when the photon

packet crosses tissue/tissue interfaces. The member s is the step size in cm for the current

step. The member sleft is used to store the unfinished step size in dimensionless units

when a step size is large enough to hit a boundary or interface. For example, if a selected

step size s is long enough to hit a boundary of the current layer with interaction coefficient µ t as explained in Section 3.6, a foreshortened step size s1 is chosen as the current step

size, and the unfinished step size has to be stored. In the program, the following assignments are implemented: the member s = s1 and the member sleft = (s – s1) µ t.

Note that we store the unfinished step size in dimensionless unit, therefore, we only need

to know the interaction coefficient of the current layer to convert the step size back in cm

when the photon packet crosses layers.

The parameters that are needed to describe a layer of tissue are grouped into one

structure:

typedef struct { double z0, z1; /* z coordinates of a layer. [cm] */ double n; /* refractive index of a layer. */ double mua; /* absorption coefficient. [1/cm] */ double mus; /* scattering coefficient. [1/cm] */ double g; /* anisotropy. */

double cos_crit0, cos_crit1; } LayerStruct;

The Cartesian coordinates of the top and bottom boundaries are denoted by z0 and z1

respectively. The refractive index, absorption coefficient, scattering coefficient, and

anisotropy factor of a layer of medium are represented by the members n, mua, mus, and g

respectively. The cosines of the critical angles are denoted by the members cos_crit0 and

cos_crit1 respectively. They are computed with the relative refractive index of this layer

with respect to the two neighbor layers.

All the input parameters are defined in the following structure. Most of the

members of the structure are provided by the user before the simulation.

typedef struct { char out_fname[STRLEN]; /* output filename. */

36 Chapter 5 Programming mcml

char out_fformat; /* output file format. */ /* 'A' for ASCII, */ /* 'B' for binary. */ long num_photons; /* to be traced. */ double Wth; /* play roulette if photon */ /* weight < Wth.*/

double dz; /* z grid separation.[cm] */ double dr; /* r grid separation.[cm] */ double da; /* alpha grid separation. */ /* [radian] */ short nz; /* array range 0..nz-1. */ short nr; /* array range 0..nr-1. */ short na; /* array range 0..na-1. */

short num_layers; /* number of layers. */ LayerStruct * layerspecs; /* layer parameters. */ } InputStruct;

The filename for data output is out_fname, and its format (out_fformat) can be A for

ASCII or B for binary. Currently, only ASCII format is supported. The number of

photon packets to be simulated is denoted by the member num_photons. Since larger

number of photon packets may be simulated, type long int, whose value ranges between

–2,147,483,648 (231) and +2,147,483,648 (Plauger et al, 1989), is used for the member

num_photons. The threshold weight is denoted by the member Wth. The photon packet

with weight less than Wth will experience a roulette. The grid line separations ∆z, ∆r, and ∆α are represented by members dz, dr, and da respectively. The numbers of grid elements Nz, Nr, and Nα are represented by nz, nr, and na respectively. The total

number of layers is represented by the member num_layers. The member layerspecs is a

pointer to the structure LayerStruct. This pointer can be dynamically allocated with an

array of structures, in which each structure represents a layer, or the top ambient medium,

or the bottom ambient medium (e.g., air). Therefore, there are (num_photons + 2)

elements in the array, where the element 0 and element (num_photons + 1) store the

refractive indices for the top ambient medium and bottom ambient medium respectively.

The dynamic allocation will be discussed subsequently.

All the output data are organized into one structure too:

typedef struct { double Rsp; /* specular reflectance. [-] */ double ** Rd_ra; /* 2D distribution of diffuse */ /* reflectance. [1/(cm2 sr)] */ double * Rd_r; /* 1D radial distribution of diffuse */ /* reflectance. [1/cm2] */ double * Rd_a; /* 1D angular distribution of diffuse */ /* reflectance. [1/sr] */ double Rd; /* total diffuse reflectance. [-] */

double ** A_rz; /* 2D probability density in turbid */ /* media over r & z. [1/cm3] */ double * A_z; /* 1D probability density over z. */ /* [1/cm] */

Chapter 5 Programming mcml 37

double * A_l; /* each layer's absorption */ /* probability. [-] */ double A; /* total absorption probability. [-] */

double ** Tt_ra; /* 2D distribution of total */ /* transmittance. [1/(cm2 sr)] */ double * Tt_r; /* 1D radial distribution of */ /* transmittance. [1/cm2] */ double * Tt_a; /* 1D angular distribution of */ /* transmittance. [1/sr] */ double Tt; /* total transmittance. [-] */ } OutStruct;

The member Rsp is the specular reflectance. The pointer Rd_ra will be allocated

dynamically, and used equivalently as if it is a 2D array over r and α. Rd_ra is the internal representation of diffuse reflectance Rd(r, α) discussed in Section 4.1. The members Rd_r

and Rd_a are the 1D diffuse reflectance distributions over r and α respectively. The member Rd is the total diffuse reflectance Rd. The member A_rz is the representation of the

2D internal photon distribution A(r, z) (see Section 4.2). The members A_z and A_l are

the corresponding 1D internal photon distributions with respect to z and layers

respectively. The member A is the probability of photon absorption by the whole tissue.

The members for transmittance are analogous to these for reflectance except that there is

no distinction between unscattered transmittance and diffuse transmittance. All the

transmitted photon weight is scored into the arrays.

In the above defined structures, pointers are used to denote the 2D or 1D arrays.

These pointers are dynamically allocated at run time according to user's specifications.

Therefore, the user can use different numbers of layers or numbers of grid elements

without changing the source code of the program. This provides the flexibility of the

program and the efficiency of memory utilization. The dynamic allocation procedures are

modified from Press (1988). Only 1D array allocation will be presented here, and the

matrix allocation can be found in Appendix B.5 -- "mcmlnr.c".

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; }

This function returns a pointer, which points to an array of elements. Each element is a

double precision floating point number. The index range of the array is from nl to nh

38 Chapter 5 Programming mcml

inclusive. In our simulation, nl is always 0, which is the default value in C. This function

also initializes all the elements equal to zero.

5.4 Flowchart of photon tracing

Fig. 5.1 indicates the basic flowchart for the photon tracing part of the Monte

Carlo calculation as described in Chapter 3. Many boxes in the flowchart are direct

implementations of the discussions in Chapter 3. This chart also includes the situation

where the photon packet is in a glass, in which absorption and scattering do not exist. In

this case, the photon packet will be moved to the boundary of the glass layer in the current

photon direction. Then, we have to determine statistically whether the photon packet will

cross the boundary or be reflected according to the Fresnel's formulas.

The box "Launch photon" initializes the photon packet position, direction, weight,

and several other structure members including ds and dsleft in PhotonStruct. The flow

control box "Photon in glass?" determines whether the current layer is a glass layer or

tissue layer.

Chapter 5 Programming mcml 39

Fig. 5.1. Flowchart for Monte Carlo simulation of multi-layered tissue.

When the photon packet is in glass layers, which have no absorption or scattering,

the box "Set step size s" chooses the distance between the current photon position and the

boundary in the direction of the photon movement as the step size. The box "Move to

Y

Photon in glass?

Hit boundary?

Move s

Absorb

Scatter

Y

N

N

Launch photon

N

Photon weight small?

Survive roulette?

Last photon?

Y

N

Y

End

N

Y

Set step size sSet step size s

Move to boundary

Transmit or reflect

Store unfinished sMove to boundary

Transmit or reflect

40 Chapter 5 Programming mcml

boundary" updates the position of the photon packet. Now, as the photon packet is on the

boundary, two approaches of processing photon transmission and reflection are supported

in the box "Transmit or reflect" as discussed in Section 3.6. If the constant

PARTIALREFLECTION is zero, the box determines whether the photon packet should cross

the boundary or be reflected according to the Fresnel reflectance in an all-or-none fashion.

If the photon packet is reflected, the propagation direction should be updated. Otherwise,

if the photon packet crosses the interface into another layer of tissue, the propagation

direction and the index to the layer are updated. If the photon packet crosses the

boundary and moves out of the medium, the photon packet is terminated and the photon

weight is scored into the array for reflectance or transmittance depending on where the

photon packet exits. If the constant PARTIALREFLECTION is one, the box "Transmit or

reflect" deals with ambient medium/tissue interface differently. Part of the photon weight

is transmitted to the ambient medium as reflectance or transmittance, and the rest of the

weight will be reflected and continue propagation. Normally we set PARTIALREFLECTION

to 0 because the all-or-none simulation is faster (see Section 3.6).

In tissue layers, the box "Set step size s" sets the step size to the unfinished step

size according to the structure member sleft if sleft is not zero. Otherwise, it sets the

step size according to the interaction coefficient of the medium. With the chosen step size

s, the box "Hit boundary?" identifies whether the step size is long enough to hit the

boundary of the current layer.

If the step does not hit the boundary, then the box "Move s" will update the

position of the photon packet. Then, the box "Absorb" will deposit a portion of the

photon packet weight in the local grid element, and the box "Scatter" will determine the

new traveling direction for the rest of the photon packet after absorption.

If the step hits the boundary, then the step size s is foreshortened. The

foreshortened step size is the distance between the photon position and the boundary in

the direction of the photon movement, and the unfinished step size is stored by the box

"Store unfinished s". The stored unfinished step size in dimensionless units will be used by

the box "Set step size s" for tissue layers to generate the next step size. The next two

boxes "Move to boundary" and "Transmit or reflect" function the same as for the glass

layer.

At this point, the photon weight and the structure member dead are checked in the

box "Photon weight small?". If the photon packet is dead, it will jump to the box "Last

Chapter 5 Programming mcml 41

photon?" (transition not shown in the flowchart). If the photon packet is still alive and its

weight exceeds Wth, it will start the next step of propagation. If the photon packet is still

alive and its weight is lower than Wth, it has to experience a roulette in the box "Survive

roulette?". If the photon packet survives the roulette, it will start the next step of

propagation. Otherwise, the photon packet is terminated and the box "Last photon?"

determines whether to end the simulation or to start tracing a new photon packet.

5.5 Flow of the program mcml

A flow graph of the mcml source code has been generated using the UNIX

command cflow under SunOS. Each line of output begins with a reference number, i.e., a

line number, followed by a suitable number of tabs indicating the level, then the name of

the global (normally only a function not defined as an external or beginning with an

underscore), a colon, and its definition. The definition consists of an abstract type

declaration (for example, char *), and delimited by angle brackets, the name of the source

file and the line number where the definition was found.

Once a definition of a name has been printed, subsequent references to that

name contain only the reference number of the line where the definition may be found.

For undefined references, only <> is printed.

The list in Fig. 5.2 is generated by command: cflow -d2 mcml*.c. The option "-

d2" limits the nesting depth to 2. The command output with all the nesting levels is shown

in Appendix A.

42 Chapter 5 Programming mcml

1 main: char(), <mcmlmain.c 198> 2 ShowVersion: void*(), <mcmlio.c 49> 3 CenterStr: char*(), <mcmlio.c 28> 4 puts: <> 5 GetFnameFromArgv: void*(), <mcmlmain.c 150> 6 strcpy: <> 7 GetFile: struct*(), <mcmlio.c 94> 8 printf: <> 9 scanf: <> 10 strlen: <> 11 exit: <> 12 fopen: <> 13 CheckParm: void*(), <mcmlio.c 514> 14 ReadNumRuns: short(), <mcmlio.c 205> 15 printf: 8 16 ReadParm: void*(), <mcmlio.c 425> 17 FnameTaken: char(), <mcmlio.c 487> 18 sprintf: <> 19 free: <> 20 nrerror: void*(), <mcmlnr.c 19> 21 FreeFnameList: void*(), <mcmlio.c 500> 22 rewind: <> 23 ReadNumRuns: 14 24 ReadParm: 16 25 DoOneRun: void*(), <mcmlmain.c 163> 26 InitOutputData: void*(), <mcmlio.c 546> 27 Rspecular: double(), <mcmlgo.c 117> 28 PunchTime: long(), <mcmlmain.c 60> 29 ReportStatus: void*(), <mcmlmain.c 122> 30 LaunchPhoton: void*(), <mcmlgo.c 143> 31 HopDropSpin: void*(), <mcmlgo.c 726> 32 ReportResult: void*(), <mcmlmain.c 133> 33 FreeData: void*(), <mcmlio.c 581> 34 fclose: <>

Fig. 5.2. Flow of mcml.

The names of most functions well describe what they do. The function

ShowVersion() prints the name of the program, the names and address of the authors of

the program, and the version of the program. The function GetFnameFromArgv() get an

input data filename from the command line input for UNIX users or DOS users.

Macintosh OS does not allow command line input. The function GetFile() is used to get

the pointer to the file stream for the input data file. The function CheckParm() checks the

input parameters. If the CheckParm() detects an error, it will exit the program.

Otherwise, if all the input parameters pass the error check by the function CheckParm(),

independent simulation runs will be implemented one by one. For each independent run,

the function ReadParm() obtains the input parameters, and the function DoOneRun() does

the simulation and reports the results to the output data file.

After all the input parameters are checked by CheckParm(), the file pointer is

rewound to the beginning of the file stream so that the input parameters can be read again

by the functions ReadNumRuns() and ReadParm(). The function ReadNumRuns() gets how

Chapter 5 Programming mcml 43

many independent simulations are to be specified in this input data file. The function

ReadParm() only reads the input parameters for one independent run.

Under the function DoOneRun(), the function PunchTime() provides the user time

and the real time used so far by the current simulation run. The function ReportStatus()

fetches the real time and predicts when the current simulation run will finish. However,

these timing functions will not work if the source code is compiled by a GNU C compiler.

The function LaunchPhoton() initializes a photon packet. The function HopDropSpin()

moves the photon packet, deposits some photon packet weight, scatters the packet, and

deals with the boundary. After the given number of photon packets are traced, the results

are properly processed (see Chapter 4) and then written to an output file by the function

ReportResult().

5.6 Multiple simulations

The program can do any number of independent simulations sequentially without

being subject to memory limit. It checks the parameters of one simulation after another

before starting the simulation. If it detects an error in the input data file, the program

stops the execution. Otherwise, it reads in the parameters of one independent simulation

at a time and starts the simulation. At the end of the simulation, it writes the results to the

output data file whose name is specified by the input data file. The next independent run

will be processed thereafter. The users should try to make sure not to use the same output

filenames for different simulations, although the program checks against duplicated output

filenames.

To check against duplicated filenames specified in an input data file, we set up a

linear linked list to store the filenames specified in the input data file. Each new filename

is compared with every node of the filename list. If the name is already taken, the

program notifies the user of the name, and exits to the system. Otherwise, if no filename

is duplicated, the program deletes the filename list to release the memory, and continues

execution.

5.7 Timing profile of the program

The timing profile of the program mcml indicates how to improve the efficiency of

the program by logging how much time the program spends on each function. Profilers

are available in a few compilers including C compilers of UNIX operating systems, and

44 Chapter 5 Programming mcml

THINK C compiler on Macintosh. We used the THINK C 5.0.1 profiler here (Symantec,

1991). To log the timings, we turn on the flag THINKCPROFILER to 1 in mcmlmain.c, and

check the "Generate profiler calls" check box in the Debugging page of the "Options..."

dialog box in the THINK C compiler.

The following input file is used (see Section 9.1 for how to name the input file) to

test the profile, where only 100 photon packets are traced. The optical parameters of the tissue are: refractive index n = 1.37, absorption coefficient µ a = 1 cm−1, scattering

coefficient µs = 100 cm−1, anisotropy factor g = 0.9, and thickness d = 0.1 cm.

1.0 # file version 1 # number of runs

prof.mco A # output filename, ASCII/Binary 100 # No. of photons 1E-2 1E-2 # dz, dr 100 100 1 # No. of dz, dr & da.

1 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.37 1 100 0.90 0.1 # layer 1 1.0 # n for medium below.

The profiler output is listed in Table 5.2. The heading "Function" gives the name

of a function. The headings "Minimum", "Maximum" and "Average" give the minimum,

maximum and average time spent in a routine respectively. The unit of time here is a unit

of the VIA 1 timer. Each unit is 1.2766 µsec (approximately 780,000 in a second). The

heading "%" is the percentage of profiling period spent in the routine, where the profiling

period is the accumulated time spent in routines that were compiled with the "Generate

profiler calls" options on. The heading "Entries" is the number of times the routine was

called.

Chapter 5 Programming mcml 45

Table 5.2. Timing profile of the program mcml.

Function Minimum Maximum Average % Entries

AllocMatrix 2507 134153 46662 0 3

AllocVector 65 319 169 0 6

CrossDnOrNot 53 670 145 0 95

CrossOrNot 40 631 66 0 175

CrossUpOrNot 53 635 144 0 80

Drop 166 887 245 5 3554

HitBoundary 38 622 62 1 3729

Hop 40 595 59 1 3729

HopDropSpin 52 711 74 1 3729

HopDropSpinInTissue 111 815 242 5 3729

InitOutputData 314 314 314 0 1

LaunchPhoton 35 123 59 0 100

PredictDoneTime 56472 61201 58638 3 9

PunchTime 76 116 104 0 10

RFresnel 300 905 411 0 105

RandomNum 37 735 67 4 10937

RecordR 1099 2293 1690 0 46

RecordT 1363 2396 1761 0 54

ReportStatus 19 790 90 0 100

Rspecular 58 58 58 0 1

Spin 914 2157 1412 33 3554

SpinTheta 72 665 102 2 3554

StepSizeInTissue 36 2094 1378 33 3729

ran3 27 1187 44 3 10938

From the percentage column, we observe that the functions Spin() and

StepSizeInTissue() take most of the computation time. They are the primary places to

modify if we need to improve the efficiency. The function StepSizeinTissue() takes a

long time because of the logarithmic operation. The function PredictDoneTime()

apparently takes much computation time too. This is because we simulated only 100

photon packets. This function will be called for a fixed number of times (10 times) for a

46 Chapter 5 Programming mcml

simulation no matter how many photon packets are simulated. Therefore, its percentage

will decrease linearly with the number of photon packets to be simulated. Note that we

made the profiler include only the main part of the simulation (See file mcmlmain.c in

Appendix B). Therefore, the functions for input or output of data are not included since

they do not scale up as the number of photon packets to be simulated increases.

Chapter 6 Computation Results of mcml and Verification 47

6. Computation Results of mcml and Verification

Some computation results are described in this chapter as examples, and some of

them are compared with the results from other theory or with the Monte Carlo simulation

results from other investigators to verify the program.

6.1 Total diffuse reflectance and total transmittance

We computed the total diffuse reflectance and total transmittance of a slab of

turbid medium with the following optical properties: relative refractive index n = 1, absorption coefficient µa = 10 cm−1, scattering coefficient µ s = 90 cm−1, anisotropy factor

g = 0.75, and thickness d = 0.02 cm. Ten Monte Carlo simulations of 50,000 photon

packets each are completed. Then, the averages and the standard errors of the total

diffuse reflectance and total transmittance are computed (Table 6.1). The table also lists

the results from van de Hulst's table (van de Hulst, 1980) and from Monte Carlo

simulations by Prahl et al. (1989). All results agree with each other.

Table 6.1. Verification of the total diffuse reflectance and the total

transmittance in a slab with a matched boundary.

The columns "Rd Average" and "Rd Error" are the average and the standard error of the

total diffuse reflectance respectively, while the columns "Tt Average" and "Tt Error" are

the average and the standard error of the total transmittance.

For a semi-infinite turbid medium that has mismatched refractive index with the

ambient medium, the average and the standard error of the total diffuse reflectance are

computed similarly, and compared in Table 6.2 with Giovanelli's (1955) results and Monte

Carlo simulation results by Prahl et al. (1989). The medium has the following optical properties: relative refractive index n = 1.5, µ a = 10 cm−1, µ s = 90 cm−1, g = 0 (isotropic

scattering). Ten Monte Carlo simulations of 5,000 photon packets each are completed to

compute the average and the standard error of the total diffuse reflectance.

Source Rd Average Rd Error Tt Average Tt Error

van de Hulst, 1980 0.09739 0.66096

mcml 0.09734 0.00035 0.66096 0.00020

Prahl et al., 1989 0.09711 0.00033 0.66159 0.00049

48 Chapter 6 Computation Results of mcml and Verification

Table 6.2. Verification of the total diffuse reflectance in a semi-infinite

medium with a mismatched boundary.

Source Rd Average Rd Error

Giovanelli, 1955 0.2600

mcml 0.25907 0.00170

Prahl et al., 1989 0.26079 0.00079

6.2 Angularly resolved diffuse reflectance and transmittance

We used mcml to compute the angularly resolved diffuse reflectance and

transmittance of a slab of turbid medium with the following optical properties: relative refractive index n = 1, absorption coefficient µa = 10 cm−1, scattering coefficient µ s = 90

cm−1, anisotropy factor g = 0.75, and thickness d = 0.02 cm. In the simulation, 500,000

photon packets are used, and the number of angular grid elements is 30. The results are

compared with the data from van de Hulst's table (van de Hulst, 1980) as shown in Figs.

6.2a and b.

Since mcml also scores the unscattered transmittance into the transmittance array,

we have to subtract it from the first element of the array to obtain the diffuse transmittance. In this case, the unscattered transmittance is exp(–(µ a+µ s) d) = exp(–2) ≈

0.13534, which is only scored into the first grid element in the α direction. The solid angle spanned by the first grid element is ∆Ω ≈ 2 π sin(∆α/2) ∆α, where ∆α = π/(2×30). Therefore, the contribution of the unscattered transmittance to the first element of the array Tt(α) is exp(–2)/∆Ω ≈ 15.68. After the subtraction, we get the adjusted first

element of transmittance array being 0.765 sr−1, which is the diffuse transmittance. Note

that the variance in Fig. 6.2a for diffuse reflectance is larger than that in Fig. 6.2b for

diffuse transmittance. This is because the total diffuse reflectance is much less than the

total diffuse transmittance (0.09739 vs 0.66096–0.13534 = 0.52562, as shown in Table

6.1).

Since van de Hulst used a different definition of reflectance and transmittance, and

a normalization to incident flux π, we multiplied van de Hulst's data by the cosine of the exiting angle from the normal to the surface, then divided by π.

Chapter 6 Computation Results of mcml and Verification 49

0

0.005

0.01

0.015

0.02

0.025

R d ( α ) [s

r -1 ]

0š 0.1š 0.2š 0.3š 0.4š 0.5š

R d (α) van de Hulst

R d (α) mcml

(a)

0

0.2

0.4

0.6

0.8

T d ( α ) [s

r -1 ]

0.1š 0.2š 0.3š 0.4š 0.5š

(b)

T d (α) van de Hulst

T d (α) mcml

α [rad]

Fig. 6.2. Angularly resolved (a) diffuse reflectance Rd(α) and (b) diffuse transmittance Td(α) vs the angle between the photon exiting direction and

the normal to the medium surface α. Solid circles are from van de Hulst's table, and the open square boxes are from mcml simulation. The optical parameters are: relative refractive index n = 1.0, µ a = 10 cm−1, µ s = 90

cm−1, g = 0, thickness d = 0.02 cm.

6.3 Radially resolved diffuse reflectance

As an example of the simulation of radially resolved diffuse reflectance, we

compare the diffuse reflectances of two semi-infinite media whose optical properties are

50 Chapter 6 Computation Results of mcml and Verification

governed by similarity relations (Wyman et al., 1989a and 1989b). The two sets of optical parameters share the same absorption coefficient µ a and transport scattering coefficient

µ s(1–g). These two media should give approximately the same diffuse reflectances if the

similarity relations are valid. The results (Fig. 6.3) confirmed that the similarity relations

do not apply for photon sources near the media boundary as Wyman et al. expected. The

relative difference between two diffuse reflectances is very large when the radius r is small,

and becomes smaller when r becomes larger. We have also confirmed that the similarity

relations work very well when the photon source is deep inside the media using a modified

mcml (see Chapter 11).

In Fig. 6.3., The

optical properties for curve A are: µ a = 0.1 cm−1, µ s =

100 cm−1, g = 0.9, nrel = 1,

and the optical properties for curve B are: µ a = 0.1

cm−1, µ s = 10 cm−1, g = 0,

nrel = 1. The two curves A

and B are results from pure

Monte Carlo simulations of

1 million photon packets.

The grid line separation in

the r direction is 0.005 cm,

and number of grid

elements is 200.

6.4 Depth resolved

internal fluence

As an example of

simulation of the depth

resolved internal fluence,

we show the results for two

semi-infinite media with

matched and mismatched

boundaries respectively

0.01

1

100

0 0.2

R d ( cm -2 )

0.4 0.6 0.8 1

A: g=0.9

B: g=0

(a)

-0.5

0

0.5

1

1.5

2

R e la

tiv e E

rr o r

0 0.2 0.4 0.6 0.8 1 r (cm)

(b)

(B-A) / A

Fig. 6.3. (a) Comparison and (b) the relative

difference of diffuse reflectances as a function

of radius r for two semi-infinite media whose

optical properties are "equivalent" according

to similarity relations.

Chapter 6 Computation Results of mcml and Verification 51

(Fig. 6.4). The dimensionless internal fluence as a function of depth z, φz[iz] in Section

4.2, is computed from the response to an infinitely narrow photon beam normally incident

on a semi-infinite medium. However, it can be equivalently considered as the response of

an infinitely wide photon beam perpendicularly incident on a semi-infinite medium with a

difference of a constant S (see Section 4.2), where S is the power density. Since the direction output of the program mcml gives Az[iz] instead of φz[iz], we divided Az[iz] by the absorption coefficient of the semi-infinite medium to get φz[iz]. Although the response

of an infinitely narrow photon beam is dimensionless (Fig. 6.4), if the input photon beam is

measured in W/cm2 or J/cm2 as the power density of energy density, the unit of fluence is

also in the unit of W/cm2 or J/cm2 correspondingly. Since we only consider steady-state

responses, we can discuss either energy density or power density because they can be

converted back and forth.

Note that the fluence near the surface is larger than 1 because the back scattered

light augments the fluence. Furthermore, the internal fluence for the medium with a

mismatched boundary is higher than that for the medium with a matched boundary. This

is due to the internal reflection by the mismatched boundary, therefore the photons that

would escape from the boundary of the boundary-matched medium may be reflected back

into the medium by the mismatched boundary and hence have a greater chance to be

absorbed. Also note that when z is sufficiently deep, the two curves are parallel. This

confirms the valid range of diffusion theory. For z larger than the penetration depth δ, diffusion theory predicts that the internal fluence distribution should be (Wilson et al.,

1990):

φ(z) = φ0 k exp(–z/δ) (6.1)

where k is a scalar that depends on the amount of back scattered reflectance, and φ0 is the

incident irradiance, which is 1 in our mcml simulation. The scalar k is obviously a function

of the relative index of refraction. Therefore, the matched boundary and the mismatched

boundary will have different k. The penetration depth δ is computed:

δ = 1/ 3 µ a (µ s + µ s (1–g)) = 1/ 3(0.1)(0.1+100(1–0.9)) ≈ 0.57 cm (6.2)

which is independent of relative index of refraction. Therefore, the two curves in Fig. 6.4

should be off just by a factor due to different k values when z > δ, which means the curves are parallel in log-linear scale when z > δ. The two curves shown here are parallel even when z > 1 mfp' = 1/(µs + µ s (1–g)) ≈ 0.1 cm, where mfp' is the transport mean free path.

52 Chapter 6 Computation Results of mcml and Verification

One mfp' may be a better criterion for valid application of diffusion theory than the

penetration depth. Further supporting evidence can be found in Chapter 11 and Wang et

al. (1992).

1

10

0 0.2

F lu

e n

ce [

-]

0.4 0.6 0.8 1

nrel = 1 nrel = 1.37

z (cm)

µa = 0.1 cm-1

µs = 100 cm -1

g = 0.9 N = 1,000,000

1 mfp'

Fig. 6.4. Comparison of internal fluences as a function of depth z for two

semi-infinite media with a matched boundary and a mismatched boundary

respectively. The results are from Monte Carlo simulations with 1 million

photon packets each using mcml. The grid line separation in the z direction

is 0.005 cm, and number of grid elements is 200.

We fit the parallel part of the two curves with exponential functions. The damping

constants for the curves are approximately 1.73 cm−1 for the matched boundary and 1.74

cm−1 for the mismatched boundary respectively. The reciprocals of the damping constants

are 0.578 cm for the matched boundary and 0.575 cm for the mismatched boundary

respectively. They are very close to the penetration depth (0.57 cm, Eq. 6.2) predicted

from diffusion theory.

6.5 Computation times vs optical properties

We completed multiple Monte Carlo simulations with mcml for semi-infinite media

with various optical properties, and fitted the user times as a function of the ratio between scattering coefficient and absorption coefficient µs/µ a and anisotropy g. The situations for

media with matched boundaries (relative refractive index is 1) and mismatched boundaries

Chapter 6 Computation Results of mcml and Verification 53

(relative refractive index is not 1) will be presented separately. Note that the user time is

whatever the system allocates to the running of the program, as opposed to the real time

which is wall-clock time. In a time-shared system, they need not be the same, and the real

time of the same run may not be reproduced depending on the status of the system. In

mcml, the user time is reported to the output data file, and the real time is used to predict

when the simulation finishes during the simulation.

Before starting multiple mcml simulations for various optical properties, we know

that the time required to finish tracing a photon packet is proportional to the number of

steps that a photon packet takes until being terminated. According to the rules for photon

propagation described in Chapter 3, this number of steps should not be dependent on the absolute values of scattering coefficient µs and absorption coefficient µa, but their ratio.

Therefore, we keep one of the two parameters constant (e.g., µ s = 100 cm−1), and vary

the other one (e.g., µ a).

Matched boundary

For media with matched boundaries (relative refractive index nrel is 1), we finished

multiple mcml simulations of 10,000 photon packets each for various absorption coefficients µa and anisotropy factors while keeping the scattering coefficient µ s fixed to

100 cm−1. The results are listed in Table 6.3, where the ratios between the scattering

coefficient and the absorption coefficient are tabulated, instead of the two coefficients

themselves separately, and the user

54 Chapter 6 Computation Results of mcml and Verification

Table. 6.3. Computation times for various media with matched

boundaries. The column "Predicted User Time" is the computed values

using Eqs. 6.1-6.3 presented later. The column "Error" is equal to

(Predicted User Time – User Time)/(User Time)*100.

µs/µa g User Time (sec./1000 photons)

Predicted User Time (sec./1000 photons)

Error (%)

0.2 0 0.43 0.36 –16.15 1 0 0.78 0.81 3.58 2 0 1.10 1.15 4.42 10 0 2.59 2.56 –0.99 20 0 3.66 3.62 –0.97 100 0 8.46 8.10 –4.23 200 0 12.27 11.46 –6.64 1000 0 29.61 25.61 –13.49

0.2 0.1 0.40 0.38 –5.04 1 0.1 0.80 0.86 7.19 2 0.1 1.10 1.22 10.70 10 0.1 2.80 2.75 –1.82 20 0.1 3.90 3.90 0.10 100 0.1 9.80 8.81 –10.07 200 0.1 13.20 12.52 –5.19 1000 0.1 28.60 28.25 –1.21

0.2 0.5 0.50 0.45 –9.54 1 0.5 1.00 1.08 7.71 2 0.5 1.40 1.57 11.80 10 0.5 3.50 3.73 6.49 20 0.5 5.30 5.42 2.19 100 0.5 12.10 12.90 6.60 200 0.5 17.90 18.74 4.71 1000 0.5 40.60 44.63 9.93

0.2 0.9 0.50 0.49 –1.83 1 0.9 1.20 1.35 12.74 2 0.9 1.90 2.09 10.20 10 0.9 6.30 5.77 –8.39 20 0.9 9.80 8.93 –8.86 100 0.9 25.80 24.62 –4.58 200 0.9 38.10 38.10 0.00 1000 0.9 95.30 105.02 10.20

0.2 0.99 0.50 0.42 –16.18 1 0.99 1.20 1.42 18.68 2 0.99 2.10 2.41 14.85 10 0.99 8.80 8.20 –6.87 20 0.99 16.50 13.88 –15.89 100 0.99 60.60 47.16 –22.18 200 0.99 97.20 79.86 –17.84 1000 0.99 248.80 271.37 9.07

Chapter 6 Computation Results of mcml and Verification 55

times are converted to seconds per 1000, instead of 10,000, photon packets. The columns

"Predicted User Time" and "Error" will be discussed subsequently

We plotted the user times as a function of the ratio between the scattering

coefficient and the absorption coefficient for each anisotropy factor g in a log-log scale

(Fig. 6.5). For each anisotropy factor g, the higher the ratio between the scattering

coefficient and the absorption coefficient, the longer the user time. This is because the photon packets in media of higher ratio µ s/µ a, compared with media of lower ratio, can

jump more steps before reaching the threshold weight and hence having a chance to be

terminated. Therefore, the Monte Carlo simulation of low absorbing medium is very slow.

If the diffuse reflectance as a function of r in a low absorption semi-infinite turbid medium

is the only physical quantity to be computed, a hybrid model of pure Monte Carlo

simulation and diffusion theory (Wang et al., 1992) is a much faster model than pure

Monte Carlo simulations. The speed of the hybrid model is not so sensitive to the ratio

between the scattering coefficient and the absorption coefficient.

For the same ratio between the scattering coefficient and the absorption

coefficient, the larger the anisotropy factor g, the longer the user time. This is because

that the photon packets in media of larger anisotropy factors g have less chance to be

reflected out of the media because the scatterings are more forward directed, and hence to

be terminated.

For each anisotropy factor g, the data points are well aligned in the log-log plot.

This means that we can fit the data points for each anisotropy factor g with a power

function. The fitted lines are presented in Fig. 6.5.

The two fitting coefficients C1(g) and C2(g) are dependent on the anisotropy

factor g. The fitting coefficients C1(g) are plotted against the anisotropy factor g in a log-

linear scale (Fig. 6.6a), and can be fitted with an exponential function. Similarly, the fitting coefficients C2(g) are plotted against (1 – g) in a linear-log scale (Fig. 6.6b), and

can be fitted with a logarithmic function. These three fittings are summarized as the

following empirical formulas:

56 Chapter 6 Computation Results of mcml and Verification

0.1

1

10

100

U se

r T

im e [ se

c. /1

0 0 0 p

h o to

n s]

1000

0.1 1 10 100 µs/µa

1000

g=0 g=0.5 g=0.9 g=0.99

t = C1(g) (µs/µa) C2(g)

nrel = 1

Fig. 6.5. The user times vs the ratio between the scattering coefficient µ s and the absorption coefficient µ a for different anisotropy factors g of media

with matched boundaries.

C1(g) = 0.81 exp(0.57 g) (6.3)

C2(g) = 0.50 – 0.13 log(1–g) (6.4)

t = C1(g) (µ s/µ a) C2(g) [sec./1000 photons] (6.5)

where t is the user time in seconds per 1000 photon packets for semi-infinite media with

matched boundaries.

Chapter 6 Computation Results of mcml and Verification 57

1

0

C 1

0.2 0.4 0.6 0.8 1

C1(g) = 0.81 exp(0.57 g)

g

(a)

0.4

0.5

0.6C 2

0.7

0.8

0.01 0.1

C2(g) = 0.50 - 0.13 log(1-g)

1-g 1

(b)

Fig. 6.6. The fitting coefficients (a) C1(g) and (b) C2(g) vs the anisotropy

factor g of media with matched boundaries.

To test how good Eqs. 6.3-6.5 are, we use them to compute the user times for the

optical properties given in Table 6.3, and presented the computed user times in Table 6.3

as the column "Predicted User Time". The relative errors are within 20% as shown in the

column "Error" in Table 6.3 for all rows in the table except one of them (in bold face).

The Eqs. 6.3-6.5 can be used generally to predict user time of a medium with a

matched boundary. However, several limitations and notes have to be mentioned. First,

58 Chapter 6 Computation Results of mcml and Verification

the anisotropy factor g is limited to less than 0.99, and the accuracy is unknown for g > 0.99 because the fitting coefficient C2(g) approaches infinity when g approaches 1.

Second, these equations are based on mcml simulations on a Sun SPARCstation 2.

Therefore, we expect a scale factor for Eq. 6.5 or the values in Table 6.3 on a different

computer system. This scale factor can be determined by simulating one or several media

with optical properties in Table 6.3 using mcml running on your computer system and

taking the ratio between the user time on your machine and the user time in Table 6.3.

Third, the speed of mcml is related to the threshold weight in the program mcml (WEIGHT

in Table 5.1), which is normally 1×10 –4 (See Table 5.1), and the chance of surviving a roulette (CHANCE in Table 5.1), which is normally 0.1 (See Table 5.1). If these two

parameters are changed, Eqs. 6.3-6.5 are no longer valid. It is unexplored yet how these

two parameters will affect the user times.

Mismatched boundary

We repeat the above process to media with mismatched boundaries (relative refractive index nrel � 1). We finished multiple mcml simulations of 1,000 (instead of

10,000 for matched boundaries) photon packets each for various absorption coefficients

and anisotropy factors while keeping the scattering coefficient fixed to 100 cm−1 and the

relative refractive index to 1.37, which is typical for human tissues in visible or infrared

wavelength. The results are listed in Table 6.4.

We plotted the user times as a function of the ratio between the scattering

coefficient and the absorption coefficient for each anisotropy factor g in a log-log scale (Fig. 6.7). Then, the fitting coefficients C1(g) and C2(g) are plotted with respect to g and

(1–g) respectively (Fig. 6.8a and b), and fitted with an exponential function and a

logarithmic function correspondingly. The fittings give the following empirical formulas:

Chapter 6 Computation Results of mcml and Verification 59

Table 6.4. Computation times for various media with mismatched

boundaries. The column "Predicted User Time" is the computed values

using Eqs. 6.4-6.6 presented later. The column "Error" is equal to

(Predicted User Time – User Time)/(User Time)*100.

µs/µa g User Time (sec./1000 photons)

Predicted User Time (sec./1000 photons)

Error (%)

0.2 0 0.48 0.43 –9.74 1 0 0.98 1.05 7.14 2 0 1.42 1.54 8.26 10 0 3.67 3.73 1.51 20 0 5.47 5.45 –0.28 100 0 13.43 13.22 –1.57 200 0 18.67 19.35 3.66 1000 0 50.50 46.90 –7.13

0.2 0.1 0.50 0.44 –11.03 1 0.1 1.00 1.09 8.85 2 0.1 1.50 1.60 6.68 10 0.1 3.83 3.92 2.23 20 0.1 5.90 5.76 –2.44 100 0.1 15.95 14.08 –11.70 200 0.1 22.55 20.71 –8.18 1000 0.1 42.38 50.66 19.54

0.2 0.5 0.53 0.49 –8.10 1 0.5 1.13 1.26 11.25 2 0.5 1.73 1.89 9.31 10 0.5 4.87 4.88 0.22 20 0.5 7.63 7.34 –3.77 100 0.5 19.53 18.95 –2.97 200 0.5 27.08 28.51 5.28 1000 0.5 77.96 73.58 –5.62

0.2 0.9 0.55 0.49 –11.64 1 0.9 1.27 1.45 14.31 2 0.9 2.03 2.33 14.58 10 0.9 7.28 6.95 –4.55 20 0.9 12.10 11.13 –7.99 100 0.9 35.98 33.26 –7.56 200 0.9 55.05 53.28 –3.21 1000 0.9 133.06 159.18 19.63

0.2 0.99 0.55 0.41 –25.96 1 0.99 1.28 1.50 17.16 2 0.99 2.10 2.63 25.19 10 0.99 8.82 9.68 9.77 20 0.99 16.82 16.97 0.92 100 0.99 69.55 62.51 –10.12 200 0.99 120.56 109.60 –9.09 1000 0.99 366.45 403.62 10.14

60 Chapter 6 Computation Results of mcml and Verification

C1(g) = 1.05 exp(0.36 g) (6.6)

C2(g) = 0.55 – 0.13 log(1–g) (6.7)

t = C1(g) (µ s/µ a) C2(g) [sec./1000 photons] (6.8)

where t is the user time in seconds per 1000 photon packets for semi-infinite media with mismatched boundaries (nrel = 1.37).

0.1

1

10

100

1000

0.1 1

U se

r T

im e [ se

c. /1

0 0 0 p

h o to

n s]

10 100 1000

g=0 g=0.5 g=0.9 g=0.99

µs/µa

C2(g)

nrel = 1.37

t = C1(g) (µs/µa)

Fig. 6.7. The user times vs the ratio between the scattering coefficient µ s and the absorption coefficient µ a for different anisotropy factors g in media

with relative refractive index 1.37.

Chapter 6 Computation Results of mcml and Verification 61

1

1.1

C 1

1.2

1.3

1.4

1.5

1.6

0 g

C1 = 1.05 exp(0.36 g)

0.2 0.4 0.6 0.8 1

(a)

0.5

0.6

0.7

0.8

0.9

0.01

C2 = 0.55 - 0.13 log(1-g)

C 2

0.1 1

(b)

1-g

Fig. 6.8. The fitting coefficients (a) C1 and (b) C2 vs the anisotropy factor

g of media with relative refractive index 1.37.

To test how good Eqs. 6.6-6.8 are, we use them to compute the user times for the

optical properties in Table 6.4, and presented the computed user times in Table 6.4 in the

column "Predicted User Time". For all rows in the table except two of them (in bold

face), the relative errors are within 20%. The cautions made for media with matched

boundaries apply here as well.

62 Chapter 6 Computation Results of mcml and Verification

To summarize the empirical formulas for both matched and mismatched

boundaries, we list the formulas in Table 6.5.

Table 6.5. Empirical formulas of user times for matched and mismatched

boundaries.

Items Matched (nrel = 1) Mismatched (nrel = 1.37)

C1(g) 0.81 exp(0.57 g) 1.05 exp(0.36 g)

C2(g) 0.50 – 0.13 log(1–g) 0.55 – 0.13 log(1–g)

t [sec./1000 photons] C1(g) (µ s/µ a) C2(g) C1(g) (µ s/µ a) C2(g)

6.6 Scored Physical Quantities of Multi-layered Tissues

For multi-layered tissues, we have not found computation results based on other

theories to compare with our computation. However, thanks to Gardner's cooperation

(Gardner et al., 1992), we compared our Monte Carlo simulation results of multi-layered

tissues with their results of an independently written Monte Carlo simulation. The comparison includes the diffuse reflectance versus radius, Rd(r), where the radius r is the

distance between the photon incident point and the observation point, the transmittance versus radius, Tt(r), and the internal fluence versus z, φz(z), and the internal fluence versus r and z, φrz(r, z). This comparison can at least greatly reduce the chance of programming

errors.

We chose a three-layer tissue for the simulation. The optical properties of each

layer are shown in Table 6.6.

Table 6.6. The optical properties of the three-layer tissue.

Layer Refractive Index n

Absorption Coeff. (cm–1)

Scattering Coeff. (cm–1)

Anisotropy Factor g

Thickness (cm)

1 1.37 1 100 0.9 0.1

2 1.37 1 10 0 0.1

3 1.37 2 10 0.7 0.2

The refractive indices of the top and bottom ambient media are both set to 1.0.

The grid separations in z and r directions are both 0.01 cm. The number of grid elements

in z and r directions are 40 and 50 respectively. We do not want to resolve the exiting

angles of reflected or transmitted photons, therefore we set the number of grid elements in

Chapter 6 Computation Results of mcml and Verification 63

the angle α direction to 1. The number of photon packets traced is 1,000,000. The actual input file for the program mcml is as follows.

1.0 # file version 1 # number of runs

### Specify data comp.mco A # output filename, ASCII/Binary 1000000 # No. of photons .01 .01 # dz, dr 40 50 1 # No. of dz, dr & da.

3 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.37 1 100 0.90 0.1 # layer 1 1.37 1 10 0 0.1 # layer 2 1.37 2 10 0.70 0.2 # layer 3 1.0 # n for medium below.

Craig Gardner set up the same run for his simulation code except he used only

100,000 photons (Gardner et al., 1992). The total diffuse reflectance and transmittance

from the two simulations are compared in Table 6.7.

Table 6.7. Comparison of total diffuse reflectances and transmittances.

Source Diffuse Reflectance Rd Transmittance Tt

Gardner et al., 1992 0.2381 0.0974

mcml 0.2375 0.0965

The comparison between the diffuse reflectances and transmittances are shown in Figs. 6.9 and 6.10 respectively. The comparison between fluences φrz(r, z), as an impulse

response, as a function of radius r for several given z coordinates is shown in Fig. 6.11. The comparison between fluences φz(z) as a function of z as described in Section 4.2 is

subsequently given in Fig. 6.12. All of the comparisons have shown agreement between

the two independent simulations.

64 Chapter 6 Computation Results of mcml and Verification

10-2

10-1

10 0

10 1

10 2

R d [ cm -2 ]

0 0.1 0.2 0.3 0.4 0.5 r [cm]

Gardner et al.

mcml

Impulse responses

Fig. 6.9. Comparison between diffuse reflectances as a function of radius

based on Gardner's computation (Gardner et al., 1992) and mcml

simulation.

10-2

10-1

10 0

0 0.1

T t [ cm -2 ]

0.2 0.3 0.4 0.5

Gardner et al.

mcml

r [cm]

Impulse responses

Fig. 6.10. Comparison between transmittances as a function of radius

based on Gardner's computation (Gardner et al., 1992) and mcml

simulation.

Chapter 6 Computation Results of mcml and Verification 65

10-1

10 0

10 1

10 2

10 3

10 4

F lu

e n ce

[c m

-2 ]

0 0.1 0.2 0.3 0.4 0.5 r [cm]

mcml z=0.005 cm mcml z=0.205 mcml z=0.395 Gardner et al. z=0.005 Gardner et al. z=0.205 Gardner et al. z=0.395

Impulse Responses

Fig. 6.11. Comparison between fluences as a function of radius r for

several z coordinates based on Gardner's computation (Gardner et al.,

1992) and mcml simulation.

0

0.5

1

1.5

2

F lu

e n ce

[ -]

2.5

3

0 0.1 0.2 0.3 z [cm]

0.4

Gardner et al.

Responses to infinitely wide beam

mcml

Fig. 6.12. Comparison between fluences as a function of z based on

Gardner's computation (Gardner et al., 1992) and mcml simulation.

66 Chapter 6 Computation Results of mcml and Verification

For 2D arrays such as the fluence as a function of r and z, conv has the ability to

output the data in contour format (see Sections 10.9 and 10.10). The fluence as a

function of r and z of the impulse response is shown in contour lines in Fig. 6.13.

0.25

0.2

0.15

0.1

0.05

0

z [c

m ]

0 0.05 0.1 0.15 0.2 0.25 r [cm]

1000 100 50

10

Fig. 6.13. Contour plot of the fluence as a function of r and z of impulse

response based on mcml simulation.

Chapter 7 Convolution for Photon Beams of Finite Size 67

7. Convolution for Photon Beams of Finite Size

This chapter will discuss the principles and implementation of convolving Monte

Carlo simulation results for an infinitely narrow photon beam to yield the responses to

photon beams of finite size. Gaussian beams and circularly flat beams are considered as

special cases.

7.1 Principles of convolution

So far we have only dealt with the response to an infinitely narrow photon beam

normally incident on the surface of a multi-layered tissue. This response is also called the

impulse response. However, all photon beams have finite size in reality. Theoretically, we

can use the Monte Carlo simulation to compute the response to a finite size photon beam

directly by distributing the initial positions of the launched photon packets. The only

problem is that it requires a larger number of photon packets to be traced to get

acceptable variance than simulating the responses of an infinitely narrow photon beam.

Therefore, this method is not efficient although sometimes it might be the only approach

for some types of tissue configurations which can not be convolved, such as a tissue with

an irregular buried object.

Fortunately, the system we are dealing with is linear and invariant. The linearity

means that if the input intensity of the infinitely narrow photon beam is multiplied by a

factor, the responses will be multiplied by the same factor. It also means that the response

to two photon beams is the sum of the responses to each photon beam. The invariance

means that when the infinitely narrow photon beam is shifted horizontally by a distance in

a certain direction, the responses will be shifted also horizontally by the same distance in

the same direction. Therefore, if we assume the photon beam of finite size is collimated,

the response of an infinitely narrow photon beam will be a Green's function of the tissue

system, and the response of the finite size photon beam can be computed from the

convolution of the Green's function according to the profile of the finite size photon beam.

Note that the responses mentioned above can be the internal absorption

distribution, or the reflectance or transmittance distributions. We denote the responses

generally as C(x, y, z) although it may not be a three-variable function of all three

coordinates. Such degeneracy due to the symmetry will be discussed subsequently. We

denote the Green's function corresponding to the type of response under consideration as

68 Chapter 7 Convolution for Photon Beams of Finite Size

G(x, y, z). Since the photon beam is normally incident on the tissue surface, the function

G(x, y, z) possesses cylindrical symmetry. If the collimated photon beam as the source has

the intensity profile S(x, y), the responses can be obtained through convolution (Prahl,

1988; Prahl et al., 1989):

C(x, y, z) = ⌡⌠ –∞

∞ ⌡⌠

–∞

∞ G(x–x', y–y', z) S(x', y') dx' dy' (7.1a)

or through variable transformation with x'' = x – x' and y'' = y – y':

C(x, y, z) = ⌡⌠ –∞

∞ ⌡⌠

–∞

∞ G(x'', y'', z) S(x–x'', y–y'') dx'' dy'' (7.1b)

In Eq. 7.1a the Green's function is a function of the distance between the source

point (x', y') and the observation point (x, y), where the distance is:

d' = (x–x')2 + (y–y')2 (7.2)

If the intensity profile S(x', y') of the source also has cylindrical symmetry, S(x', y') is only

a function of the radius of the source point (x', y') with respect to the origin point of the

coordinate system, where the radius is:

r' = x'2 + y'2 (7.3)

Therefore, Eq. 7.1a can be reformulated considering these symmetries:

C(x, y, z) = ⌡⌠ –∞

∞ ⌡⌠

–∞

∞ G( (x–x')2 + (y–y')2 , z) S( x'2 + y'2) dx' dy' (7.4a)

Similarly, Eq. 7.1b can also be reformulated with these symmetries:

C(x, y, z) = ⌡⌠ –∞

∞ ⌡⌠ –∞

∞ G( x''2 + y''2 , z) S( (x–x'')2 + (y–y'')2) dx'' dy'' (7.4b)

Since the response C(x, y, z) will have the same cylindrical symmetry, the problem

can be more easily handled in a cylindrical coordinate system, where Eqs. 7.4a and 7.4b

can be written:

Chapter 7 Convolution for Photon Beams of Finite Size 69

C(r, z) = ⌡  ⌠

0

S(r') r'   

  

⌡⌠

0

2 π

G( )r2 + r'2 – 2rr'cosθ' , z dθ' dr' (7.5a)

C(r, z) = ⌡  ⌠

0

G(r'', z) r''   

  

⌡⌠

0

2 π

S( )r2 + r''2 – 2rr''cosθ'' dθ'' dr'' (7.5b)

Eq. 7.5b is more advantageous than Eq. 7.5a in computation because the integration over

θ'' is independent of z. This integration hence need only be computed once for all depths z. In some cases as presented subsequently, the integral over θ'' can be expressed analytically, therefore the two-dimensional integral is converted into a one dimensional

integral.

The transformation of variables is illustrated in Fig. 7.1. The first coordinate

system (r', θ', z) has its origin at the center of the source, as in Fig. 7.1A and Eq. 7.5a. The point of observation is at (r, 0). An incremental region of source is located at (r', θ') and has a value S(r'). The distance between the source point and the observation point is

d' equal

r2 + r'2 – 2rr'cosθ' .

The second coordinate system (r", θ", z) has its origin at the center of the point of observation, as in Fig. 7.1B and Eq. 7.5b. The source is centered at (r, 0). The

incremental region of source is located at (r", θ") and has a value S(d"), where d" equals

r2 + r"2 – 2rr"cosθ" . The distance between the source point and the observation point

is r".

In the following sections, we will consider a Gaussian beam and a circularly flat

beam as examples of cylindrical symmetry to further simplify Eq. 7.5b.

70 Chapter 7 Convolution for Photon Beams of Finite Size

Fig. 7.1. Illustration of the transform between two coordinate systems.

The stippled circle is the laser source and P is the point of observation.

The circular lines schematically represent the integration of Eq. 7.5a (A)

and Eq. 7.5b (B).

(A)

r

(r, 0)

θ"(r", θ") r"

d"

(Β)

d'

r'

r

(r, 0)

θ'(r', θ')

d'' =

r2 + r'' 2 - 2rr''cos θ''

d' =

r2 + r' 2 - 2rr'cos θ'

P

P

Chapter 7 Convolution for Photon Beams of Finite Size 71

7.2 Convolution over Gaussian beams

In the case of a Gaussian beam, if the divergence is ignored, the above convolution

can be applied. If the 1/e2 radius of the Gaussian beam is denoted by R, the beam intensity

profile is:

S(r') = S0 exp(–2 (r'/R)2) (7.6)

where the intensity in the center (r=0), S0, is related to the total power P by:

S0 = 2 P / (π R2) (7.7)

Substituting Eq. 7.6 into Eq. 7.5b, the convolution becomes:

C(r, z) = S(r)

⌡   ⌠

0

G(r'', z)exp(–2 (r''/R)2)    

   

⌡⌠ 0

2 π exp(4rr''cosθ''/R2) dθ'' r''dr'' (7.8)

The integration in the square brackets resembles the integral representation of the

modified Bessel function (Spiegel, 1968):

I0(x) = 1

2 π ⌡ ⌠

0

2π exp(x sinθ) dθ (7.9a)

which can be reformatted to be:

I0(x) = 1

2 π ⌡ ⌠

0

2π exp(x cosθ) dθ (7.9b)

Eq. 7.8 can be written by substituting Eq. 7.9b into it:

C(r, z) = S(r) ⌡⌠ 0

∞ G(r'', z) exp(–2 (r''/R)2) I0(4rr''/R2) 2 π r'' dr'' (7.10)

where I0 is the zero order modified Bessel function.

72 Chapter 7 Convolution for Photon Beams of Finite Size

7.3 Convolution over circularly flat beams

If the photon beam is homogeneous within a radius R and collimated, the source

function becomes:

S(r') =   P/(π R2) if r' ≤ R 0 if r' > R

(7.11)

where P is the total power of the beam. Substituting Eq. 7.11 into Eq. 7.5b, the

convolution becomes:

C(r, z) = P/(π R2) ⌡⌠ 0

∞ G(r'', z) Iθ(r, r'') 2 π r'' dr'' (7.12)

where the function Iθ(r, r'') is:

Iθ(r, r'') =

  

1 if R ≥ r + r''

1 π

cos–1((r2 + r''2 – R2)/(2rr'')) if |r – r''| ≤ R < r + r''

0 if R < |r – r''|

(7.13)

From Eq. 7.13 and Fig. 7.1B, the limits of integration in Eq. 7.12 can be changed to a

finite region:

C(r, z) = P/(π R2) ⌡⌠ a

r + R G(r'', z) Iθ(r, r'') 2 π r'' dr'' (7.14a)

where

a = Max(0, r – R) (7.14b)

where the function Max takes the larger of the two arguments.

The circular lines in Fig. 7.1B illustrated the second case in Eq. 7.13 when the

observation point P is outside of the source. When the point P is outside the source, the

first case of Eq. 7.13 is never satisfied. Readers can similarly draw the pictures for the

case where the point P is inside the source. Note that much of the region of integration

lies outside the source and therefore the value of S in the integrand is zero.

Chapter 7 Convolution for Photon Beams of Finite Size 73

As a special case of a circularly flat beam, we let the radius R approach infinity,

which represents an infinitely wide flat beam. In this case the total power P also

approaches infinity, but we can use the power density to describe the intensity of the

beam. The convolution for this case can be accomplished by simply letting R → ∞ and P/(π R2) → S, where S is the power density or irradiance (W/cm2), in Eq. 7.14. Iθ(r, r'')

will be 1 constantly. Eq. 7.14 becomes:

C(r, z) = S ⌡⌠ 0

∞ G(r'', z) 2 π r'' dr'' (7.15)

7.4 Numerical solution to the convolution

In these two special cases of photon beams, the two-dimensional integrations are

converted into one-dimensional integrations, which are significantly faster in numerical

computation. Since the Monte Carlo simulation scores physical quantities to discrete grid

points, the best choice of integration algorithm is the extended trapezoidal rule, which is

written in C called qtrap() by Press et al. (1988). "Increased sophistication will usually

translate into a higher order method whose efficiency will greater only for sufficiently

smooth integrands. qtrap is the method of choice, e.g., for an integrand which is a

function of a variable that is linearly interpolated between measured data points." Press et

al. (1988) state.

The simplest choice of the integration is the summation of the integrand values at

the original grid points multiplied by the grid separation. However, this approach does not

have any control over the integration accuracy. For a given accuracy, sometimes this

approach gives more accuracy than required, which is a waste of computation time, and

sometimes it gives less accuracy, which does not meet the expectation. For example, the

number of original grid elements in the r direction is 50, and we want to convolve the

responses over a circularly flat beam with a radius of R which is about 5 ∆r, where ∆r is the grid separation in the r direction. To compute C(0, z) in Eq. 7.14a, the integral range,

from 0 and R, only covers 5 ∆r. This means only 5 function evaluations will be completed, which may yield unacceptable answer. On the contrary, the extended

trapezoidal rule does the right amount of computation until it reaches the user specified

accuracy.

74 Chapter 7 Convolution for Photon Beams of Finite Size

We have slightly modified the original function qtrap() so that it takes the

required degree of accuracy as an argument. Therefore, the users of the program conv can

change the allowed error at run time (the program conv is to be discussed in the next

section).

The sequence of integrand evaluations used in the extended trapezoidal integration

is shown in Fig. 7.2. (Press et al., 1988) If we are integrating f(x) over [a, b], we evaluate

f(a) and f(b) in the first step as noted by 1 and 2 in Fig. 7.2. This step will not give

sufficient accuracy unless the function if linear. To refine the grid, we evaluate f((a+b)/2)

in the second step as noted by 3. We continue this process until the integration evaluation

reaches the specified accuracy.

Note that the sequence of integrand evaluation after the third evaluation in Fig. 7.2

resembles a perfectly balanced binary tree. If we want to store the evaluated function

values, it is natural to store them in a binary tree for speed retrieval (to be discussed

subsequently).

1 2

3

4 5

6 7 8 9

N = 1

2

3

4

(total after N = 4)

Fig. 7.2. Illustration of integrand evaluation sequence in trapzd() called by

qtrap(). "Sequential calls to the routine trapzd() incorporate the

information from previous calls and evaluate the integrand only at those

new points necessary to refine the grid. The bottom line shows the totality

of function evaluations after the fourth call." (Press et al., 1988) The

sequence of integrand evaluation after the third evaluation resembles a

perfectly balanced binary tree.

Chapter 7 Convolution for Photon Beams of Finite Size 75

Interpolation and extrapolation of physical quantities

As shown in Fig. 7.2, the function qtrap() will need to evaluate the integrand,

Original data points Interpolation Extrapolation

Nr = 8

G (r

, z)

0 1 2 3 4 5 6

r/ ∆∆r 7 8 9

Nr - 0.5

1 2 1

2

3

4

(total after N = 4)

N

3

4 5

6 7 8 9

a b

Fig. 7.3. Illustration of the interpolation and

extrapolation of the physical quantities. As an

example, the number of grid elements in the r direction Nr is set to 8. ∆r is the grid separation in

the r direction. The integral limits are a and b (see

Eqs. 7.21 & 7.22). The arrows point to the places

where the integrand is evaluated.

76 Chapter 7 Convolution for Photon Beams of Finite Size

hence the physical quantities, at points which may not be the original grid points. Linear

interpolations are used for those points that fall between two original grid points. Linear

extrapolations are used for those points that fall beyond the original grid system. The

interpolation and extrapolation are illustrated in Fig. 7.3. The solid circles represent the

original score values at the grid points. The solid lines and the dashed lines represent the

interpolation and extrapolation respectively. For a given number of grid elements in the r direction (e.g., Nr = 8 in this picture), the extrapolation is only computed up to (Nr - 0.5)

because the linear extrapolation can be unreliable for points beyond (Nr - 0.5). Therefore,

the physical quantity is set to zero beyond (Nr - 0.5). Sometimes, the data may be so

noisy that the function at the last point is even higher than the function at the second to the

last point. In this case, the extrapolation is not used. Instead, we simply set the function

values to zero. Note that as we mentioned in the beginning of Chapter 4, the last cells in

the r direction are used to collect the photons that do not fit into the grid system.

Therefore, the values in the last cell are usually much higher than the values in the second

to the last cells, and hence are not used in the convolution process. In a word, the physical quantities are non-zero in the interval [0, rmax], where rmax is:

rmax = (Nr – 0.5) ∆r (7.16)

where ∆r is the grid separation in the r direction.

Integrand evaluation for Gaussian beams

As shown in Eq. 7.10, the evaluation of the physical quantities is only part of the

integrand evaluation for convolution over the Gaussian beam. Although the integration

has to converge due to physical reasons, the form of Eq. 7.10 may not be directly

computed numerically because the modified Bessel function increases rapidly as the

argument increases, and it can exceed the limit which the computer can hold (e.g., 10+38

for some computers). Therefore, a proper reformulation is required to compute Eq. 7.10.

We note that the modified Bessel function in the region where the argument is large has

the following approximation:

I0(x) ≈ exp(x) / 2 π x for large x (7.17)

Therefore, if we extract the exponential term from I0(), we can make sure the modified

Bessel function decreases as the argument increases. We define the following new function based on I0():

Chapter 7 Convolution for Photon Beams of Finite Size 77

I0e(x) = I0(x) exp(–x) (7.18a)

or

I0(x) = I0e(x) exp(x) (7.18b)

I0e() should always be bounded. Note that Eq. 7.17 is presented just to show the

asymptotic behavior of the function I0(). Eq. 7.18a by no means carries any

approximations. Substituting Eqs. 7.6, 7.7 and 7.18b into Eq. 7.10, it becomes:

C(r, z) = 4 P R2 ⌡

 ⌠

0

G(r'', z) exp[–2 ( r''– r R )

2] I0e( 4rr'' R2

) r'' dr'' (7.19)

Since both the exponential term and the I0e() term decrease, the integrand can be

computed without being out of bound.

Up to now, we have just solved the problem of how to compute the convolution

without overflow. However, the computation speed is another issue. We found that the evaluation of the exp() I0e() in Eq. 7.19 is a major part of the computation for each

integration, which can be up to 90% depending on the specific problem being solved. For

multi-variant physical quantities (e.g., A(r, z)), the convolution may repeatedly evaluate the exp() I0e() in Eq. 7.19 at the same point as the integration is computed for different z

coordinates.

Therefore, if we can save the function evaluations, i.e. the computations of exp() I0e() in Eq. 7.19, for one z coordinate, then we can save a lot computation time.

However, the integration is executed iteratively until a given precision is reached. Hence,

the number of function evaluations is unknown in advance. We can only save the function

evaluations with dynamic data allocation. Furthermore, since the evaluation sequence of

the trapezoidal integration qtrap() resembles a binary tree as in Fig. 7.2., a well-balanced

binary tree can be used to store the function evaluations for searching speed.

Integral Limits for Gaussian beams

Since the integral limits in Eq. 7.14a for circularly flat beams are finite, the

integration can be computed directly using the function qtrap(). In contrast, the upper

integral limit in Eq. 7.19 for Gaussian beams is infinity. This problem can be solved using

78 Chapter 7 Convolution for Photon Beams of Finite Size

variable transformation and the integration can be computed by the routine midexp()

(Press et al., 1988). However, we found this approach is not computationally efficient.

We can reduce the upper limit to a finite value by properly truncating the exponential term

in Eq. 7.19. When

|r" – r| ≤ K R (7.20a)

or

r – K R ≤ r" ≤ r + K R (7.20b)

where K is a constant which can be set in the convolution program conv, we compute the

integrand, otherwise we think the integrand negligible. For example, if we choose K equal

to 4 (which is actually used in the program), the exponential term in Eq. 7.19 is about

1x10–14 whose order of magnitude is considerable larger than the dynamic range of the

order of magnitude of the scored physical quantities.

As we discussed in the beginning of this section, we only compute the physical quantities in the interval [0, rmax], where rmax is given by Eq. 7.16. Combining this limit

and Eq. 7.20b, Eq. 7.19 becomes:

C(r, z) = 4 P R2 ⌡

 ⌠

a

b

G(r'', z) exp[–2 ( r''– r R )

2] I0e( 4rr'' R2

) r'' dr'' (7.21a)

a = Max(0, r – K R) (7.21b)

b = Min(rmax, r + K R) (7.21c)

where the functions Max() and Min() take the greater and the lesser of the two arguments

respectively.

Integrand evaluation for circularly flat beams

The integrand evaluation for circularly flat beams is much simpler than that for

Gaussian beams. However, the evaluation of Iθ() in Eq. 7.14a is time-consuming.

Similar to the integrand evaluation for Gaussian beams, a binary tree is used to store the

evaluated Iθ() to speed up the integration (see the discussion for Gaussian beams).

Chapter 7 Convolution for Photon Beams of Finite Size 79

Integral Limits for circularly flat beams

Since the integral limits in Eq. 7.14a for circularly flat beams are finite, the

integration can be computed directly using the function qtrap(). As we discussed in the beginning of this section, we only compute the physical quantities in the interval [0, rmax],

where rmax is given by Eq. 7.16. Considering this limit, Eq. 7.14a becomes:

C(r, z) = P/(π R2) ⌡⌠ a

b G(r'', z) Iθ(r, r'') 2 π r'' dr'' (7.22a)

a = Max(0, r – R) (7.22b)

b = Min(rmax, r + R) (7.22c)

where the functions Max() and Min() take the greater and the lesser of the two arguments

respectively.

Source of error in convolution

In Eqs. 7.21c and 7.22c, the upper limit of the integration may be limited by rmax

which is the grid limit in the r direction during the Monte Carlo simulation. The physical

quantities beyond the original grid limit in the r direction do not contribute to the

convolution, which leads an error.

Let us discuss the case for circularly flat beams first because it is easier. From Eq.

7.22c, we know that when

rmax ≥ r + R (7.23a)

or

r ≤ rmax – R (7.23b)

the limited grid in the r direction does not affect the convolution. Otherwise, the

convolution is truncated by the limited grid in the r direction. This effect can be see in the next section. Therefore, we should not trust the convolution data for r ≥ rmax – R. In

other words, if you want to observe the physical quantity at r in response to a circularly

80 Chapter 7 Convolution for Photon Beams of Finite Size

flat beam of radius R, the grid limit in the r direction should be large enough so that Eq.

7.23a holds when you perform the Monte Carlo simulation with mcml.

For Gaussian beams, there are no clean formulas like Eqs. 7.23 to describe the

valid range because the Gaussian beams theoretically extend to infinity in the r direction.

However, the convolution results of a Gaussian beam with a 1/e2 radius of R is so close

to those of a circularly flat beam with a radius of R for r >> R (shown in the next section).

Therefore, we can use the same criteria for circularly flat beams (Eqs. 7.23) for Gaussian

beams to certain precision.

The other source of error is due to the Monte Carlo simulation by mcml 1.0. This

version of mcml does not score the first interactions separately (see Section 4.3) as

Gardner et al. (1992b) did. This may make considerable error if the radius of the

Gaussian beam is less than three times the grid separation ∆r. In other words, the following equation should be satisfied to get reliable convolution:

R ≥ 3 ∆r (7.24)

In summary, when Eqs. 7.23 and 7.24 hold, the convolution should be reliable.

7.5 Computation results of conv and verification

The convolution process of the Monte Carlo simulation results from mcml is

implemented in another program called "conv". Like the program mcml, it is written in

ANSI Standard C, hence it can be executed on a variety of computers.

Convolution results of Gaussian beams

We do not have any standard data to verify the convolution program. However,

Craig Gardner kindly provided some convolution results using his convolution program

(Gardner et al., 1992). The impulse responses are based on the simulation discussed in

Section 6.6 where we have used the same turbid media and grid system. He computed his

convolution on his Monte Carlo simulation results, and we did it on ours after we

compared the Monte Carlo simulation results in Section 6.6.

The incident photon beam is a Gaussian beam with total energy of 1 J and radius of

0.1 cm. The convolved diffuse reflectances and transmittance are compared in Figs. 7.4

and 7.5 respectively. The convolved fluences are compared in Fig. 7.6. Note that the

Chapter 7 Convolution for Photon Beams of Finite Size 81

curves in Figs. 7.4-6 bend down faster near r equal 0.5 cm. This can be explained by the

integrations in Eqs. 7.21. Due to the spatially limited range of the grid system (50 radial grids of 0.01 cm spacing, or rmax = 0.5 cm), the upper limit of the integration is cut by

rmax more and more as the observation point r approaches rmax. Therefore, the

integration underestimates the true value.

10 -2

10 -1

10 0

10 1

0

R d [J /c

m

2 ]

0.1 0.2 0.3 0.4 0.5

Gardner et al.

r [cm]

conv

Gaussian beam responses Total energy: 1 J 1/e 2 radius: 0.1 cm

Fig. 7.4. Comparison between diffuse reflectances as a function of r based

on conv and Gardner's computation (Gardner et al., 1992).

10 -2

10 -1

10 0

0 0.1

T t [ J/

cm

2 ]

0.2 0.3 0.4 0.5

Gardner et al. conv

r [cm]

Gaussian beam responses Total energy: 1 J 1/e 2 radius: 0.1 cm

82 Chapter 7 Convolution for Photon Beams of Finite Size

Fig. 7.5. Comparison between transmittances as a function of r based on

conv and Gardner's computation (Gardner et al., 1992).

10 -1

10 0

10 1

10 2

0

F lu

e n ce

[J /c

m

2 ]

0.1 0.2 0.3 0.4 0.5

Gardner et al. z=0.005

r [cm]

Gardner et al. z=0.205

conv z=0.005 cm

conv z=0.205 cm

Gaussian beam Total energy: 1 J

1/e 2 radius: 0.1 cm

Fig. 7.6. Comparison between fluences as a function of r for given z

coordinates based on conv and Gardner's computation (Gardner et al.,

1992).

For 2D arrays such as the fluence as a function of r and z, conv has the ability to

output the data in contour format (see Sections 10.9 and 10.10). The fluence as a

function of r and z of the Gaussian beam is shown in contour lines in Fig. 7.7.

Chapter 7 Convolution for Photon Beams of Finite Size 83

0.4

0.3

0.2

0.1

0

0 0.1

z [c

m ]

0.2 0.3 0.4

air: n = 1

layer 1: n = 1.37 µa = 1, µs = 100

g = 0.9

layer 2: n = 1.37 µa = 1, µs = 10

g = 0

r [cm]

layer 3: n = 1.37 µa = 2, µs = 10 g = 0.7

air: n = 1

50 25 10 5 2.5 1 0.5

Fig. 7.7. Contour plot of the fluence [J cm−2] as a function of r and z

based on conv for a Gaussian beam. The Gaussian beam has total energy

of 1 J and 1/e2 radius of 0.1 cm. The Monte Carlo simulation is for a three-layer tissue of Table 6.6. The absorption coefficient µ a and

scattering coefficient µs are in cm–1.

Convolution results of circularly flat beams

For circularly flat photon beams, we do not have other results for comparison.

However, we would like to compare the results of circularly flat photon beams with that

of Gaussian beams. The results of Gaussian beams are taken from the above

computations. The circularly flat beam has 1 J of total energy and 0.1 cm of radius. The

diffuse reflectances, transmittances and fluences are compared respectively in Figs. 7.8,

7.9, and 7.10.

84 Chapter 7 Convolution for Photon Beams of Finite Size

10 -2

10 -1

10 0

10 1

0

R d [J /c

m

2 ]

0.1 0.2 0.3 0.4 0.5

Flat

r [cm]

Gaussian

Total energy: 1 J 1/e 2 radius: 0.1 cm Computed by conv

Fig. 7.8. Comparison between diffuse reflectances as a function of r

convolved over a Gaussian beam and a flat beam using conv. Both beams

have total energy of 1 J and radii of 0.1 cm.

10 -2

10 -1

10 0

0 0.1

T t [ J/

cm

2 ]

0.2 0.3 0.4 0.5

Flat Gaussian

r [cm]

Total energy: 1 J 1/e 2 radius: 0.1 cm Computed by conv

Fig. 7.9. Comparison between transmittances as a function of r convolved

over a Gaussian beam and a flat beam using conv. Both beams have total

energy of 1 J and radii of 0.1 cm.

Chapter 7 Convolution for Photon Beams of Finite Size 85

10 -1

10 0

10 1

10 2

0

F lu

e n ce

[J /c

m

2 ] 0.1 0.2 0.3 0.4 0.5

Flat

r [cm]

Gaussian

Computed by conv z = 0.005 cm Total energy: 1 J Radius: 0.1 cm

Fig. 7.10. Comparison between fluences as a function of r at z equal 0.005

cm convolved over a Gaussian beam and a flat beam using conv. Both

beams have total energy of 1 J and radii of 0.1 cm.

It is observed that the Gaussian beam and the flat beam give nearly the same

results when r is larger than about 2 R, where R is the radius of the beams. Furthermore,

both kinds of responses bend down when r approaches 0.5 cm which is the grid limit in the

r direction as discussed in last section.

Convolution error

The convolution integration is computed iteratively. The iteration stops when the

difference between the new estimate and the old estimate of the integration is a small part

of the new estimate. This small ratio can be controlled by users using command "e". It

ranges between 0 to 1. Small values would give better precision but longer computation

time and vice versa. Normally, 0.001 to 0.1 is recommended. Sometimes, a high allowed

error can cause some discontinuity in the convolved results. If this happens, choose a

lower allowed convolution error and redo the convolution. For example, the convolution

over a Gaussian beam in Fig. 7.10 has been done with an allowed convolution error of

0.001. If we choose the allowed convolution error to be 0.01, we can see the

discontinuity in the fluence distribution (Fig. 7.11).

86 Chapter 7 Convolution for Photon Beams of Finite Size

10 -1

10 0

10 1

10 2

0

F lu

e n ce

[J /c

m

2 ]

0.1 0.2 0.3 0.4 0.5

Error = 0.1%

r [cm]

Error = 1%

discontinuity

Computed by conv z = 0.005 cm Gaussian beam Total energy: 1 J Radius: 0.1 cm

Fig. 7.11. Comparison between fluences as a function of r at z equal 0.005

cm convolved over a Gaussian beam with different allowed convolution

errors using conv. The result with allowed error of 0.01 has discontinuity

around r = 0.15 cm. Using an allowed error of 0.001 eliminates the

discontinuity. The Gaussian beams have total energy of 1 J and 1/e2 radii

of 0.1 cm.

Chapter 8 Installing mcml and conv 87

Part II. User Manual

8. Installing mcml and conv

This chapter provides the instructions on how to install the software. Since both

mcml and conv are written in ANSI Standard C, they in principle should be able to be

compiled on any computer systems that support ANSI C. Subject to the computer

systems available to this laboratory, we will only provide the executables for Sun

workstations, IBM PC compatibles, and Macintoshes. On Sun SPARCstations 2, we have

compiled the mcml and conv using the ANSI C (acc). On IBM PC compatibles, we used

Microsoft QuickC. And on Macintoshes, we used Symantec THINK C. We will provide

the source code, users can feel free to compile them on their computer systems. Consult

corresponding manuals for information on how to compile the code.

As Monte Carlo simulations are computationally intensive, we suggest that you

use workstations such as Sun SPARCstations on which you can submit background jobs

and which provide high speed computation. The convolution program is also more

pleasant to use if you have a fast computer, although it is not as computation-intensive as

Monte Carlo simulations.

8.1 Installing on Sun workstations

The distribution disk is an IBM format double density 3 1/2" disk, which Sun

SPARCstation 2 should be able to read. All the files are packed into one file. Copy the

file mcR1_1.tar to a working directory using the command: mcopy a:mcR1_1.tar ., where

the period means the current directory, and then untar the file using the command: tar -

xvfo mcR1_1.tar.

The package includes three directories: mcmlcode, convcode, and Sun. The

directory mcmlcode includes all the source code of mcml and the makefile used for acc.

The directory convcode (not provided this time) includes all the source code of conv and

the corresponding makefile. You need to modify the makefiles for other compilers (See

Appendix C). The directory Sun includes all the executables, a template file of mcml input

(template.mci), a sample mcml output file (sample.mco), and a short manual

(mcmlconv.man) which is Chapter 8-10 of this manual.

88 Chapter 8 Installing mcml and conv

To install the executables, copy the executables to the sub directory ~/bin under

your home directory. Then, put the directory ~/bin under the search path in .cshrc or

.login if you are using C Shell. Consult manual if you are using other shells.

Having finished copying, you can eject the disk using the command eject. If your

Sun workstation does not have a floppy drive, you can transfer the files through a

networked IBM PC or a compatible. If you have an electronic mail address, we can also

send the package to you through mail.

8.2 Installing on IBM PC compatibles

For IBM PC's or compatibles, the distribution disk is a double density 3 1/2" disk.

An alternative 5 1/4" disk can be sent upon request. All the files are packed into one file.

Copy the self-extracting file mcR1_1.exe to your working directory on your hard drive

and run the file to extract all packed files using the command: mcR1_1.exe -d, where the

option "-d" keeps the directory structure.

The package includes three directories: mcmlcode, convcode, and IBMPC. The

directory mcmlcode includes all the source code of mcml. The directory convcode (not

provided for now) includes all the source code of conv. The directory IBMPC includes all

the executables, a template file of mcml input (template.mci), a sample mcml output file

(sample.mco), and a short manual (mcmlconv.man) which is Chapter 8-10 of this manual.

The executables include mcml.exe and conv.exe. The code was compiled and linked using

Microsoft QuickC 2.5. The executables will be able to detect whether your computer has

math coprocessor, and take advantage of the math coprocessors if they are present.

If you want to be able to execute the programs under any directory, you should

put the directory IBMPC in the search path. The search path can be changed in the file

autoexec.bat.

8.3 Installing on Macintoshes

For Macintoshes, the distribution disk is a double density 3 1/2" disk. All the files

are packed into one file. Copy the self-extracting file mcR1.1.sea to a working folder on

you hard drive, and double click on the icon to extract the files.

The package includes three folders: mcmlcode, convcode, and Mac. The folder

mcmlcode includes all the source code of mcml. The folder convcode includes all the

Chapter 8 Installing mcml and conv 89

source code of conv. The folder Mac includes all the executables, a template file of mcml

input (template.mci), a sample mcml output file (sample.mco), and a short manual

(mcmlconv.man) which is Chapter 8-10 of this manual. The executables include

mcml.fpu, conv.fpu, mcml.020, conv.020, mcml.000, and conv.000 for different types of

computers as discussed subsequently.

Before you install the executables, you need to know what kind of Macintosh you

are using. You can test the following conditions to decide which executables to use:

A. MC68040

B. MC68020 or MC68030

C. MC68881 or MC68882

If your Macintosh meets condition A, or conditions B and C, you should copy the

executables with extensions ".fpu". If your Macintosh meets condition B only, you should

keep the executables with extensions ".020". Otherwise, you should use the executables

with extensions ".000". We suggest that you remove the extensions of the executables on

your hard drive to keep consistency with the manual.

8.4 Installing by Electronic Mail

For these users who have electronic mail access on UNIX machines, we can

deliver the software package through electronic mails. The package is archived using the

command tar, compressed using the command compress, then encoded using the

command uuencode before it is mailed out using the mail utilities. After you receive the

mail, you need to do the following.

1. Save the mail as a file, e.g., mc.mail.

2. Decode the file (mc.mail) to get a file named mc.tar.Z using

uudecode mc.mail

3. Uncompress the file mc.tar.Z to get the file mc.tar

uncompress mc.tar.Z

4. Unarchive the file mc.tar to get the package using:

90 Chapter 8 Installing mcml and conv

tar -xvfo mc.tar

At this moment, you should have three directories under the working directory. They are

mcmlcode, convcode, and Sun, or IBMPC, or Mac.

If you ordered a Sun version of the package, you only need to put the executables

under the proper directory., e.g., ~/bin (see Section 8.1).

If you ordered an IBM PC version or a Mac version of the package, you need to

transfer the files to your local computer using FTP or modem. Then refer to Section 8.2

or 8.3 for details.

It is appropriate to describe in more detail how we send the package through

electronic mails which is exactly the opposite of the above procedure. We put the

package in a working directory which include three subdirectories: mcmlcode, convcode,

and Sun, or IBMPC, or Mac. Then:

tar -cvf mc.tar

compress mc.tar

uuencode mc.tar.Z mc.tar.Z > mc.mail

mail your_address

In the mail utility, you can add in any messages in the beginning of the mail, then you need

to use the command r to read in the file mc.mail. Then, you can send the file by typing a

period "." and a return in a new line (see the manual page of mail).

Chapter 9 Instructions for mcml 91

9. Instructions for mcml

This chapter describes the actual instructions to use mcml. Macintoshes, IBM PC

compatibles and UNIX machines are used as examples of computer systems, although

mcml can execute on any computer systems that support ANSI Standard C. The reader is

assumed to be familiar with the operating system and comfortable with at least one of the

text editors on the computer system to be used to execute mcml. Three steps involved in

the Monte Carlo simulation using mcml are included in the following sections: preparing

the input data file, executing the program mcml with the input data file, processing the

output data in the data files named in the input data file. We will also show some known

bugs.

9.1 File of input data

The first step to do the simulation using mcml is to prepare an input data file (e.g.,

"filename.mci"). Any valid filenames on your system without spaces will be acceptable,

but extension ".mci" is recommended. In ANSI C, spaces are used as separators.

Therefore, filenames with spaces may not be accepted by mcml, although they are allowed

by some operating systems themselves such as the Macintosh System. We will use

"filename.mci" as an example in the following discussions.

This input data file may be edited with any text editors such as Apple Edit,

MockWrite or Microsoft Word on Macintoshes, Norton editor NE or Microsoft Word on

IBM PC compatibles, vi editor or EMACS on UNIX systems. However, if you use word

processors like Microsoft Word to edit the file, make sure that you save the file in text

format since mcml does not accept binary files as input. If you are using the UNIX system

and are uncomfortable with vi or other editors available on UNIX, you can use editors on

your personal computer, then transfer the file using Kermit if you use modem or FTP if

your personal computer is on a network. Make sure to use ASCII or text mode when you

transfer this file.

The best way to write an input data file is to make a copy of the template file

called "template.mci" (See Appendix D), then modify the parameters in the file. The input

data file is organized line by line. All parameters must be in the right order. The lines with

parameters in order must also be in order themselves. However, feel free to insert

comment lines or space lines in between to make the file more readable. Comment lines

92 Chapter 9 Instructions for mcml

start with the symbol "#". The symbol "#" can also be used after the parameters in a line

to mark the start of comments.

The parameters in the input data file are read by mcml line by line. If there are

multiple parameters in a line, use tabs or spaces to separate them. A tab is preferred,

because it aligns the parameters for better readability. All dimensional quantities are in cm

or derived from cm. The thickness of each layer is in cm. The grid line separations are

also in cm. Absorption coefficient and scattering coefficient are in 1/cm. Each line of the

input file is explained in the order that they appear in the input data file as follows.

1. File version of the input data file. Always use "1.0" for now.

2. Number of runs (integer). Each run is an independent simulation. You can specify

any number of runs, which is not subject to memory limit. Make sure you use an

integer instead of a floating point number for this parameter, e.g., 5 instead of 5.0.

3. Output filename and file format. Extension ".mco" is recommended for the output

filenames, e.g., "output1.mco". The program mcml currently only supports ASCII

format, therefore always use "A" as the second parameter in this line. Make sure

that you use different output filenames if you have multiple runs in an input data

file, although mcml checks for this mistake. What is more important is that the

filenames should not be the same as the names of existent ones unless you want to

overwrite the existent files on purpose. Since the program mcml does not check

this error, you will lose the existent files.

4. Number of photon packets to be traced (integer).

5. Separations (in cm) between grid lines in z and r direction of the cylindrical

coordinate system. These are floating point numbers. Both z and r originate from

the photon incident point on the surface of first layer, and the z axis points down

into the turbid medium. Make sure these parameters are large enough to give you

an acceptable variance, and small enough to give you an acceptable resolution.

These parameters should be determined coordinately with the number of photons

to achieve both accuracy and resolution. Also note that users should try to choose

grid size in the z direction so that grid boxes do not cross tissue-tissue interfaces

or boundaries (see Section 9.5).

Chapter 9 Instructions for mcml 93

6. Number of grid elements (integers) in the z, r directions of the cylindrical

coordinate system and in the alpha direction, where alpha is the angle spanned

between the photon exiting direction and the surface normal. Since the angle

always covers 0 through 90 degrees, the angular separation is 90 degrees divided

by the number of angular grid elements specified in this line. Be careful with this

line, if the numbers are too large, the output file will be very big because 2D arrays

are written into the output file. If you do not need to resolve one of the directions

(z or r) or the angle, use 1 (not 0) for that parameter. Make sure to use integers

for these three parameters.

7. Number of layers (integer). This number does not include the ambient media above

or below the tissue.

8. Refractive index for the top ambient medium above the first layer (e.g., 1.0 for

air).

9. Layer parameter lines. One line for each layer. In each line are the refractive

index, the absorption coefficient (1/cm), the scattering coefficient (1/cm), the

anisotropy factor, and the thickness (cm). To simulate semi-infinite tissue, use a

very large thickness (e.g., 1E8 cm) compared with the mean free path of the tissue.

10. Refractive index for the bottom ambient medium below the last layer (e.g., 1.0 for

air).

11. Repeat lines 3 through 10 for each additional run if you have multiple runs.

Note: Two points are worth noting. The only limit to the number of grid elements

and layers is the amount of memory allocated to mcml in your system because the arrays

are dynamically allocated according to these parameters. Do not use floating point

numbers for the integers. Otherwise, the program may interpret them incorrectly.

However, you may use integers for floating point numbers, e.g., 100 instead of 100.0.

9.2 Execution

Once the input data file is prepared, the program mcml can be executed using the

input data file. During the execution, the program mcml will report an output message

which gives the number of photons remaining in the simulation, the number of runs left,

and the time of ending the job. The first report is after 10 photon packets are traced, then

94 Chapter 9 Instructions for mcml

it is updated when every 1/10 of the total number of photon packets are traced. The

methods of execution are slightly different on different operating systems.

Macintosh

To run mcml on Macintosh System 6, you have to copy or move the executable

mcml to your working folder where the input data file resides, then double click on the

mcml icon to start the program. The program mcml will prompt for the input data

filename, which is entered through the keyboard. If the input data file cannot be found,

the program will prompt you again until it finds the file or a period "." is typed, where "."

is used to abort the program. If you use Macintosh System 7, you may use an alias of

mcml instead of a copy of it.

IBM PC compatibles

For IBM PC compatibles, make sure that the directory with mcml is in the search

path, which can be checked by typing the command "path" or the file "autoexec.bat". To

run mcml with the input data file as a command parameter under DOS command prompt,

type:

mcml filename.mci

If you want to save the output message as a file (e.g., message.out), type:

mcml filename.mci > message.out

which redirects the output message to the file "message.out". To run mcml in the

interactive mode, type the following command without input data filename:

mcml

Then, the program mcml will prompt for the input data file.

UNIX

On a UNIX system, you should place the executable mcml in a directory that is in

the search path. The directory ~/bin is a good choice. The search path can be found and

modified in the file ".cshrc" if you are using C Shell or the file ".login". The three ways of

Chapter 9 Instructions for mcml 95

invoking mcml under DOS can be used under UNIX operating systems. Moreover, if you

wish to discard the messages during the execution, use the command:

mcml filename.mci > /dev/null

which redirects the output to the "bit bucket" (/dev/null). You can also simply submit a

background job using:

mcml filename.mci > /dev/null &

Refer to your UNIX manual for how to inquire about the status of a background

job. If you are still in the same session, the command "jobs" can be used in C Shell.

Otherwise, you should use the UNIX command "ps" to check for background processes.

You can also directly look for the output files to check if the job is done.

9.3 File of output data

When the job is completed, the results will be written into the output data files as

you named in your input data file. A sample output data file is shown in Appendix E. The

output data files can be read with any text editors if they are ASCII as a result of using

"A" for the file format in the input data file. They may be big if your numbers of grid

elements are large.

The contents of output files are self explanatory. The same policy for the input

data file is used for the output data file, that is, comment lines starting with a symbol "#"

and space lines are written to the file for clarity. The first line is used for file type

identification when the file is read by other applications. Then, the user time spent on the

simulation is reported in a comment line. Then, a few categories of data are reported

sequentially in the following order: pure numbers, 1D arrays and 2D arrays. The

definitions of the output data can be found in Chapter 4. The category "InParm" reports

all the input parameters specified in the input data file again so that the output file is a

complete reference and the input parameters may also be double checked against any

errors in the input data file. The category "RAT" reports the specular reflectance, the

total diffuse reflectance, the total absorption, and the total transmittance. The category

"A_l" is the absorption as a function of layer. The category "A_z" is the absorption as a

function of depth z. The categories "Rd_r" and "Rd_a" are the diffuse reflectances as a

function of radius r and angle alpha respectively. The categories "Tt_r" and "Tt_a" are the

transmittance as a function of radius r and angle alpha respectively. The 2D arrays A_rz,

96 Chapter 9 Instructions for mcml

Rd_ra, and Tt_ra are then reported. The category A_rz is the absorption as a function of

depth z and radius r. The categories Rd_ra and Tt_ra are correspondingly the diffuse

reflectance and transmittance as a function of radius r and angle alpha. The name of each

category is written before the data, such that the data can be easily identified. The units

for these data were discussed in Chapter 4.

9.4 Subset of output data

Sometimes, only a subset of the output data is needed for presentation or

processing. For example, we may need to print a 1D array into a file in XY format,

namely two columns of data, or a 2D array in XYZ format. These files can then be read

into some commercial applications such as AXUM on IBM PC compatibles and

KaleidaGraph on Macintoshes. This subset extraction can be done using another program

-- conv. The program conv is intended to read in the output data file of mcml that gives

responses of infinitely narrow photon beam, and convolve the output data if the responses

of finite size beam are to be computed. The program conv can output the original data or

the convolved data in various formats. The convolution part of the program conv has not

been finished, although it can be used to extract subsets of the original output data.

The program conv is made to be interactive. After the program is invoked, the

menu system will direct the data input, output, or process. On Macintosh, copy or move

the program conv to your working folder. Start conv by double clicking on the icon. On

IBM PC compatibles or UNIX machines, invoke the program conv by typing:

conv

Follow the menu to input an mcml output file (e.g., "filename.mco"). Then, output

specific data to new files. However, for the sake of efficiency, we wrote a C Shell script

file "conv.bat" for UNIX users. For shell programming, refer to Anderson et al. (1986) or

Arthur (1990). A similar file can be written on MS-DOS operating system. The file

"conv.bat" is used for fast batch process. For example, if there are several mcml output

files named "outfile1.mco", "outfile2.mco"... "outfilen.mco", and you need to select the

diffuse reflectance as a function of radius r of these mcml output files on a UNIX system,

then you can use the command:

conv.bat "outfile*.mco" Rr

Chapter 9 Instructions for mcml 97

This command takes two arguments. The first one gives the mcml output files to be

processed. If wild cards, such as * or ?, are used, the argument has to be within quotes to

prevent immediate file expansion. The second argument gives the type of subsets to be

extracted. In this case, it is the diffuse reflectance as a function of r. The files of the

subsets will be named as "outfile*.Rr". In each of these output files, there are two

columns, the first one is the radius, and the second one is the reflectance. To check the

complete usage of "conv.bat", type "conv.bat" on command line. More examples are:

conv.bat "outfile*.mco" Az

for 1D absorption as a function of z. In each of the output files of this command, there

are two columns representing z and the internal absorption respectively.

conv.bat "outfile*.mco" Azr

for 2D absorption as a function of z and r. In each of the output files of this command,

there are three columns representing z, r, and the internal absorption respectively.

If you use UNIX to do the simulation and want to present the results using

Macintosh or IBM PC compatibles, transfer the smaller subset files using KERMIT if you

use modem or FTP if you use Ethernet.

9.5 Bugs of mcml

1. Users have to be careful with several known bugs about the program mcml version

1.0. If a grid element crosses a medium interface, e.g., a glass/tissue interface, the

photon absorption within this grid element is considered to be the absorption in the

medium where the center of the grid element is located. Therefore, if the center is

on the side of the glass, mcml may report small absorption in the glass.

Sometimes, this problem may be avoided by choosing the z-grid system carefully

so that the boundaries of elements align with the layer interfaces.

2. The user time of a simulation can be reported as zero if the simulation is long

enough to overflow the timer (See the function clock() in the file "mcmlmain.c").

3. The input parameters in the input data file have to be in the order as specified.

Furthermore, you have to use integers for number of photon packets, number of

layers, and number of grid elements in the input data file. If floating point numbers

98 Chapter 9 Instructions for mcml

are inadvertently used, mcml can not detect the error and may read in the wrong

parameters.

If you find any new bugs, please report to us using the information in Appendix G.

It is very important that you provide us enough information about the bug so that we can

reproduce it.

Chapter 10 Instructions for conv 99

10. Instructions for conv

This chapter describes the instructions to use the program conv, which is used to

convolve the impulse responses of mcml over incident beams of finite size. This program

reads the output of mcml, then convolves the impulse responses according to the user

specified incident beams. The program can output the original data from mcml or the

convolved data in various ASCII formats as discussed subsequently.

10.1 Start conv

To start conv on IBM PC compatibles or UNIX machines, invoke the program

conv by typing:

conv

To use conv on Macintoshes, copy or move the program conv to your working folder.

Then, double click the conv icon to start it. If you are using System 7, you may take

advantage of the alias mechanism.

10.2 Main menu of conv

Once conv is started, it is in the main menu of the program after showing some

information about the program. In the main menu, the program prompts for a command

as:

> Main menu (h for help) =>

To show all the available command, type "h" and return key. It will show you the

following information and prompt for the next command. You only need to show the help

information when you forget the commands.

i = Input filename of mcml output b = specify laser Beam r = convolution Resolution. e = convolution Error. oo = Output Original data oc = Output Convolved data co = Contour output of Original data cc = Contour output of Convolved data so = Scanning output of Original data sc = Scanning output of Convolved data q = Quit * Commands in conv are not case-sensitive

> Main menu (h for help) =>

100 Chapter 10 Instructions for conv

Each command will be introduced subsequently.

10.3 Command "i" of conv

You have to provide the filename of the mcml output to conv. This can be done

by typing "i" and return key in the main menu prompt, then type in the filename of the

mcml output. For example:

> Main menu (h for help) => i Input filename of mcml output(or . to quit): example.mco

> Main menu (h for help) =>

The program returns to the main menu automatically. If the file cannot be located or

opened, the program will prompt you to type in another filename. You can also type "."

and return key to quit inputting the filename. If the file is not the output of mcml, the

program will quit to the operating system. You need to start the program again.

10.4 Command "b" of conv

You need to specify the type and parameters of the incident beam. In version 1.0

of conv, only Gaussian beams and circularly flat (rectangular) beams are supported. To

enter the incident beam, use command "b". Then you have to choose from "f" for flat

beam, "g" for Gaussian beam, or "q" to quit this command. If you choose either flat beam

or Gaussian beam., conv asks the total energy and the radius of the beam. For example:

> Main menu (h for help) => b Beam profile:f=flat, g=Gaussian. q=quit: f Total energy of the flat beam [J]: 1 Radius of the flat beam [cm]: .1 Total power: 1 J, and radius: 0.1 cm.

> Main menu (h for help) =>

It returns to the main menu automatically. Although we specify units of energy for the

incident beam, you can substitute units of power throughout the program. To get reliable

results, the radius should be much larger than the grid separation in the r direction of the

original mcml output, and much less than the total covered radius by the grid system in the

r direction of the original mcml output. As a rule of thumb, the radius should be in the

range between about 3 times the grid separation in the r direction and the total grid

coverage in the r direction minus the maximum radius of observation (see Eqs. 7.23 &

7.24 in Section 7.4).

Chapter 10 Instructions for conv 101

10.5 Command "r" of conv

This command is used to change the grid separation and the number of grid elements in

the r direction for the convolution. Since they take the values of the mcml output as the

default, you do not have to enter this command if you do not want to change them. The

maximum convolution radius should not be larger than that of the original mcml output to

get reliable results. For example:

> Main menu (h for help) => r Current resolution: 0.01 cm and number of points: 50 Input resolution in r direction [cm]: .02 Input number of points in r direction: 20 Resolution: 0.02 cm and number of points: 20

> Main menu (h for help) =>

Note that if the number of points is chosen too large, the program can exit due to

the lack of memory. This is a bug in the current version of conv.

10.6 Command "e" of conv

The integration is computed iteratively. The iteration stops when the difference

between the new estimate and the old estimate of the integration is a small part of the new

estimate. This small ratio can be controlled by users using command "e". It ranges

between 0 to 1. Small values would give better precision but longer computation time and

vice versa. Normally, 0.001 to 0.1 is recommended. The default value is 0.1. For

example:

> Main menu (h for help) => e Relative convolution error Current value is 0.05 (0.001-0.1 recommended): .01

Special attention has to be paid to this command. The convolution results may

have weird discontinuities if the allowed convolution error is too high (see Fig. 7.11), and

the convolution process may take too long if the convolution error is too low. The rule of

thumb is that you choose the lowest convolution error that does not make the convolution

too long to compute. If the convolution results still have any discontinuities which should

not be there, you need to decrease the convolution error and redo the convolution.

10.7 Command "oo" of conv

After you input the filename of the mcml output , you can output the original data

of the mcml output with various formats. One of the formats can be obtained by the

command "oo". For example:

102 Chapter 10 Instructions for conv

> Main menu (h for help) => oo

> Output mcml data (h for help) => h I = Input parameters of mcml 3 = reflectance, absorption, and transmittance AL = absorption vs layer [-] Az = absorption vs z [1/cm] Arz = absorption vs r & z [1/cm3] Fz = fluence vs z [-] Frz = fluence vs r & z [1/cm2] Rr = diffuse reflectance vs radius r [1/cm2] Ra = diffuse reflectance vs angle alpha [1/sr] Rra = diffuse reflectance vs radius and angle [1/(cm2 sr)] Tr = transmittance vs radius r [1/cm2] Ta = transmittance vs angle alpha [1/sr] Tra = transmittance vs radius and angle [1/(cm2 sr)] K = Keijzer's format Q = Quit to main menu * input filename: example.mco

> Output mcml data (h for help) =>

At this point, you can output various physical quantities by inputting the

subcommands, which can be listed by command "h" as shown above. After you type the

command, the program will ask you for the output filename. The exact physical meanings

of these physical quantities can be found in Chapter 4. The command "i" outputs the input

parameters of mcml to a file. The command "3" outputs three quantities to a file including

specular reflectance, total diffuse reflectance, absorption probability, and total

transmittance, which are actually four numbers. The command "Al" outputs the

absorption probability as a function layer to a file. The command "Az" outputs the

absorption as a function of z coordinate whose dimension is cm–1. The command "Arz"

outputs the absorption probability density as a function of r and z whose dimension is cm–

3. The commands "Fz" and "Frz" output the results of the commands "Az" and "Arz"

divided by the absorption coefficients. The command "Rr" outputs the diffuse reflectance

as a function of r whose unit is cm–2. The command "Ra" outputs the diffuse reflectance

as a function of the exit angle α, whose dimension is sr–1. The command "Rra" outputs the diffuse reflectance as a function of r and α, whose unit is cm–2 sr–1. Similarly, the commands "Tr", "Ta" and "Tra" are the corresponding commands for the transmittance.

The command "K" is used to convert the format of the mcml output to the format of

Marleen Keijzer's convolution program (in PASCAL on Macintoshes) which was used by

our group before the program conv was written. This command is only useful if you have

Marleen Keijzer's program. The command "q" will return the program to the main menu.

For 1D arrays, the outputs are in two columns. The first column gives the

independent variable, and the second column gives the physical quantities. For example,

Chapter 10 Instructions for conv 103

the output of the command "Rr" will have two columns. The first column gives the radius

in cm, and the second column gives the diffuse reflectance in cm–2.

For 2D arrays, the outputs are in three columns. The first two columns give the

first and the second independent variables, and the third column gives the physical

quantities. For example, the command "Arz" will give three columns. The first two

columns give r and z in cm respectively, and the third column gives the absorption

probability density in cm–2 sr–1 as a function of r and z.

An example is shown as follows:

> Output mcml data (h for help) => Rr Enter output filename with extension .Rr (or . to quit): example.Rr

> Output mcml data (h for help) =>

This command will output the diffuse reflectance as a function of r to the file named

"example.Rr".

10.8 Command "oc" of conv

After you input the filename of the mcml output and specify the incident photon

beam, you can output the convolved data with various formats. One of the formats is

writing data in columns, which can be obtained using the command "oc". For example:

> Main menu (h for help) => oc

> Output convolved data (h for help) => h Arz = absorption vs r & z [J/cm3] Frz = fluence vs r & z [J/cm2] Rr = diffuse reflectance vs radius r [J/cm2] Rra = diffuse reflectance vs radius and angle [J/(cm2 sr)] Tr = transmittance vs radius r [J/cm2] Tra = transmittance vs radius and angle [J/(cm2 sr)] Q = Quit to main menu * input filename: example.mco

> Output convolved data (h for help) =>

At this point, you can output various physical quantities by inputting the

subcommands, which can be listed by command "h" as shown above. After you type the

command, the program will ask you for the output filename. The exact physical meanings

of these physical quantities can be found in Chapters 4 and 7. The command "Arz"

outputs the absorption energy density as a function of r and z whose dimension is J cm–3.

The command "Frz" outputs the results of the command "Arz" divided by the absorption

coefficients, which is the fluence in J cm–2. Since we consider steady-state responses only

104 Chapter 10 Instructions for conv

in mcml and conv, you can systematically replace the energy [Joules] with power [Watts]

in conv.

The command "Rr" outputs the diffuse reflectance as a function of r whose unit is

J cm–2. The command "Rra" outputs the diffuse reflectance as a function of r and α, whose unit is J cm–2 sr–1. Similarly, the commands "Tr" and "Tra" are the corresponding

commands for the transmittance. The command "q" will return the program to the main

menu.

For 1D arrays, the outputs are in two columns. The first column gives the

independent variable, and the second column gives the physical quantities. For example,

the output of the command "Rr" will have two columns. The first column gives the radius

in cm, and the second column gives the diffuse reflectance in J cm–2.

For 2D arrays, the outputs are in three columns. The first two columns give the

first and the second independent variables respectively, and the third column gives the

physical quantities. For example, the command "Arz" will give three columns. The first

two columns give r and z in cm respectively, and the third column gives the absorption

energy density in J cm–2 sr–1 as a function of r and z.

An example is shown as follows:

> Output convolved data (h for help) => Rr Enter output filename with extension .Rrc (or . to quit): example.Rrc

> Output convolved data (h for help) =>

This command will output the diffuse reflectance as a function of r to the file named

"example.Rrc".

10.9 Command "co" of conv

After you input the filename of the mcml output, you can output the original data

of the mcml output with various formats. One of the formats for 2D arrays is writing data

in contour lines. Every contour line will be given by two columns. This format can be

obtained using the command "co" standing for "contours of the original data". Then, the

output file can be imported to some plotting software such as KaleidaGraph on

Macintoshes, and the contour lines can be drawn. For example:

> Main menu (h for help) => co

> Contour output of mcml data (h for help) => h

Chapter 10 Instructions for conv 105

A = absorption vs r & z [1/cm3] F = fluence vs r & z [1/cm2] R = diffuse reflectance vs radius and angle [1/(cm2 sr)] T = transmittance vs radius and angle [1/(cm2 sr)] Q = Quit to main menu * input filename: example.mco

> Contour output of mcml data (h for help) =>

Since only the 2D arrays need to be presented in contour lines, there are only four

physical quantities. The command "A" outputs the absorption probability density as a

function of r and z whose dimension is cm–3. The command "F" outputs the probability

fluence as a function of r and z in cm–2. The commands "R" and "T" output diffuse

reflectance and transmittance as a function of r and α in cm–2 sr–1.

After you input one of the commands, the program will prompt for the output

filename and the isovalues for the contour output. The value range of the physical

quantity is shown so that valid isovalues can be provided by users. You can enter as many

isovalues as you want. System memory is the only thing that limits the number of

isovalues. Stop entering isovalues by inputting a period ".". For example:

> Contour output of mcml data (h for help) => A Enter output filename with extension .iso (or . to quit): example.iso The range of the value is 0.156280 to 3294.800000. Input an isovalue or . to stop: 1000 Input an isovalue or . to stop: 100 Input an isovalue or . to stop: 10 Input an isovalue or . to stop: 1 Input an isovalue or . to stop: .

> Contour output of mcml data (h for help) =>

The output file of this example will have eight columns, each pair of columns describe one

contour line. The values of the contour lines are 1000, 100, 10, and 1 respectively.

10.10 Command "cc" of conv

After you input the filename of the mcml output and specify the incident photon

beam, you can output the convolved data with various formats. One of the formats for 2D

arrays is writing data in contour lines. Every contour line will be given by two columns.

This format can be obtained using the command "cc". The output file can be imported to

some plotting software such as KaleidaGraph, and the contour lines can be drawn. For

example:

> Main menu (h for help) => cc

> Contour output of convolved data (h for help) => h A = absorption vs r & z [J/cm3] F = fluence vs r & z [J/cm2]

106 Chapter 10 Instructions for conv

R = diffuse reflectance vs radius and angle [J/(cm2 sr)] T = transmittance vs radius and angle [J/(cm2 sr)] Q = Quit to main menu * input filename: example.mco

> Contour output of convolved data (h for help) =>

Since only the 2D arrays need to be presented in contour lines, there are only four

physical quantities. The command "A" outputs the absorption energy density as a function

of r and z whose dimension is J cm–3. The command "F" outputs the fluence as a function

of r and z in J cm–2. The commands "R" and "T" output diffuse reflectance and

transmittance as a function of r and α in J cm−2 sr–1 respectively.

After you input one of the commands, the program will prompt for the output

filename and the isovalues for the contour output. The value range of the physical

quantity is shown so that valid isovalues can be provided by users. You can enter as many

isovalues as you want. System memory is the only thing that limits the number of

isovalues. Stop entering isovalues by inputting a period ".". For example:

> Contour output of convolved data (h for help) => A Enter output filename with extension .iso (or . to quit): exampleAc.iso The range of the value is 0.048200 to 95.624939. Input an isovalue or . to stop: 80 Input an isovalue or . to stop: 8 Input an isovalue or . to stop: 0.8 Input an isovalue or . to stop: .

> Contour output of convolved data (h for help) =>

The output file of this example will have six columns, each pair of columns describe one

contour line. The values of the contour lines are 80, 8, and 0.8 respectively.

10.11 Command "so" of conv

After you input the filename of the mcml output, you can output the original data

of the mcml output with various formats. One of the formats for 2D arrays is writing data

in two columns, where the two columns give the physical quantity as a function of one of

two independent variables. The other variable is fixed at a certain value which can be

chosen by users. This format, we call scanning output, can be obtained using the

command "so". The output file can be imported to some plotting software such as

KaleidaGraph. For example:

> Main menu (h for help) => so

> Scans of mcml data (h for help) => h Ar = absorption vs r @ fixed z [1/cm3] Az = absorption vs z @ fixed r [1/cm3] Fr = fluence vs r @ fixed z [1/cm2]

Chapter 10 Instructions for conv 107

Fz = fluence vs z @ fixed r [1/cm2] Rr = diffuse reflectance vs r @ fixed angle [1/(cm2 sr)] Ra = diffuse reflectance vs angle @ fixed r [1/(cm2 sr)] Tr = transmittance vs r @ fixed angle [1/(cm2 sr)] Ta = transmittance vs angle @ fixed r [1/(cm2 sr)] Q = quit * input filename: example.mco

> Scans of mcml data (h for help) =>

The command "Ar" outputs the absorption probability density as a function of r for

a fixed z, whose dimension is cm–3. The command "Az" outputs the absorption

probability density as a function of z for a fixed r, whose dimension is cm–3. The

command "Fr" outputs the probability fluence as a function of r for a fixed z in cm–2. The

command "Fz" outputs the probability fluence as a function of z for a fixed r in cm–2. The

command "Rr" outputs the diffuse reflectance as a function of r for a fixed α in cm–2sr–1. The command "Ra" outputs the diffuse reflectance as a function of α for a fixed r in cm– 2sr–1. The commands "Tr" and "Ta" output the transmittance in the same format as for

the diffuse reflectance. The command "q" returns to the main menu.

After you input one of the commands, the program will prompt for the output

filename and the grid index to the value of the fixed variable. If you want to abort this

output, you can input a period "." as the filename. For example:

> Scans of mcml data (h for help) => Ar Enter output filename with extension .Ars (or . to quit): example.Ars z grid separation is 0.01 cm. Input fixed z index (0 - 39): 0

> Scans of mcml data (h for help) =>

This command outputs the absorption as a function of r for a fixed z. The

program shows that the z grid separation is 0.01 cm. The number of grid elements in the z

direction is 40. The grid index in the z direction is in the range from 0 to 39. The

command will generate two columns. The first column is r, and the second is the

absorption.

10.12 Command "sc" of conv

After you input the filename of the mcml output and specify the incident photon

beam, you can output the convolved data with various formats. One of the formats for 2D

arrays is writing data in two columns, where the two columns give the physical quantity as

a function of one of two independent variables. The other variable is fixed at a certain

value which can be chosen by users. This format, we call scanning output, can be

108 Chapter 10 Instructions for conv

obtained using the command "sc". Then, the output file can be imported to some plotting

software such as KaleidaGraph. For example:

> Main menu (h for help) => sc

> Scans of convolved data (h for help) => h Ar = absorption vs r @ fixed z [J/cm3] Az = absorption vs z @ fixed r [J/cm3] Fr = fluence vs r @ fixed z [J/cm2] Fz = fluence vs z @ fixed r [J/cm2] Rr = diffuse reflectance vs r @ fixed angle [J/(cm2 sr)] Ra = diffuse reflectance vs angle @ fixed r [J/(cm2 sr)] Tr = transmittance vs r @ fixed angle [J/(cm2 sr)] Ta = transmittance vs angle @ fixed r [J/(cm2 sr)] Q = quit * input filename: example.mco

> Scans of convolved data (h for help) =>

The command "Ar" outputs the absorption energy density as a function of r for a

fixed z, whose dimension is J cm–3. The command "Az" outputs the absorption energy

density as a function of z for a fixed r, whose dimension is J cm−3. The command "Fr"

outputs the fluence as a function of r for a fixed z in J cm–2. The command "Fz" outputs

the fluence as a function of z for a fixed r in J cm–2. The command "Rr" outputs the

diffuse reflectance as a function of r for a fixed α in J cm−2 sr−1. The command "Ra" outputs the diffuse reflectance as a function of α for a fixed r in J cm–2 sr–1. The commands "Tr" and "Ta" output the transmittance in the same format as for the diffuse

reflectance. The command "q" returns to the main menu.

After you input one of the commands, the program will prompt for the output

filename and the grid index to the value of the fixed variable. If you want to abort this

output, you can input a period "." as the filename. For example:

> Scans of convolved data (h for help) => Ar Enter output filename with extension .Arsc (or . to quit): example.Arsc z grid separation is 0.01 cm. Input fixed z index (0 - 39): 0

> Scans of convolved data (h for help) =>

This command outputs the absorption as a function of r for a fixed z. The

program shows that the z grid separation is 0.01 cm. The number of grids in the z

direction is 40. The grid index in the z direction is in the range from 0 to 39. The

command will generate two columns. The first column is r, and the second is the

absorption.

Chapter 10 Instructions for conv 109

10.13 Command "q" of conv

If you want to quit the program conv, use the command "q" in the main menu.

The program will ask you if you really mean to quit. You can answer yes or no. The

program will quit if the answer is "y". Otherwise, the program will return to the main

menu. For example:

> Main menu (h for help) => q Do you really want to quit conv (y/n): n

> Main menu (h for help) => q Do you really want to quit conv (y/n): y

10.14 Bugs of conv

The convolution results may have weird discontinuities if the allowed convolution

error is too high, and the convolution process may take too long if the convolution error is

too low. We do not have a good way to predict the best convolution error yet. The rule

of thumb is that you choose the lowest convolution error that does not make the

convolution too long to compute. If the convolution results still have any discontinuities

which should not be there, you need to decrease the convolution error and redo the

convolution.

As we discussed in Section 7.5, the radius of the incident beam has to be in the

right range to get reliable convolution integration due to the spatial resolution and the

range of grid system. As a rule of thumb, the radius should be in the range between about

3 times the grid separation in the r direction and the total grid coverage in the r direction

minus the maximum radius of observation (see Eqs. 7.23 & 7.24 in Section 7.4).

If the number of points in the r direction is chosen too large in the command "r",

the program can exit due to the lack of memory.

110 Chapter 11 How to Modify mcml

11. How to Modify mcml

The current version of program mcml simulates responses of an infinitely narrow

photon beam normally incident on multi-layered turbid media. We intend to make mcml

more general in the future. However, if you need to solve a different problem, such as

responses of isotropic photon sources instead of infinitely narrow photon beams,

responses of buried beams instead of externally incident beams, or time-resolved

simulations, then you will need to modify the program slightly. To do so, you need to

have prior knowledge of C language and understand Chapter 5. Appendix A and

Appendix B are provided to aid you in modifying the program. Appendix A gives you an

overall flow of the program, and Appendix B provides line-numbered source codes, which

can be used in combination with Appendix A for quick reference of the detail of the

program.

As an example, let us modify several places of the program mcml to compute the

responses of buried isotropic photon sources. First, we need to allow users to provide the

depth of the isotropic photon source inside the tissue, which can be entered in the input

data file. For example, we can add the depth of the source as the second parameter in the

line for the number of photon packets. Second, we define one more member called

source_z in the structure InputStruct to store the depth of the source. Third, we read the

depth into the member source_z of structure InputStruct in function ReadParm() which is

in the file "mcmlio.c". Fourth, we need to modify the function LaunchPhoton() in the file

"mcmlgo.c" such that the photons are initialized isotropically at the correct depth

according to source_z. If you know the program well, you will know that you can make

the source isotropic utilizing the function Spin() in the file "mcmlgo.c". To do so, we can

initialize the photon packet to be unidirectional (e.g., +z direction) temporarily, then

pretend that the whole photon packet suffers an isotropic scattering on the same spot as

initialized using the function Spin(). All you need to do is to provide an anisotropy factor

g equal 0 as the real parameter for the function. The propagation simulation functions

need no change, and the scoring procedures need no change either because the problem is

still cylindrically symmetric. After you modify the source code, you need to recompile and

link (refer to the manual with the compiler and linker).

After we modified mcml like this, we simulated the diffuse reflectances of a buried

isotropic photon source in two media separately, whose optical properties are equivalent

according to the similarity relations (Wyman et al., 1989a and 1989b). The optical

Chapter 11 How to Modify mcml 111

properties for the isotropic scattering medium (g = 0) are: absorption coefficient µ a = 0.1

cm−1, scattering coefficient µ s = 10 cm−1, anisotropy factor g = 0, relative refractive index

nrel = 1. The optical properties for the anisotropic scattering medium (g � 0) are: µ a =

0.1 cm−1, µ s = 100 cm−1, g = 0.9, nrel = 1. The depth of the isotropic photon source is 1

transport mean free path (mfp'), which is computed by 1 mfp' = 1/(µa + µ s(1–g)) = 1/(0.1

+ 10) ≈ 0.1 cm. The grid separations in the z and r directions are both 0.005 cm, and the numbers of grid elements in the z and r directions are both 200. One million photon

packets were used in the modified mcml.

0.01

0.1

1

10

0

R d ( cm -2 )

0.2 0.4 0.6 0.8 1 r (cm)

C: Isotropic Source, g=0

D: Isotropic Source, g=0.9

Fig. 11.1. Comparison of diffuse reflectances as a function of radius r for

two semi-infinite media whose optical properties are governed by the

similarity relations.The diffuse reflectances and the fluences for the two

media are compared in Figs. 11.1 and 11.2 respectively. The results of the diffuse

reflectances show that the similarity relations work well for photon sources deep inside the

tissue, and the results of the fluences show that the similarity relations work well when the

observation point is far away from the source. The fluence near the source for the

isotropic scattering medium is larger than that for the anisotropic scattering medium.

Similar to the discussion in Section 4.2, the fluences presented here are the responses of

an isotropic infinitely wide plane source with a difference of a constant factor which is the

power density of the source.

112 Chapter 11 How to Modify mcml

0

1

2

3

4

5

6

F lu

e n ce

[ -]

0 0.2 0.4 0.6 0.8 1 z [cm]

g = 0 g = 0.9

Responses to isotropic infinitely wide plane source

Depth of source

Fig. 11.2. Comparison between fluences as a function of z for two semi-

infinite media whose optical properties are governed by the similarity

relations (see Section 4.2 for discussion of an infinitely wide incident

beam).

Appendix A Cflow Output of the Program mcml 113

Appendices

Appendix A. Cflow Output of the Program mcml

We have listed the short format of the UNIX command cflow output in Section

5.5. To show all depth of the nesting levels, we list here the results of the UNIX

command:

cflow mcmlgo.c

1 main: char(), <mcmlmain.c 198> 2 ShowVersion: void*(), <mcmlio.c 49> 3 CenterStr: char*(), <mcmlio.c 28> 4 strlen: <> 5 strcpy: <> 6 strcat: <> 7 puts: <> 8 GetFnameFromArgv: void*(), <mcmlmain.c 150> 9 strcpy: 5 10 GetFile: struct*(), <mcmlio.c 94> 11 printf: <> 12 scanf: <> 13 strlen: 4 14 exit: <> 15 fopen: <> 16 CheckParm: void*(), <mcmlio.c 514> 17 ReadNumRuns: short(), <mcmlio.c 205> 18 FindDataLine: char*(), <mcmlio.c 184> 19 fgets: <> 20 printf: 11 21 CheckChar: char(), <mcmlio.c 139> 22 strlen: 4 23 nrerror: void*(), <mcmlnr.c 19> 24 fprintf: <> 25 exit: 14 26 KillChar: void*(), <mcmlio.c 123> 27 CommentLine: char(), <mcmlio.c 165> 28 strspn: <> 29 strcspn: <> 30 strcpy: 5 31 nrerror: 23 32 sscanf: <> 33 printf: 11 34 ReadParm: void*(), <mcmlio.c 425> 35 ReadFnameFormat: void*(), <mcmlio.c 225> 36 FindDataLine: 18 37 strcpy: 5 38 nrerror: 23 39 sscanf: 32 40 toupper: <> 41 ReadNumPhotons: void*(), <mcmlio.c 243> 42 FindDataLine: 18 43 strcpy: 5 44 nrerror: 23 45 sscanf: 32 46 ReadDzDr: void*(), <mcmlio.c 260> 47 FindDataLine: 18 48 strcpy: 5 49 nrerror: 23 50 sscanf: 32

114 Appendix A Cflow Output of the Program mcml

51 ReadNzNrNa: void*(), <mcmlio.c 276> 52 FindDataLine: 18 53 strcpy: 5 54 nrerror: 23 55 sscanf: 32 56 ReadNumLayers: void*(), <mcmlio.c 299> 57 FindDataLine: 18 58 strcpy: 5 59 nrerror: 23 60 sscanf: 32 61 ReadLayerSpecs: void*(), <mcmlio.c 375> 62 malloc: <> 63 nrerror: 23 64 ReadAmbient: void*(), <mcmlio.c 318> 65 FindDataLine: 18 66 strcpy: 5 67 sprintf: <> 68 nrerror: 23 69 sscanf: 32 70 ReadOneLayer: char(), <mcmlio.c 347> 71 FindDataLine: 18 72 strcpy: 5 73 sscanf: 32 74 sprintf: 67 75 CriticalAngle: void*(), <mcmlio.c 405> 76 sqrt: <> 77 FnameTaken: char(), <mcmlio.c 487> 78 NameInList: char(), <mcmlio.c 446> 79 strcmp: <> 80 AddNameToList: void*(), <mcmlio.c 459> 81 malloc: 62 82 strcpy: 5 83 sprintf: 67 84 free: <> 85 nrerror: 23 86 FreeFnameList: void*(), <mcmlio.c 500> 87 free: 84 88 rewind: <> 89 ReadNumRuns: 17 90 ReadParm: 34 91 DoOneRun: void*(), <mcmlmain.c 163> 92 InitOutputData: void*(), <mcmlio.c 546> 93 nrerror: 23 94 AllocMatrix: double**(), <mcmlnr.c 52> 95 malloc: 62 96 nrerror: 23 97 AllocVector: double*(), <mcmlnr.c 33> 98 malloc: 62 99 nrerror: 23 100 Rspecular: double(), <mcmlgo.c 117> 101 PunchTime: long(), <mcmlmain.c 60> 102 clock: <> 103 time: <> 104 sprintf: 67 105 puts: 7 106 strcpy: 5 107 difftime: <> 108 ReportStatus: void*(), <mcmlmain.c 122> 109 printf: 11 110 PredictDoneTime: void*(), <mcmlmain.c 95> 111 time: 103 112 localtime: <> 113 strftime: <> 114 printf: 11 115 PunchTime: 101 116 LaunchPhoton: void*(), <mcmlgo.c 143> 117 Rspecular: 100

Appendix A Cflow Output of the Program mcml 115

118 HopDropSpin: void*(), <mcmlgo.c 726> 119 HopInGlass: void*(), <mcmlgo.c 675> 120 StepSizeInGlass: void*(), <mcmlgo.c 265> 121 Hop: void*(), <mcmlgo.c 246> 122 CrossOrNot: void*(), <mcmlgo.c 660> 123 CrossUpOrNot: void*(), <mcmlgo.c 547> 124 RFresnel: double(), <mcmlgo.c 421> 125 sqrt: 76 126 RandomNum: double(), <mcmlgo.c 83> 127 time: 103 128 ran3: float(), <mcmlgo.c 32> 129 RecordR: void*(), <mcmlgo.c 480> 130 sqrt: 76 131 acos: <> 132 CrossDnOrNot: void*(), <mcmlgo.c 609> 133 RFresnel: 124 134 RandomNum: 126 135 RecordT: void*(), <mcmlgo.c 508> 136 sqrt: 76 137 acos: 131 138 HopDropSpinInTissue: void*(), <mcmlgo.c 706> 139 StepSizeInTissue: void*(), <mcmlgo.c 295> 140 RandomNum: 126 141 log: <> 142 HitBoundary: char(), <mcmlgo.c 323> 143 Hop: 121 144 CrossOrNot: 122 145 Drop: void*(), <mcmlgo.c 365> 146 sqrt: 76 147 Spin: void*(), <mcmlgo.c 204> 148 SpinTheta: double(), <mcmlgo.c 176> 149 RandomNum: 126 150 sqrt: 76 151 RandomNum: 126 152 cos: <> 153 fabs: <> 154 Roulette: void*(), <mcmlgo.c 395> 155 RandomNum: 126 156 ReportResult: void*(), <mcmlmain.c 133> 157 strcpy: 5 158 PunchTime: 101 159 SumScaleResult: void*(), <mcmlio.c 802> 160 Sum2DRd: void*(), <mcmlio.c 607> 161 Sum2DA: void*(), <mcmlio.c 652> 162 IzToLayer: short(), <mcmlio.c 637> 163 Sum2DTt: void*(), <mcmlio.c 677> 164 ScaleRdTt: void*(), <mcmlio.c 720> 165 sin: <> 166 ScaleA: void*(), <mcmlio.c 766> 167 WriteResult: void*(), <mcmlio.c 1091> 168 fopen: 15 169 nrerror: 23 170 toupper: 40 171 WriteVersion: void*(), <mcmlio.c 819> 172 fprintf: 24 173 fprintf: 24 174 WriteInParm: void*(), <mcmlio.c 833> 175 fprintf: 24 176 WriteRAT: void*(), <mcmlio.c 872> 177 fprintf: 24 178 WriteA_layer: void*(), <mcmlio.c 895> 179 fprintf: 24 180 WriteA_z: void*(), <mcmlio.c 1035> 181 fprintf: 24 182 WriteRd_r: void*(), <mcmlio.c 941> 183 fprintf: 24 184 WriteRd_a: void*(), <mcmlio.c 960>

116 Appendix A Cflow Output of the Program mcml

185 fprintf: 24 186 WriteTt_r: void*(), <mcmlio.c 1054> 187 fprintf: 24 188 WriteTt_a: void*(), <mcmlio.c 1073> 189 fprintf: 24 190 WriteA_rz: void*(), <mcmlio.c 1008> 191 fprintf: 24 192 WriteRd_ra: void*(), <mcmlio.c 914> 193 fprintf: 24 194 WriteTt_ra: void*(), <mcmlio.c 980> 195 fprintf: 24 196 fclose: <> 197 FreeData: void*(), <mcmlio.c 581> 198 free: 84 199 FreeMatrix: void*(), <mcmlnr.c 86> 200 free: 84 201 FreeVector: void*(), <mcmlnr.c 77> 202 free: 84 203 fclose: 196

Section B.1 mcml.h 117

Appendix B. Source Code of the Program mcml

The whole program is divided into several files. The file "mcml.h" is the header

file, which defines data structures and some constants. The file "mcmlmain.c" contains the

function main(). It also deals with the timings and status report. The file "mcmlio.c"

reads or writes data from or to data files. The file "mcmlgo.c" does most of the Monte

Carlo simulations. The file "mcmlnr.c" (nr stands for numerical recipes) contains several

functions for dynamical data allocations and error report.

B.1 mcml.h

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * Monte Carlo simulation of photon distribution in 6 * multi-layered turbid media in ANSI Standard C. 7 **** 8 * Starting Date: 10/1991. 9 * Current Date: 6/1992. 10 * 11 * Lihong Wang, Ph. D. 12 * Steven L. Jacques, Ph. D. 13 * Laser Biology Research Laboratory - 17 14 * M.D. Anderson Cancer Center 15 * University of Texas 16 * 1515 Holcombe Blvd. 17 * Houston, TX 77030 18 * USA 19 * 20 * This program was based on: 21 * (1) The Pascal code written by Marleen Keijzer and 22 * Steven L. Jacques in this laboratory in 1989, which 23 * deals with multi-layered turbid media. 24 * 25 * (2) Algorithm for semi-infinite turbid medium by 26 * S.A. Prahl, M. Keijzer, S.L. Jacques, A.J. Welch, 27 * SPIE Institute Series Vol. IS 5 (1989), and by 28 * A.N. Witt, The Astrophysical journal Supplement 29 * Series 35, 1-6 (1977). 30 * 31 * Major modifications include: 32 * . Conform to ANSI Standard C. 33 * . Removal of limit on number of array elements, 34 * because arrays in this program are dynamically 35 * allocated. This means that the program can accept 36 * any number of layers or gridlines as long as the 37 * memory permits. 38 * . Avoiding global variables whenever possible. This 39 * program has not used global variables so far. 40 * . Grouping variables logically using structures. 41 * . Top-down design, keep each subroutine clear & 42 * short. 43 * . Reflectance and transmittance are angularly 44 * resolved. 45 **** 46 * General Naming Conventions:

118 Appendix B Source Code of the Program mcml

47 * Preprocessor names: all capital letters, 48 * e.g. #define PREPROCESSORS 49 * Globals: first letter of each word is capital, no 50 * underscores, 51 * e.g. short GlobalVar; 52 * Dummy variables: first letter of each word is capital, 53 * and words are connected by underscores, 54 * e.g. void NiceFunction(char Dummy_Var); 55 * Local variables: all lower cases, words are connected 56 * by underscores, 57 * e.g. short local_var; 58 * Function names or data types: same as Globals. 59 * 60 **** 61 * Dimension of length: cm. 62 * 63 ****/ 64 65 #include <math.h> 66 #include <stdlib.h> 67 #include <stdio.h> 68 #include <stddef.h> 69 #include <time.h> 70 #include <string.h> 71 #include <ctype.h> 72 73 #define PI 3.1415926 74 #define WEIGHT 1E-4 /* Critical weight for roulette. */ 75 #define CHANCE 0.1 /* Chance of roulette survival. */ 76 #define STRLEN 256 /* String length. */ 77 78 #define Boolean char 79 80 #define SIGN(x) ((x)>=0 ? 1:-1) 81 82 /****************** Stuctures *****************************/ 83 84 /**** 85 * Structure used to describe a photon packet. 86 ****/ 87 typedef struct { 88 double x, y ,z; /* Cartesian coordinates.[cm] */ 89 double ux, uy, uz;/* directional cosines of a photon. */ 90 double w; /* weight. */ 91 Boolean dead; /* 1 if photon is terminated. */ 92 short layer; /* index to layer where the photon */ 93 /* packet resides. */ 94 double s; /* current step size. [cm]. */ 95 double sleft; /* step size left. dimensionless [-]. */ 96 } PhotonStruct; 97 98 /**** 99 * Structure used to describe the geometry and optical 100 * properties of a layer. 101 * z0 and z1 are the z coordinates for the upper boundary 102 * and lower boundary respectively. 103 * 104 * cos_crit0 and cos_crit1 are the cosines of the 105 * critical angle of total internal reflection for the 106 * upper boundary and lower boundary respectively. 107 * They are set to zero if no total internal reflection 108 * exists. 109 * They are used for computation speed. 110 ****/ 111 typedef struct { 112 double z0, z1; /* z coordinates of a layer. [cm] */ 113 double n; /* refractive index of a layer. */

Section B.1 mcml.h 119

114 double mua; /* absorption coefficient. [1/cm] */ 115 double mus; /* scattering coefficient. [1/cm] */ 116 double g; /* anisotropy. */ 117 118 double cos_crit0, cos_crit1; 119 } LayerStruct; 120 121 /**** 122 * Input parameters for each independent run. 123 * 124 * z and r are for the cylindrical coordinate system. [cm] 125 * a is for the angle alpha between the photon exiting 126 * direction and the surface normal. [radian] 127 * 128 * The grid line separations in z, r, and alpha 129 * directions are dz, dr, and da respectively. The numbers 130 * of grid lines in z, r, and alpha directions are 131 * nz, nr, and na respectively. 132 * 133 * The member layerspecs will point to an array of 134 * structures which store parameters of each layer. 135 * This array has (number_layers + 2) elements. One 136 * element is for a layer. 137 * The layers 0 and (num_layers + 1) are for top ambient 138 * medium and the bottom ambient medium respectively. 139 ****/ 140 typedef struct { 141 char out_fname[STRLEN]; /* output file name. */ 142 char out_fformat; /* output file format. */ 143 /* 'A' for ASCII, */ 144 /* 'B' for binary. */ 145 long num_photons; /* to be traced. */ 146 double Wth; /* play roulette if photon */ 147 /* weight < Wth.*/ 148 149 double dz; /* z grid separation.[cm] */ 150 double dr; /* r grid separation.[cm] */ 151 double da; /* alpha grid separation. */ 152 /* [radian] */ 153 short nz; /* array range 0..nz-1. */ 154 short nr; /* array range 0..nr-1. */ 155 short na; /* array range 0..na-1. */ 156 157 short num_layers; /* number of layers. */ 158 LayerStruct * layerspecs; /* layer parameters. */ 159 } InputStruct; 160 161 /**** 162 * Structures for scoring physical quantities. 163 * z and r represent z and r coordinates of the 164 * cylindrical coordinate system. [cm] 165 * a is the angle alpha between the photon exiting 166 * direction and the normal to the surfaces. [radian] 167 * See comments of the InputStruct. 168 * See manual for the physcial quantities. 169 ****/ 170 typedef struct { 171 double Rsp; /* specular reflectance. [-] */ 172 double ** Rd_ra; /* 2D distribution of diffuse */ 173 /* reflectance. [1/(cm2 sr)] */ 174 double * Rd_r; /* 1D radial distribution of diffuse */ 175 /* reflectance. [1/cm2] */ 176 double * Rd_a; /* 1D angular distribution of diffuse */ 177 /* reflectance. [1/sr] */ 178 double Rd; /* total diffuse reflectance. [-] */ 179 180 double ** A_rz; /* 2D probability density in turbid */

120 Appendix B Source Code of the Program mcml

181 /* media over r & z. [1/cm3] */ 182 double * A_z; /* 1D probability density over z. */ 183 /* [1/cm] */ 184 double * A_l; /* each layer's absorption */ 185 /* probability. [-] */ 186 double A; /* total absorption probability. [-] */ 187 188 double ** Tt_ra; /* 2D distribution of total */ 189 /* transmittance. [1/(cm2 sr)] */ 190 double * Tt_r; /* 1D radial distribution of */ 191 /* transmittance. [1/cm2] */ 192 double * Tt_a; /* 1D angular distribution of */ 193 /* transmittance. [1/sr] */ 194 double Tt; /* total transmittance. [-] */ 195 } OutStruct; 196 197 /*********************************************************** 198 * Routine prototypes for dynamic memory allocation and 199 * release of arrays and matrices. 200 * Modified from Numerical Recipes in C. 201 ****/ 202 double *AllocVector(short, short); 203 double **AllocMatrix(short, short,short, short); 204 void FreeVector(double *, short, short); 205 void FreeMatrix(double **, short, short, short, short); 206 void nrerror(char *);

Section B.2 mcmlmain.c 121

B.2 mcmlmain.c

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * main program for Monte Carlo simulation of photon 6 * distribution in multi-layered turbid media. 7 * 8 ****/ 9 10 /**** 11 * THINKCPROFILER is defined to generate profiler calls in 12 * Think C. If 1, remember to turn on "Generate profiler 13 * calls" in the options menu. 14 ****/ 15 #define THINKCPROFILER 0 16 17 /* GNU cc does not support difftime() and CLOCKS_PER_SEC.*/ 18 #define GNUCC 0 19 20 #if THINKCPROFILER 21 #include <profile.h> 22 #include <console.h> 23 #endif 24 25 #include "mcml.h" 26 27 /* Declare before they are used in main(). */ 28 FILE *GetFile(char *); 29 short ReadNumRuns(FILE* ); 30 void ReadParm(FILE* , InputStruct * ); 31 void CheckParm(FILE* , InputStruct * ); 32 void InitOutputData(InputStruct, OutStruct *); 33 void FreeData(InputStruct, OutStruct *); 34 double Rspecular(LayerStruct * ); 35 void LaunchPhoton(double, LayerStruct *, PhotonStruct *); 36 void HopDropSpin(InputStruct *,PhotonStruct *,OutStruct *); 37 void SumScaleResult(InputStruct, OutStruct *); 38 void WriteResult(InputStruct, OutStruct, char *); 39 40 41 /*********************************************************** 42 * If F = 0, reset the clock and return 0. 43 * 44 * If F = 1, pass the user time to Msg and print Msg on 45 * screen, return the real time since F=0. 46 * 47 * If F = 2, same as F=1 except no printing. 48 * 49 * Note that clock() and time() return user time and real 50 * time respectively. 51 * User time is whatever the system allocates to the 52 * running of the program; 53 * real time is wall-clock time. In a time-shared system, 54 * they need not be the same. 55 * 56 * clock() only hold 16 bit integer, which is about 32768 57 * clock ticks. 58 ****/ 59 time_t PunchTime(char F, char *Msg) 60 { 61 #if GNUCC 62 return(0);

122 Appendix B Source Code of the Program mcml

63 #else 64 static clock_t ut0; /* user time reference. */ 65 static time_t rt0; /* real time reference. */ 66 double secs; 67 char s[STRLEN]; 68 69 if(F==0) { 70 ut0 = clock(); 71 rt0 = time(NULL); 72 return(0); 73 } 74 else if(F==1) { 75 secs = (clock() - ut0)/(double)CLOCKS_PER_SEC; 76 if (secs<0) secs=0; /* clock() can overflow. */ 77 sprintf(s, "User time: %8.0lf sec = %8.2lf hr. %s\n", 78 secs, secs/3600.0, Msg); 79 puts(s); 80 strcpy(Msg, s); 81 return(difftime(time(NULL), rt0)); 82 } 83 else if(F==2) return(difftime(time(NULL), rt0)); 84 else return(0); 85 #endif 86 } 87 88 /*********************************************************** 89 * Print the current time and the estimated finishing time. 90 * 91 * P1 is the number of computed photon packets. 92 * Pt is the total number of photon packets. 93 ****/ 94 void PredictDoneTime(long P1, long Pt) 95 { 96 time_t now, done_time; 97 struct tm *date; 98 char s[80]; 99 100 now = time(NULL); 101 date = localtime(&now); 102 strftime(s, 80, "%H:%M %x", date); 103 printf("Now %s, ", s); 104 105 done_time = now + 106 (time_t) (PunchTime(2,"")*(Pt-P1)/(double)P1); 107 date = localtime(&done_time); 108 strftime(s, 80, "%H:%M %x", date); 109 printf("End %s\n", s); 110 } 111 112 /*********************************************************** 113 * Report estimated time, number of photons and runs left 114 * after calculating 10 photons or every 1/10 of total 115 * number of photons. 116 * 117 * Num_Runs is the number of runs left. 118 * Pi is the index to the current photon, counting down. 119 * Pt is the total number of photons. 120 ****/ 121 void ReportStatus(short Num_Runs,long Pi,long Pt) 122 { 123 if(Pt-Pi == 10 || Pi*10%Pt == 0 && Pi != Pt) { 124 printf("%ld photons & %hd runs left, ", Pi, Num_Runs); 125 PredictDoneTime(Pt-Pi, Pt); 126 } 127 } 128 129 /***********************************************************

Section B.2 mcmlmain.c 123

130 * Report time and write results. 131 ****/ 132 void ReportResult(InputStruct In_Parm, OutStruct Out_Parm) 133 { 134 char time_report[STRLEN]; 135 136 strcpy(time_report, " Simulation time of this run."); 137 PunchTime(1, time_report); 138 139 SumScaleResult(In_Parm, &Out_Parm); 140 WriteResult(In_Parm, Out_Parm, time_report); 141 } 142 143 /*********************************************************** 144 * Get the file name of the input data file from the 145 * argument to the command line. 146 ****/ 147 void GetFnameFromArgv(int argc, 148 char * argv[], 149 char * input_filename) 150 { 151 if(argc>=2) { /* filename in command line */ 152 strcpy(input_filename, argv[1]); 153 } 154 else 155 input_filename[0] = '\0'; 156 } 157 158 159 /*********************************************************** 160 * Execute Monte Carlo simulation for one independent run. 161 ****/ 162 void DoOneRun(short NumRuns, InputStruct *In_Ptr) 163 { 164 register long i_photon; 165 /* index to photon. register for speed.*/ 166 OutStruct out_parm; /* distribution of photons.*/ 167 PhotonStruct photon; 168 169 #if THINKCPROFILER 170 InitProfile(200,200); cecho2file("prof.rpt",0, stdout); 171 #endif 172 173 InitOutputData(*In_Ptr, &out_parm); 174 out_parm.Rsp = Rspecular(In_Ptr->layerspecs); 175 i_photon = In_Ptr->num_photons; 176 PunchTime(0, ""); 177 178 do { 179 ReportStatus(NumRuns, i_photon, In_Ptr->num_photons); 180 LaunchPhoton(out_parm.Rsp, In_Ptr->layerspecs, &photon); 181 do HopDropSpin(In_Ptr, &photon, &out_parm); 182 while (!photon.dead); 183 } while(--i_photon); 184 185 #if THINKCPROFILER 186 exit(0); 187 #endif 188 189 ReportResult(*In_Ptr, out_parm); 190 FreeData(*In_Ptr, &out_parm); 191 } 192 193 /*********************************************************** 194 * The argument to the command line is filename, if any. 195 * Macintosh does not support command line. 196 ****/

124 Appendix B Source Code of the Program mcml

197 char main(int argc, char *argv[]) 198 { 199 char input_filename[STRLEN]; 200 FILE *input_file_ptr; 201 short num_runs; /* number of independent runs. */ 202 InputStruct in_parm; 203 204 ShowVersion("Version 1.1, 1992"); 205 GetFnameFromArgv(argc, argv, input_filename); 206 input_file_ptr = GetFile(input_filename); 207 CheckParm(input_file_ptr, &in_parm); 208 num_runs = ReadNumRuns(input_file_ptr); 209 210 while(num_runs--) { 211 ReadParm(input_file_ptr, &in_parm); 212 DoOneRun(num_runs, &in_parm); 213 } 214 215 fclose(input_file_ptr); 216 return(0); 217 }

Section B.3 mcmlio.c 125

B.3 mcmlio.c

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * Input/output of data. 6 ****/ 7 8 #include "mcml.h" 9 10 /*********************************************************** 11 * Structure used to check against duplicated file names. 12 ****/ 13 struct NameList { 14 char name[STRLEN]; 15 struct NameList * next; 16 }; 17 18 typedef struct NameList NameNode; 19 typedef NameNode * NameLink; 20 21 22 /*********************************************************** 23 * Center a string according to the column width. 24 ****/ 25 char * CenterStr(short Wid, 26 char * InStr, 27 char * OutStr) 28 { 29 size_t nspaces; /* number of spaces to be filled */ 30 /* before InStr. */ 31 32 nspaces = (Wid - strlen(InStr))/2; 33 if(nspaces<0) nspaces = 0; 34 35 strcpy(OutStr, ""); 36 while(nspaces--) strcat(OutStr, " "); 37 38 strcat(OutStr, InStr); 39 40 return(OutStr); 41 } 42 43 /*********************************************************** 44 * Print some messages before starting simulation. 45 * e.g. author, address, program version, year. 46 ****/ 47 #define COLWIDTH 80 48 void ShowVersion(char *version) 49 { 50 char str[STRLEN]; 51 52 CenterStr(COLWIDTH, 53 "mcml - Monte Carlo Simulation of Multi-layered Turbid Media", 54 str); 55 puts(str); 56 puts(""); 57 58 CenterStr(COLWIDTH, "Lihong Wang, Ph. D.", str); 59 puts(str); 60 61 CenterStr(COLWIDTH, "Steven L. Jacques, Ph. D.", str); 62 puts(str);

126 Appendix B Source Code of the Program mcml

63 64 CenterStr(COLWIDTH, 65 "Laser Biology Research Laboratory - Box 17",str); 66 puts(str); 67 68 CenterStr(COLWIDTH, "M.D. Anderson Cancer Center", str); 69 puts(str); 70 71 CenterStr(COLWIDTH, "University of Texas", str); 72 puts(str); 73 74 CenterStr(COLWIDTH, "Houston, TX 77030", str); 75 puts(str); 76 77 CenterStr(COLWIDTH, "Fax: (713)792-3995", str); 78 puts(str); 79 puts(""); 80 81 CenterStr(COLWIDTH, version, str); 82 puts(str); 83 puts("\n\n\n\n"); 84 } 85 #undef COLWIDTH 86 87 /*********************************************************** 88 * Get a filename and open it for reading, retry until 89 * the file can be opened. '.' terminates the program. 90 * 91 * If Fname != NULL, try Fname first. 92 ****/ 93 FILE *GetFile(char *Fname) 94 { 95 FILE * file=NULL; 96 Boolean firsttime=1; 97 98 do { 99 if(firsttime && Fname[0]!='\0') { 100 /* use the filename from command line */ 101 firsttime = 0; 102 } 103 else { 104 printf("Input filename(or . to exit):"); 105 scanf("%s", Fname); 106 firsttime = 0; 107 } 108 109 if(strlen(Fname) == 1 && Fname[0] == '.') 110 exit(1); /* exit if no filename entered. */ 111 112 file = fopen(Fname, "r"); 113 } while(file == NULL); 114 115 return(file); 116 } 117 118 /*********************************************************** 119 * Kill the ith char (counting from 0), push the following 120 * chars forward by one. 121 ****/ 122 void KillChar(size_t i, char * Str) 123 { 124 size_t sl = strlen(Str); 125 126 for(;i<sl;i++) Str[i] = Str[i+1]; 127 } 128 129 /***********************************************************

Section B.3 mcmlio.c 127

130 * Eliminate the chars in a string which are not printing 131 * chars or spaces. 132 * 133 * Spaces include ' ', '\f', '\t' etc. 134 * 135 * Return 1 if no nonprinting chars found, otherwise 136 * return 0. 137 ****/ 138 Boolean CheckChar(char * Str) 139 { 140 Boolean found = 0; /* found bad char. */ 141 size_t sl = strlen(Str); 142 size_t i=0; 143 144 while(i<sl) 145 if (Str[i]<0 || Str[i]>255) 146 nrerror("Non-ASCII file\n"); 147 else if(isprint(Str[i]) || isspace(Str[i])) 148 i++; 149 else { 150 found = 1; 151 KillChar(i, Str); 152 sl--; 153 } 154 155 return(found); 156 } 157 158 /*********************************************************** 159 * Return 1 if this line is a comment line in which the 160 * first non-space character is "#". 161 * 162 * Also return 1 if this line is space line. 163 ****/ 164 Boolean CommentLine(char *Buf) 165 { 166 size_t spn, cspn; 167 168 spn = strspn(Buf, " \t"); 169 /* length spanned by space or tab chars. */ 170 171 cspn = strcspn(Buf, "#\n"); 172 /* length before the 1st # or return. */ 173 174 if(spn == cspn) /* comment line or space line. */ 175 return(1); 176 else /* the line has data. */ 177 return(0); 178 } 179 180 /*********************************************************** 181 * Skip space or comment lines and return a data line only. 182 ****/ 183 char * FindDataLine(FILE *File_Ptr) 184 { 185 char buf[STRLEN]; 186 187 buf[0] = '\0'; 188 do { /* skip space or comment lines. */ 189 if(fgets(buf, 255, File_Ptr) == NULL) { 190 printf("Incomplete data\n"); 191 buf[0]='\0'; 192 break; 193 } 194 else 195 CheckChar(buf); 196 } while(CommentLine(buf));

128 Appendix B Source Code of the Program mcml

197 198 return(buf); 199 } 200 201 /*********************************************************** 202 * Skip file version, then read number of runs. 203 ****/ 204 short ReadNumRuns(FILE* File_Ptr) 205 { 206 char buf[STRLEN]; 207 short n=0; 208 209 FindDataLine(File_Ptr); /* skip file version. */ 210 211 strcpy(buf, FindDataLine(File_Ptr)); 212 if(buf[0]=='\0') nrerror("Reading number of runs\n"); 213 sscanf(buf, "%hd",&n); 214 return(n); 215 } 216 217 218 /*********************************************************** 219 * Read the file name and the file format. 220 * 221 * The file format can be either A for ASCII or B for 222 * binary. 223 ****/ 224 void ReadFnameFormat(FILE *File_Ptr, InputStruct *In_Ptr) 225 { 226 char buf[STRLEN]; 227 228 /** read in file name and format. **/ 229 strcpy(buf, FindDataLine(File_Ptr)); 230 if(buf[0]=='\0') 231 nrerror("Reading file name and format.\n"); 232 sscanf(buf, "%s %c", 233 In_Ptr->out_fname, &(In_Ptr->out_fformat) ); 234 if(toupper(In_Ptr->out_fformat) != 'B') 235 In_Ptr->out_fformat = 'A'; 236 } 237 238 239 /*********************************************************** 240 * Read the number of photons. 241 ****/ 242 void ReadNumPhotons(FILE *File_Ptr, InputStruct *In_Ptr) 243 { 244 char buf[STRLEN]; 245 246 /** read in number of photons. **/ 247 strcpy(buf, FindDataLine(File_Ptr)); 248 if(buf[0]=='\0') 249 nrerror("Reading number of photons.\n"); 250 sscanf(buf, "%ld", &In_Ptr->num_photons); 251 if(In_Ptr->num_photons<=0) 252 nrerror("Nonpositive number of photons.\n"); 253 } 254 255 256 /*********************************************************** 257 * Read the members dz and dr. 258 ****/ 259 void ReadDzDr(FILE *File_Ptr, InputStruct *In_Ptr) 260 { 261 char buf[STRLEN]; 262 263 /** read in dz, dr. **/

Section B.3 mcmlio.c 129

264 strcpy(buf, FindDataLine(File_Ptr)); 265 if(buf[0]=='\0') nrerror("Reading dz, dr.\n"); 266 sscanf(buf, "%lf%lf", &In_Ptr->dz, &In_Ptr->dr); 267 if(In_Ptr->dz<=0) nrerror("Nonpositive dz.\n"); 268 if(In_Ptr->dr<=0) nrerror("Nonpositive dr.\n"); 269 } 270 271 272 /*********************************************************** 273 * Read the members nz, nr, na. 274 ****/ 275 void ReadNzNrNa(FILE *File_Ptr, InputStruct *In_Ptr) 276 { 277 char buf[STRLEN]; 278 279 /** read in number of dz, dr, da. **/ 280 strcpy(buf, FindDataLine(File_Ptr)); 281 if(buf[0]=='\0') 282 nrerror("Reading number of dz, dr, da's.\n"); 283 sscanf(buf, "%hd%hd%hd", 284 &In_Ptr->nz, &In_Ptr->nr, &In_Ptr->na); 285 if(In_Ptr->nz<=0) 286 nrerror("Nonpositive number of dz's.\n"); 287 if(In_Ptr->nr<=0) 288 nrerror("Nonpositive number of dr's.\n"); 289 if(In_Ptr->na<=0) 290 nrerror("Nonpositive number of da's.\n"); 291 In_Ptr->da = 0.5*PI/In_Ptr->na; 292 } 293 294 295 /*********************************************************** 296 * Read the number of layers. 297 ****/ 298 void ReadNumLayers(FILE *File_Ptr, InputStruct *In_Ptr) 299 { 300 char buf[STRLEN]; 301 302 /** read in number of layers. **/ 303 strcpy(buf, FindDataLine(File_Ptr)); 304 if(buf[0]=='\0') 305 nrerror("Reading number of layers.\n"); 306 sscanf(buf, "%hd", &In_Ptr->num_layers); 307 if(In_Ptr->num_layers<=0) 308 nrerror("Nonpositive number of layers.\n"); 309 } 310 311 312 /*********************************************************** 313 * Read the refractive index n of the ambient. 314 ****/ 315 void ReadAmbient(FILE *File_Ptr, 316 LayerStruct * Layer_Ptr, 317 char *side) 318 { 319 char buf[STRLEN], msg[STRLEN]; 320 double n; 321 322 strcpy(buf, FindDataLine(File_Ptr)); 323 if(buf[0]=='\0') { 324 sprintf(msg, "Rading n of %s ambient.\n", side); 325 nrerror(msg); 326 } 327 328 sscanf(buf, "%lf", &n ); 329 if(n<=0) nrerror("Wrong n.\n"); 330 Layer_Ptr->n = n;

130 Appendix B Source Code of the Program mcml

331 } 332 333 334 /*********************************************************** 335 * Read the parameters of one layer. 336 * 337 * Return 1 if error detected. 338 * Return 0 otherwise. 339 * 340 * *Z_Ptr is the z coordinate of the current layer, which 341 * is used to convert thickness of layer to z coordinates 342 * of the two boundaries of the layer. 343 ****/ 344 Boolean ReadOneLayer(FILE *File_Ptr, 345 LayerStruct * Layer_Ptr, 346 double *Z_Ptr) 347 { 348 char buf[STRLEN], msg[STRLEN]; 349 double d, n, mua, mus, g; /* d is thickness. */ 350 351 strcpy(buf, FindDataLine(File_Ptr)); 352 if(buf[0]=='\0') return(1); /* error. */ 353 354 sscanf(buf, "%lf%lf%lf%lf%lf", &n, &mua, &mus, &g, &d); 355 if(d<0 || n<=0 || mua<0 || mus<0 || g<0 || g>1) 356 return(1); /* error. */ 357 358 Layer_Ptr->n = n; 359 Layer_Ptr->mua = mua; 360 Layer_Ptr->mus = mus; 361 Layer_Ptr->g = g; 362 Layer_Ptr->z0 = *Z_Ptr; 363 *Z_Ptr += d; 364 Layer_Ptr->z1 = *Z_Ptr; 365 366 return(0); 367 } 368 369 /*********************************************************** 370 * Read the parameters of one layer at a time. 371 ****/ 372 void ReadLayerSpecs(FILE *File_Ptr, 373 short Num_Layers, 374 LayerStruct ** Layerspecs_PP) 375 { 376 char msg[STRLEN]; 377 short i=0; 378 double z = 0.0; /* z coordinate of the current layer. */ 379 380 /* Allocate an array for the layer parameters. */ 381 /* layer 0 and layer Num_Layers + 1 are for ambient. */ 382 *Layerspecs_PP = (LayerStruct *) 383 malloc((unsigned) (Num_Layers+2)*sizeof(LayerStruct)); 384 if (!(*Layerspecs_PP)) 385 nrerror("allocation failure in ReadLayerSpecs()"); 386 387 ReadAmbient(File_Ptr, &((*Layerspecs_PP)[i]), "top"); 388 for(i=1; i<=Num_Layers; i++) 389 if(ReadOneLayer(File_Ptr, &((*Layerspecs_PP)[i]), &z)) { 390 sprintf(msg, "Error reading %hd of %hd layers\n", 391 i, Num_Layers); 392 nrerror(msg); 393 } 394 ReadAmbient(File_Ptr, &((*Layerspecs_PP)[i]), "bottom"); 395 } 396 397 /***********************************************************

Section B.3 mcmlio.c 131

398 * Compute the critical angles for total internal 399 * reflection according to the relative refractive index 400 * of the layer. 401 * All layers are processed. 402 ****/ 403 void CriticalAngle( short Num_Layers, 404 LayerStruct ** Layerspecs_PP) 405 { 406 short i=0; 407 double n1, n2; 408 409 for(i=1; i<=Num_Layers; i++) { 410 n1 = (*Layerspecs_PP)[i].n; 411 n2 = (*Layerspecs_PP)[i-1].n; 412 (*Layerspecs_PP)[i].cos_crit0 = n1>n2 ? 413 sqrt(1.0 - n2*n2/(n1*n1)) : 0.0; 414 415 n2 = (*Layerspecs_PP)[i+1].n; 416 (*Layerspecs_PP)[i].cos_crit1 = n1>n2 ? 417 sqrt(1.0 - n2*n2/(n1*n1)) : 0.0; 418 } 419 } 420 421 /*********************************************************** 422 * Read in the input parameters for one run. 423 ****/ 424 void ReadParm(FILE* File_Ptr, InputStruct * In_Ptr) 425 { 426 char buf[STRLEN]; 427 428 In_Ptr->Wth = WEIGHT; 429 430 ReadFnameFormat(File_Ptr, In_Ptr); 431 ReadNumPhotons(File_Ptr, In_Ptr); 432 ReadDzDr(File_Ptr, In_Ptr); 433 ReadNzNrNa(File_Ptr, In_Ptr); 434 ReadNumLayers(File_Ptr, In_Ptr); 435 436 ReadLayerSpecs(File_Ptr, In_Ptr->num_layers, 437 &In_Ptr->layerspecs); 438 CriticalAngle(In_Ptr->num_layers, &In_Ptr->layerspecs); 439 } 440 441 /*********************************************************** 442 * Return 1, if the name in the name list. 443 * Return 0, otherwise. 444 ****/ 445 Boolean NameInList(char *Name, NameLink List) 446 { 447 while (List != NULL) { 448 if(strcmp(Name, List->name) == 0) 449 return(1); 450 List = List->next; 451 }; 452 return(0); 453 } 454 455 /*********************************************************** 456 * Add the name to the name list. 457 ****/ 458 void AddNameToList(char *Name, NameLink * List_Ptr) 459 { 460 NameLink list = *List_Ptr; 461 462 if(list == NULL) { /* first node. */ 463 *List_Ptr = list = (NameLink)malloc(sizeof(NameNode)); 464 strcpy(list->name, Name);

132 Appendix B Source Code of the Program mcml

465 list->next = NULL; 466 } 467 else { /* subsequent nodes. */ 468 /* Move to the last node. */ 469 while(list->next != NULL) 470 list = list->next; 471 472 /* Append a node to the list. */ 473 list->next = (NameLink)malloc(sizeof(NameNode)); 474 list = list->next; 475 strcpy(list->name, Name); 476 list->next = NULL; 477 } 478 } 479 480 /*********************************************************** 481 * Check against duplicated file names. 482 * 483 * A linked list is set up to store the file names used 484 * in this input data file. 485 ****/ 486 Boolean FnameTaken(char *fname, NameLink * List_Ptr) 487 { 488 if(NameInList(fname, *List_Ptr)) 489 return(1); 490 else { 491 AddNameToList(fname, List_Ptr); 492 return(0); 493 } 494 } 495 496 /*********************************************************** 497 * Free each node in the file name list. 498 ****/ 499 void FreeFnameList(NameLink List) 500 { 501 NameLink next; 502 503 while(List != NULL) { 504 next = List->next; 505 free(List); 506 List = next; 507 } 508 } 509 510 /*********************************************************** 511 * Check the input parameters for each run. 512 ****/ 513 void CheckParm(FILE* File_Ptr, InputStruct * In_Ptr) 514 { 515 short i_run; 516 short num_runs; /* number of independent runs. */ 517 NameLink head = NULL; 518 Boolean name_taken;/* output files share the same */ 519 /* file name.*/ 520 char msg[STRLEN]; 521 522 num_runs = ReadNumRuns(File_Ptr); 523 for(i_run=1; i_run<=num_runs; i_run++) { 524 printf("Checking input data for run %hd\n", i_run); 525 ReadParm(File_Ptr, In_Ptr); 526 527 name_taken = FnameTaken(In_Ptr->out_fname, &head); 528 if(name_taken) 529 sprintf(msg, "file name %s duplicated.\n", 530 In_Ptr->out_fname); 531

Section B.3 mcmlio.c 133

532 free(In_Ptr->layerspecs); 533 if(name_taken) nrerror(msg); 534 } 535 FreeFnameList(head); 536 rewind(File_Ptr); 537 } 538 539 540 /*********************************************************** 541 * Allocate the arrays in OutStruct for one run, and 542 * array elements are automatically initialized to zeros. 543 ****/ 544 void InitOutputData(InputStruct In_Parm, 545 OutStruct * Out_Ptr) 546 { 547 short nz = In_Parm.nz; 548 short nr = In_Parm.nr; 549 short na = In_Parm.na; 550 short nl = In_Parm.num_layers; 551 /* remember to use nl+2 because of 2 for ambient. */ 552 553 if(nz<=0 || nr<=0 || na<=0 || nl<=0) 554 nrerror("Wrong grid parameters.\n"); 555 556 /* Init pure numbers. */ 557 Out_Ptr->Rsp = 0.0; 558 Out_Ptr->Rd = 0.0; 559 Out_Ptr->A = 0.0; 560 Out_Ptr->Tt = 0.0; 561 562 /* Allocate the arrays and the matrices. */ 563 Out_Ptr->Rd_ra = AllocMatrix(0,nr-1,0,na-1); 564 Out_Ptr->Rd_r = AllocVector(0,nr-1); 565 Out_Ptr->Rd_a = AllocVector(0,na-1); 566 567 Out_Ptr->A_rz = AllocMatrix(0,nr-1,0,nz-1); 568 Out_Ptr->A_z = AllocVector(0,nz-1); 569 Out_Ptr->A_l = AllocVector(0,nl+1); 570 571 Out_Ptr->Tt_ra = AllocMatrix(0,nr-1,0,na-1); 572 Out_Ptr->Tt_r = AllocVector(0,nr-1); 573 Out_Ptr->Tt_a = AllocVector(0,na-1); 574 } 575 576 /*********************************************************** 577 * Undo what InitOutputData did. 578 * i.e. free the data allocations. 579 ****/ 580 void FreeData(InputStruct In_Parm, OutStruct * Out_Ptr) 581 { 582 short nz = In_Parm.nz; 583 short nr = In_Parm.nr; 584 short na = In_Parm.na; 585 short nl = In_Parm.num_layers; 586 /* remember to use nl+2 because of 2 for ambient. */ 587 588 free(In_Parm.layerspecs); 589 590 FreeMatrix(Out_Ptr->Rd_ra, 0,nr-1,0,na-1); 591 FreeVector(Out_Ptr->Rd_r, 0,nr-1); 592 FreeVector(Out_Ptr->Rd_a, 0,na-1); 593 594 FreeMatrix(Out_Ptr->A_rz, 0, nr-1, 0,nz-1); 595 FreeVector(Out_Ptr->A_z, 0, nz-1); 596 FreeVector(Out_Ptr->A_l, 0,nl+1); 597 598 FreeMatrix(Out_Ptr->Tt_ra, 0,nr-1,0,na-1);

134 Appendix B Source Code of the Program mcml

599 FreeVector(Out_Ptr->Tt_r, 0,nr-1); 600 FreeVector(Out_Ptr->Tt_a, 0,na-1); 601 } 602 603 /*********************************************************** 604 * Get 1D array elements by summing the 2D array elements. 605 ****/ 606 void Sum2DRd(InputStruct In_Parm, OutStruct * Out_Ptr) 607 { 608 short nr = In_Parm.nr; 609 short na = In_Parm.na; 610 short ir,ia; 611 double sum; 612 613 for(ir=0; ir<nr; ir++) { 614 sum = 0.0; 615 for(ia=0; ia<na; ia++) sum += Out_Ptr->Rd_ra[ir][ia]; 616 Out_Ptr->Rd_r[ir] = sum; 617 } 618 619 for(ia=0; ia<na; ia++) { 620 sum = 0.0; 621 for(ir=0; ir<nr; ir++) sum += Out_Ptr->Rd_ra[ir][ia]; 622 Out_Ptr->Rd_a[ia] = sum; 623 } 624 625 sum = 0.0; 626 for(ir=0; ir<nr; ir++) sum += Out_Ptr->Rd_r[ir]; 627 Out_Ptr->Rd = sum; 628 } 629 630 /*********************************************************** 631 * Return the index to the layer according to the index 632 * to the grid line system in z direction (Iz). 633 * 634 * Use the center of box. 635 ****/ 636 short IzToLayer(short Iz, InputStruct In_Parm) 637 { 638 short i=1; /* index to layer. */ 639 short num_layers = In_Parm.num_layers; 640 double dz = In_Parm.dz; 641 642 while( (Iz+0.5)*dz >= In_Parm.layerspecs[i].z1 643 && i<num_layers) i++; 644 645 return(i); 646 } 647 648 /*********************************************************** 649 * Get 1D array elements by summing the 2D array elements. 650 ****/ 651 void Sum2DA(InputStruct In_Parm, OutStruct * Out_Ptr) 652 { 653 short nz = In_Parm.nz; 654 short nr = In_Parm.nr; 655 short iz,ir; 656 double sum; 657 658 for(iz=0; iz<nz; iz++) { 659 sum = 0.0; 660 for(ir=0; ir<nr; ir++) sum += Out_Ptr->A_rz[ir][iz]; 661 Out_Ptr->A_z[iz] = sum; 662 } 663 664 sum = 0.0; 665 for(iz=0; iz<nz; iz++) {

Section B.3 mcmlio.c 135

666 sum += Out_Ptr->A_z[iz]; 667 Out_Ptr->A_l[IzToLayer(iz, In_Parm)] 668 += Out_Ptr->A_z[iz]; 669 } 670 Out_Ptr->A = sum; 671 } 672 673 /*********************************************************** 674 * Get 1D array elements by summing the 2D array elements. 675 ****/ 676 void Sum2DTt(InputStruct In_Parm, OutStruct * Out_Ptr) 677 { 678 short nr = In_Parm.nr; 679 short na = In_Parm.na; 680 short ir,ia; 681 double sum; 682 683 for(ir=0; ir<nr; ir++) { 684 sum = 0.0; 685 for(ia=0; ia<na; ia++) sum += Out_Ptr->Tt_ra[ir][ia]; 686 Out_Ptr->Tt_r[ir] = sum; 687 } 688 689 for(ia=0; ia<na; ia++) { 690 sum = 0.0; 691 for(ir=0; ir<nr; ir++) sum += Out_Ptr->Tt_ra[ir][ia]; 692 Out_Ptr->Tt_a[ia] = sum; 693 } 694 695 sum = 0.0; 696 for(ir=0; ir<nr; ir++) sum += Out_Ptr->Tt_r[ir]; 697 Out_Ptr->Tt = sum; 698 } 699 700 /*********************************************************** 701 * Scale Rd and Tt properly. 702 * 703 * "a" stands for angle alpha. 704 **** 705 * Scale Rd(r,a) and Tt(r,a) by 706 * (area perpendicular to photon direction) 707 * x(solid angle)x(No. of photons). 708 * or 709 * [2*PI*r*dr*cos(a)]x[2*PI*sin(a)*da]x[No. of photons] 710 * or 711 * [2*PI*PI*dr*da*r*sin(2a)]x[No. of photons] 712 **** 713 * Scale Rd(r) and Tt(r) by 714 * (area on the surface)x(No. of photons). 715 **** 716 * Scale Rd(a) and Tt(a) by 717 * (solid angle)x(No. of photons). 718 ****/ 719 void ScaleRdTt(InputStruct In_Parm, OutStruct * Out_Ptr) 720 { 721 short nr = In_Parm.nr; 722 short na = In_Parm.na; 723 double dr = In_Parm.dr; 724 double da = In_Parm.da; 725 short ir,ia; 726 double scale1, scale2; 727 728 scale1 = 4.0*PI*PI*dr*sin(da/2)*dr*In_Parm.num_photons; 729 /* The factor (ir+0.5)*sin(2a) to be added. */ 730 731 for(ir=0; ir<nr; ir++) 732 for(ia=0; ia<na; ia++) {

136 Appendix B Source Code of the Program mcml

733 scale2 = 1.0/((ir+0.5)*sin(2.0*(ia+0.5)*da)*scale1); 734 Out_Ptr->Rd_ra[ir][ia] *= scale2; 735 Out_Ptr->Tt_ra[ir][ia] *= scale2; 736 } 737 738 scale1 = 2.0*PI*dr*dr*In_Parm.num_photons; 739 /* area is 2*PI*[(ir+0.5)*dr]*dr.*/ 740 /* ir+0.5 to be added. */ 741 742 for(ir=0; ir<nr; ir++) { 743 scale2 = 1.0/((ir+0.5)*scale1); 744 Out_Ptr->Rd_r[ir] *= scale2; 745 Out_Ptr->Tt_r[ir] *= scale2; 746 } 747 748 scale1 = 2.0*PI*da*In_Parm.num_photons; 749 /* solid angle is 2*PI*sin(a)*da. sin(a) to be added. */ 750 751 for(ia=0; ia<na; ia++) { 752 scale2 = 1.0/(sin((ia+0.5)*da)*scale1); 753 Out_Ptr->Rd_a[ia] *= scale2; 754 Out_Ptr->Tt_a[ia] *= scale2; 755 } 756 757 scale2 = 1.0/(double)In_Parm.num_photons; 758 Out_Ptr->Rd *= scale2; 759 Out_Ptr->Tt *= scale2; 760 } 761 762 /*********************************************************** 763 * Scale absorption arrays properly. 764 ****/ 765 void ScaleA(InputStruct In_Parm, OutStruct * Out_Ptr) 766 { 767 short nz = In_Parm.nz; 768 short nr = In_Parm.nr; 769 double dz = In_Parm.dz; 770 double dr = In_Parm.dr; 771 short nl = In_Parm.num_layers; 772 short iz,ir; 773 short il; 774 double scale1; 775 776 /* Scale A_rz. */ 777 scale1 = 2.0*PI*dr*dr*dz*In_Parm.num_photons; 778 /* volume is 2*pi*(ir+0.5)*dr*dr*dz.*/ 779 /* ir+0.5 to be added. */ 780 for(iz=0; iz<nz; iz++) 781 for(ir=0; ir<nr; ir++) 782 Out_Ptr->A_rz[ir][iz] /= (ir+0.5)*scale1; 783 784 /* Scale A_z. */ 785 scale1 = 1.0/(dz*In_Parm.num_photons); 786 for(iz=0; iz<nz; iz++) 787 Out_Ptr->A_z[iz] *= scale1; 788 789 /* Scale A_l. Avoid int/int. */ 790 scale1 = 1.0/(double)In_Parm.num_photons; 791 for(il=0; il<=nl+1; il++) 792 Out_Ptr->A_l[il] *= scale1; 793 794 Out_Ptr->A *=scale1; 795 } 796 797 /*********************************************************** 798 * Sum and scale results of current run. 799 ****/

Section B.3 mcmlio.c 137

800 void SumScaleResult(InputStruct In_Parm, 801 OutStruct * Out_Ptr) 802 { 803 /* Get 1D & 0D results. */ 804 Sum2DRd(In_Parm, Out_Ptr); 805 Sum2DA(In_Parm, Out_Ptr); 806 Sum2DTt(In_Parm, Out_Ptr); 807 808 ScaleRdTt(In_Parm, Out_Ptr); 809 ScaleA(In_Parm, Out_Ptr); 810 } 811 812 /*********************************************************** 813 * Write the version number as the first string in the 814 * file. 815 * Use chars only so that they can be read as either 816 * ASCII or binary. 817 ****/ 818 void WriteVersion(FILE *file, char *Version) 819 { 820 fprintf(file, 821 "%s \t# Version number of the file format.\n\n", 822 Version); 823 fprintf(file, "####\n# Data categories include: \n"); 824 fprintf(file, "# InParm, RAT, \n"); 825 fprintf(file, "# A_l, A_z, Rd_r, Rd_a, Tt_r, Tt_a, \n"); 826 fprintf(file, "# A_rz, Rd_ra, Tt_ra \n####\n\n"); 827 } 828 829 /*********************************************************** 830 * Write the input parameters to the file. 831 ****/ 832 void WriteInParm(FILE *file, InputStruct In_Parm) 833 { 834 short i; 835 836 fprintf(file, 837 "InParm \t\t\t# Input parameters. cm is used.\n"); 838 839 fprintf(file, 840 "%s \tA\t\t# output file name, ASCII.\n", 841 In_Parm.out_fname); 842 fprintf(file, 843 "%ld \t\t\t# No. of photons\n", In_Parm.num_photons); 844 845 fprintf(file, 846 "%G\t%G\t\t# dz, dr [cm]\n", In_Parm.dz,In_Parm.dr); 847 fprintf(file, "%hd\t%hd\t%hd\t# No. of dz, dr, da.\n\n", 848 In_Parm.nz, In_Parm.nr, In_Parm.na); 849 850 fprintf(file, 851 "%hd\t\t\t\t\t# Number of layers\n", 852 In_Parm.num_layers); 853 fprintf(file, 854 "#n\tmua\tmus\tg\td\t# One line for each layer\n"); 855 fprintf(file, 856 "%G\t\t\t\t\t# n for medium above\n", 857 In_Parm.layerspecs[0].n); 858 for(i=1; i<=In_Parm.num_layers; i++) { 859 LayerStruct s; 860 s = In_Parm.layerspecs[i]; 861 fprintf(file, "%G\t%G\t%G\t%G\t%G\t# layer %hd\n", 862 s.n, s.mua, s.mus, s.g, s.z1-s.z0, i); 863 } 864 fprintf(file, "%G\t\t\t\t\t# n for medium below\n\n", 865 In_Parm.layerspecs[i].n); 866 }

138 Appendix B Source Code of the Program mcml

867 868 /*********************************************************** 869 * Write reflectance, absorption, transmission. 870 ****/ 871 void WriteRAT(FILE * file, OutStruct Out_Parm) 872 { 873 fprintf(file, 874 "RAT #Reflectance, absorption, transmission. \n"); 875 /* flag. */ 876 877 fprintf(file, 878 "%-14.6G \t#Specular reflectance [-]\n", Out_Parm.Rsp); 879 fprintf(file, 880 "%-14.6G \t#Diffuse reflectance [-]\n", Out_Parm.Rd); 881 fprintf(file, 882 "%-14.6G \t#Absorbed fraction [-]\n", Out_Parm.A); 883 fprintf(file, 884 "%-14.6G \t#Transmittance [-]\n", Out_Parm.Tt); 885 886 fprintf(file, "\n"); 887 } 888 889 /*********************************************************** 890 * Write absorption as a function of layer. 891 ****/ 892 void WriteA_layer(FILE * file, 893 short Num_Layers, 894 OutStruct Out_Parm) 895 { 896 short i; 897 898 fprintf(file, 899 "A_l #Absorption as a function of layer. [-]\n"); 900 /* flag. */ 901 902 for(i=1; i<=Num_Layers; i++) 903 fprintf(file, "%12.4G\n", Out_Parm.A_l[i]); 904 fprintf(file, "\n"); 905 } 906 907 /*********************************************************** 908 * 5 numbers each line. 909 ****/ 910 void WriteRd_ra(FILE * file, 911 short Nr, 912 short Na, 913 OutStruct Out_Parm) 914 { 915 short ir, ia; 916 917 fprintf(file, 918 "%s\n%s\n%s\n%s\n%s\n%s\n", /* flag. */ 919 "# Rd[r][angle]. [1/(cm2sr)].", 920 "# Rd[0][0], [0][1],..[0][na-1]", 921 "# Rd[1][0], [1][1],..[1][na-1]", 922 "# ...", 923 "# Rd[nr-1][0], [nr-1][1],..[nr-1][na-1]", 924 "Rd_ra"); 925 926 for(ir=0;ir<Nr;ir++) 927 for(ia=0;ia<Na;ia++) { 928 fprintf(file, "%12.4E ", Out_Parm.Rd_ra[ir][ia]); 929 if( (ir*Na + ia + 1)%5 == 0) fprintf(file, "\n"); 930 } 931 932 fprintf(file, "\n"); 933 }

Section B.3 mcmlio.c 139

934 935 /*********************************************************** 936 * 1 number each line. 937 ****/ 938 void WriteRd_r(FILE * file, 939 short Nr, 940 OutStruct Out_Parm) 941 { 942 short ir; 943 944 fprintf(file, 945 "Rd_r #Rd[0], [1],..Rd[nr-1]. [1/cm2]\n"); /* flag. */ 946 947 for(ir=0;ir<Nr;ir++) { 948 fprintf(file, "%12.4E\n", Out_Parm.Rd_r[ir]); 949 } 950 951 fprintf(file, "\n"); 952 } 953 954 /*********************************************************** 955 * 1 number each line. 956 ****/ 957 void WriteRd_a(FILE * file, 958 short Na, 959 OutStruct Out_Parm) 960 { 961 short ia; 962 963 fprintf(file, 964 "Rd_a #Rd[0], [1],..Rd[na-1]. [sr-1]\n"); /* flag. */ 965 966 for(ia=0;ia<Na;ia++) { 967 fprintf(file, "%12.4E\n", Out_Parm.Rd_a[ia]); 968 } 969 970 fprintf(file, "\n"); 971 } 972 973 /*********************************************************** 974 * 5 numbers each line. 975 ****/ 976 void WriteTt_ra(FILE * file, 977 short Nr, 978 short Na, 979 OutStruct Out_Parm) 980 { 981 short ir, ia; 982 983 fprintf(file, 984 "%s\n%s\n%s\n%s\n%s\n%s\n", /* flag. */ 985 "# Tt[r][angle]. [1/(cm2sr)].", 986 "# Tt[0][0], [0][1],..[0][na-1]", 987 "# Tt[1][0], [1][1],..[1][na-1]", 988 "# ...", 989 "# Tt[nr-1][0], [nr-1][1],..[nr-1][na-1]", 990 "Tt_ra"); 991 992 for(ir=0;ir<Nr;ir++) 993 for(ia=0;ia<Na;ia++) { 994 fprintf(file, "%12.4E ", Out_Parm.Tt_ra[ir][ia]); 995 if( (ir*Na + ia + 1)%5 == 0) fprintf(file, "\n"); 996 } 997 998 fprintf(file, "\n"); 999 } 1000

140 Appendix B Source Code of the Program mcml

1001 /*********************************************************** 1002 * 5 numbers each line. 1003 ****/ 1004 void WriteA_rz(FILE * file, 1005 short Nr, 1006 short Nz, 1007 OutStruct Out_Parm) 1008 { 1009 short iz, ir; 1010 1011 fprintf(file, 1012 "%s\n%s\n%s\n%s\n%s\n%s\n", /* flag. */ 1013 "# A[r][z]. [1/cm3]", 1014 "# A[0][0], [0][1],..[0][nz-1]", 1015 "# A[1][0], [1][1],..[1][nz-1]", 1016 "# ...", 1017 "# A[nr-1][0], [nr-1][1],..[nr-1][nz-1]", 1018 "A_rz"); 1019 1020 for(ir=0;ir<Nr;ir++) 1021 for(iz=0;iz<Nz;iz++) { 1022 fprintf(file, "%12.4E ", Out_Parm.A_rz[ir][iz]); 1023 if( (ir*Nz + iz + 1)%5 == 0) fprintf(file, "\n"); 1024 } 1025 1026 fprintf(file, "\n"); 1027 } 1028 1029 /*********************************************************** 1030 * 1 number each line. 1031 ****/ 1032 void WriteA_z(FILE * file, 1033 short Nz, 1034 OutStruct Out_Parm) 1035 { 1036 short iz; 1037 1038 fprintf(file, 1039 "A_z #A[0], [1],..A[nz-1]. [1/cm]\n"); /* flag. */ 1040 1041 for(iz=0;iz<Nz;iz++) { 1042 fprintf(file, "%12.4E\n", Out_Parm.A_z[iz]); 1043 } 1044 1045 fprintf(file, "\n"); 1046 } 1047 1048 /*********************************************************** 1049 * 1 number each line. 1050 ****/ 1051 void WriteTt_r(FILE * file, 1052 short Nr, 1053 OutStruct Out_Parm) 1054 { 1055 short ir; 1056 1057 fprintf(file, 1058 "Tt_r #Tt[0], [1],..Tt[nr-1]. [1/cm2]\n"); /* flag. */ 1059 1060 for(ir=0;ir<Nr;ir++) { 1061 fprintf(file, "%12.4E\n", Out_Parm.Tt_r[ir]); 1062 } 1063 1064 fprintf(file, "\n"); 1065 } 1066 1067 /***********************************************************

Section B.3 mcmlio.c 141

1068 * 1 number each line. 1069 ****/ 1070 void WriteTt_a(FILE * file, 1071 short Na, 1072 OutStruct Out_Parm) 1073 { 1074 short ia; 1075 1076 fprintf(file, 1077 "Tt_a #Tt[0], [1],..Tt[na-1]. [sr-1]\n"); /* flag. */ 1078 1079 for(ia=0;ia<Na;ia++) { 1080 fprintf(file, "%12.4E\n", Out_Parm.Tt_a[ia]); 1081 } 1082 1083 fprintf(file, "\n"); 1084 } 1085 1086 /*********************************************************** 1087 ****/ 1088 void WriteResult(InputStruct In_Parm, 1089 OutStruct Out_Parm, 1090 char * TimeReport) 1091 { 1092 FILE *file; 1093 1094 file = fopen(In_Parm.out_fname, "w"); 1095 if(file == NULL) nrerror("Cannot open file to write.\n"); 1096 1097 if(toupper(In_Parm.out_fformat) == 'A') 1098 WriteVersion(file, "A1"); 1099 else 1100 WriteVersion(file, "B1"); 1101 1102 fprintf(file, "# %s", TimeReport); 1103 fprintf(file, "\n"); 1104 1105 WriteInParm(file, In_Parm); 1106 WriteRAT(file, Out_Parm); 1107 /* reflectance, absorption, transmittance. */ 1108 1109 /* 1D arrays. */ 1110 WriteA_layer(file, In_Parm.num_layers, Out_Parm); 1111 WriteA_z(file, In_Parm.nz, Out_Parm); 1112 WriteRd_r(file, In_Parm.nr, Out_Parm); 1113 WriteRd_a(file, In_Parm.na, Out_Parm); 1114 WriteTt_r(file, In_Parm.nr, Out_Parm); 1115 WriteTt_a(file, In_Parm.na, Out_Parm); 1116 1117 /* 2D arrays. */ 1118 WriteA_rz(file, In_Parm.nr, In_Parm.nz, Out_Parm); 1119 WriteRd_ra(file, In_Parm.nr, In_Parm.na, Out_Parm); 1120 WriteTt_ra(file, In_Parm.nr, In_Parm.na, Out_Parm); 1121 1122 fclose(file); 1123 }

142 Appendix B Source Code of the Program mcml

B.4 mcmlgo.c

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * Launch, move, and record photon weight. 6 ****/ 7 8 #include "mcml.h" 9 10 #define STANDARDTEST 0 11 /* testing program using fixed rnd seed. */ 12 13 #define PARTIALREFLECTION 0 14 /* 1=split photon, 0=statistical reflection. */ 15 16 #define COSZERO (1.0-1.0E-12) 17 /* cosine of about 1e-6 rad. */ 18 19 #define COS90D 1.0E-6 20 /* cosine of about 1.57 - 1e-6 rad. */ 21 22 23 /*********************************************************** 24 * A random number generator from Numerical Recipes in C. 25 ****/ 26 #define MBIG 1000000000 27 #define MSEED 161803398 28 #define MZ 0 29 #define FAC 1.0E-9 30 31 float ran3(int *idum) 32 { 33 static int inext,inextp; 34 static long ma[56]; 35 static int iff=0; 36 long mj,mk; 37 int i,ii,k; 38 39 if (*idum < 0 || iff == 0) { 40 iff=1; 41 mj=MSEED-(*idum < 0 ? -*idum : *idum); 42 mj %= MBIG; 43 ma[55]=mj; 44 mk=1; 45 for (i=1;i<=54;i++) { 46 ii=(21*i) % 55; 47 ma[ii]=mk; 48 mk=mj-mk; 49 if (mk < MZ) mk += MBIG; 50 mj=ma[ii]; 51 } 52 for (k=1;k<=4;k++) 53 for (i=1;i<=55;i++) { 54 ma[i] -= ma[1+(i+30) % 55]; 55 if (ma[i] < MZ) ma[i] += MBIG; 56 } 57 inext=0; 58 inextp=31; 59 *idum=1; 60 } 61 if (++inext == 56) inext=1; 62 if (++inextp == 56) inextp=1;

Section B.4 mcmlgo.c 143

63 mj=ma[inext]-ma[inextp]; 64 if (mj < MZ) mj += MBIG; 65 ma[inext]=mj; 66 return mj*FAC; 67 } 68 69 #undef MBIG 70 #undef MSEED 71 #undef MZ 72 #undef FAC 73 74 75 /*********************************************************** 76 * Generate a random number between 0 and 1. Take a 77 * number as seed the first time entering the function. 78 * The seed is limited to 1<<15. 79 * We found that when idum is too large, ran3 may return 80 * numbers beyond 0 and 1. 81 ****/ 82 double RandomNum(void) 83 { 84 static Boolean first_time=1; 85 static int idum; /* seed for ran3. */ 86 87 if(first_time) { 88 #if STANDARDTEST /* Use fixed seed to test the program. */ 89 idum = - 1; 90 #else 91 idum = -(int)time(NULL)%(1<<15); 92 /* use 16-bit integer as the seed. */ 93 #endif 94 ran3(&idum); 95 first_time = 0; 96 idum = 1; 97 } 98 99 return( (double)ran3(&idum) ); 100 } 101 102 /*********************************************************** 103 * Compute the specular reflection. 104 * 105 * If the first layer is a turbid medium, use the Fresnel 106 * reflection from the boundary of the first layer as the 107 * specular reflectance. 108 * 109 * If the first layer is glass, multiple reflections in 110 * the first layer is considered to get the specular 111 * reflectance. 112 * 113 * The subroutine assumes the Layerspecs array is correctly 114 * initialized. 115 ****/ 116 double Rspecular(LayerStruct * Layerspecs_Ptr) 117 { 118 double r1, r2; 119 /* direct reflections from the 1st and 2nd layers. */ 120 double temp; 121 122 temp =(Layerspecs_Ptr[0].n - Layerspecs_Ptr[1].n) 123 /(Layerspecs_Ptr[0].n + Layerspecs_Ptr[1].n); 124 r1 = temp*temp; 125 126 if((Layerspecs_Ptr[1].mua == 0.0) 127 && (Layerspecs_Ptr[1].mus == 0.0)) { /* glass layer. */ 128 temp = (Layerspecs_Ptr[1].n - Layerspecs_Ptr[2].n) 129 /(Layerspecs_Ptr[1].n + Layerspecs_Ptr[2].n);

144 Appendix B Source Code of the Program mcml

130 r2 = temp*temp; 131 r1 = r1 + (1-r1)*(1-r1)*r2/(1-r1*r2); 132 } 133 134 return (r1); 135 } 136 137 /*********************************************************** 138 * Initialize a photon packet. 139 ****/ 140 void LaunchPhoton(double Rspecular, 141 LayerStruct * Layerspecs_Ptr, 142 PhotonStruct * Photon_Ptr) 143 { 144 Photon_Ptr->w = 1.0 - Rspecular; 145 Photon_Ptr->dead = 0; 146 Photon_Ptr->layer = 1; 147 Photon_Ptr->s = 0; 148 Photon_Ptr->sleft= 0; 149 150 Photon_Ptr->x = 0.0; 151 Photon_Ptr->y = 0.0; 152 Photon_Ptr->z = 0.0; 153 Photon_Ptr->ux = 0.0; 154 Photon_Ptr->uy = 0.0; 155 Photon_Ptr->uz = 1.0; 156 157 if((Layerspecs_Ptr[1].mua == 0.0) 158 && (Layerspecs_Ptr[1].mus == 0.0)) { /* glass layer. */ 159 Photon_Ptr->layer = 2; 160 Photon_Ptr->z = Layerspecs_Ptr[2].z0; 161 } 162 } 163 164 /*********************************************************** 165 * Choose (sample) a new theta angle for photon propagation 166 * according to the anisotropy. 167 * 168 * If anisotropy g is 0, then 169 * cos(theta) = 2*rand-1. 170 * otherwise 171 * sample according to the Henyey-Greenstein function. 172 * 173 * Returns the cosine of the polar deflection angle theta. 174 ****/ 175 double SpinTheta(double g) 176 { 177 double cost; 178 179 if(g == 0.0) 180 cost = 2*RandomNum() -1; 181 else { 182 double temp = (1-g*g)/(1-g+2*g*RandomNum()); 183 cost = (1+g*g - temp*temp)/(2*g); 184 } 185 return(cost); 186 } 187 188 189 /*********************************************************** 190 * Choose a new direction for photon propagation by 191 * sampling the polar deflection angle theta and the 192 * azimuthal angle psi. 193 * 194 * Note: 195 * theta: 0 - pi so sin(theta) is always positive 196 * feel free to use sqrt() for cos(theta).

Section B.4 mcmlgo.c 145

197 * 198 * psi: 0 - 2pi 199 * for 0-pi sin(psi) is + 200 * for pi-2pi sin(psi) is - 201 ****/ 202 void Spin(double g, 203 PhotonStruct * Photon_Ptr) 204 { 205 double cost, sint; /* cosine and sine of the */ 206 /* polar deflection angle theta. */ 207 double cosp, sinp; /* cosine and sine of the */ 208 /* azimuthal angle psi. */ 209 double ux = Photon_Ptr->ux; 210 double uy = Photon_Ptr->uy; 211 double uz = Photon_Ptr->uz; 212 double psi; 213 214 cost = SpinTheta(g); 215 sint = sqrt(1.0 - cost*cost); 216 /* sqrt() is faster than sin(). */ 217 218 psi = 2.0*PI*RandomNum(); /* spin psi 0-2pi. */ 219 cosp = cos(psi); 220 if(psi<PI) 221 sinp = sqrt(1.0 - cosp*cosp); 222 /* sqrt() is faster than sin(). */ 223 else 224 sinp = - sqrt(1.0 - cosp*cosp); 225 226 if(fabs(uz) > COSZERO) { /* normal incident. */ 227 Photon_Ptr->ux = sint*cosp; 228 Photon_Ptr->uy = sint*sinp; 229 Photon_Ptr->uz = cost*SIGN(uz); 230 /* SIGN() is faster than division. */ 231 } 232 else { /* regular incident. */ 233 double temp = sqrt(1.0 - uz*uz); 234 Photon_Ptr->ux = sint*(ux*uz*cosp - uy*sinp) 235 /temp + ux*cost; 236 Photon_Ptr->uy = sint*(uy*uz*cosp + ux*sinp) 237 /temp + uy*cost; 238 Photon_Ptr->uz = -sint*cosp*temp + uz*cost; 239 } 240 } 241 242 /*********************************************************** 243 * Move the photon s away in the current layer of medium. 244 ****/ 245 void Hop(PhotonStruct * Photon_Ptr) 246 { 247 double s = Photon_Ptr->s; 248 249 Photon_Ptr->x += s*Photon_Ptr->ux; 250 Photon_Ptr->y += s*Photon_Ptr->uy; 251 Photon_Ptr->z += s*Photon_Ptr->uz; 252 } 253 254 /*********************************************************** 255 * If uz != 0, return the photon step size in glass, 256 * Otherwise, return 0. 257 * 258 * The step size is the distance between the current 259 * position and the boundary in the photon direction. 260 * 261 * Make sure uz !=0 before calling this function. 262 ****/ 263 void StepSizeInGlass(PhotonStruct * Photon_Ptr,

146 Appendix B Source Code of the Program mcml

264 InputStruct * In_Ptr) 265 { 266 double dl_b; /* step size to boundary. */ 267 short layer = Photon_Ptr->layer; 268 double uz = Photon_Ptr->uz; 269 270 /* Stepsize to the boundary. */ 271 if(uz>0.0) 272 dl_b = (In_Ptr->layerspecs[layer].z1 - Photon_Ptr->z) 273 /uz; 274 else if(uz<0.0) 275 dl_b = (In_Ptr->layerspecs[layer].z0 - Photon_Ptr->z) 276 /uz; 277 else 278 dl_b = 0.0; 279 280 Photon_Ptr->s = dl_b; 281 } 282 283 /*********************************************************** 284 * Pick a step size for a photon packet when it is in 285 * tissue. 286 * If the member sleft is zero, make a new step size 287 * with: -log(rnd)/(mua+mus). 288 * Otherwise, pick up the leftover in sleft. 289 * 290 * Layer is the index to layer. 291 * In_Ptr is the input parameters. 292 ****/ 293 void StepSizeInTissue(PhotonStruct * Photon_Ptr, 294 InputStruct * In_Ptr) 295 { 296 short layer = Photon_Ptr->layer; 297 double mua = In_Ptr->layerspecs[layer].mua; 298 double mus = In_Ptr->layerspecs[layer].mus; 299 300 if(Photon_Ptr->sleft == 0.0) { /* make a new step. */ 301 double rnd; 302 303 do rnd = RandomNum(); 304 while( rnd <= 0.0 ); /* avoid zero. */ 305 Photon_Ptr->s = -log(rnd)/(mua+mus); 306 } 307 else { /* take the leftover. */ 308 Photon_Ptr->s = Photon_Ptr->sleft/(mua+mus); 309 Photon_Ptr->sleft = 0.0; 310 } 311 } 312 313 /*********************************************************** 314 * Check if the step will hit the boundary. 315 * Return 1 if hit boundary. 316 * Return 0 otherwise. 317 * 318 * If the projected step hits the boundary, the members 319 * s and sleft of Photon_Ptr are updated. 320 ****/ 321 Boolean HitBoundary(PhotonStruct * Photon_Ptr, 322 InputStruct * In_Ptr) 323 { 324 double dl_b; /* length to boundary. */ 325 short layer = Photon_Ptr->layer; 326 double uz = Photon_Ptr->uz; 327 Boolean hit; 328 329 /* Distance to the boundary. */ 330 if(uz>0.0)

Section B.4 mcmlgo.c 147

331 dl_b = (In_Ptr->layerspecs[layer].z1 332 - Photon_Ptr->z)/uz; /* dl_b>0. */ 333 else if(uz<0.0) 334 dl_b = (In_Ptr->layerspecs[layer].z0 335 - Photon_Ptr->z)/uz; /* dl_b>0. */ 336 337 if(uz != 0.0 && Photon_Ptr->s > dl_b) { 338 /* not horizontal & crossing. */ 339 double mut = In_Ptr->layerspecs[layer].mua 340 + In_Ptr->layerspecs[layer].mus; 341 342 Photon_Ptr->sleft = (Photon_Ptr->s - dl_b)*mut; 343 Photon_Ptr->s = dl_b; 344 hit = 1; 345 } 346 else 347 hit = 0; 348 349 return(hit); 350 } 351 352 /*********************************************************** 353 * Drop photon weight inside the tissue (not glass). 354 * 355 * The photon is assumed not dead. 356 * 357 * The weight drop is dw = w*mua/(mua+mus). 358 * 359 * The dropped weight is assigned to the absorption array 360 * elements. 361 ****/ 362 void Drop(InputStruct * In_Ptr, 363 PhotonStruct * Photon_Ptr, 364 OutStruct * Out_Ptr) 365 { 366 double dwa; /* absorbed weight.*/ 367 double x = Photon_Ptr->x; 368 double y = Photon_Ptr->y; 369 short iz, ir; /* index to z & r. */ 370 short layer = Photon_Ptr->layer; 371 double mua, mus; 372 373 /* compute array indices. */ 374 iz = (short)(Photon_Ptr->z/In_Ptr->dz); 375 if(iz>In_Ptr->nz-1) iz=In_Ptr->nz-1; 376 377 ir = (short)(sqrt(x*x+y*y)/In_Ptr->dr); 378 if(ir>In_Ptr->nr-1) ir=In_Ptr->nr-1; 379 380 /* update photon weight. */ 381 mua = In_Ptr->layerspecs[layer].mua; 382 mus = In_Ptr->layerspecs[layer].mus; 383 dwa = Photon_Ptr->w * mua/(mua+mus); 384 Photon_Ptr->w -= dwa; 385 386 /* assign dwa to the absorption array element. */ 387 Out_Ptr->A_rz[ir][iz] += dwa; 388 } 389 390 /*********************************************************** 391 * The photon weight is small, and the photon packet tries 392 * to survive a roulette. 393 ****/ 394 void Roulette(PhotonStruct * Photon_Ptr) 395 { 396 if(Photon_Ptr->w == 0.0) 397 Photon_Ptr->dead = 1;

148 Appendix B Source Code of the Program mcml

398 else if(RandomNum() < CHANCE) /* survived the roulette.*/ 399 Photon_Ptr->w /= CHANCE; 400 else 401 Photon_Ptr->dead = 1; 402 } 403 404 /*********************************************************** 405 * Compute the Fresnel reflectance. 406 * 407 * Make sure that the cosine of the incident angle a1 408 * is positive, and the case when the angle is greater 409 * than the critical angle is ruled out. 410 * 411 * Avoid trigonometric function operations as much as 412 * possible, because they are computation-intensive. 413 ****/ 414 double RFresnel(double n1, /* incident refractive index.*/ 415 double n2, /* transmit refractive index.*/ 416 double ca1, /* cosine of the incident */ 417 /* angle. 0<a1<90 degrees. */ 418 double * ca2_Ptr) /* pointer to the */ 419 /* cosine of the transmission */ 420 /* angle. a2>0. */ 421 { 422 double r; 423 424 if(n1==n2) { /** matched boundary. **/ 425 *ca2_Ptr = ca1; 426 r = 0.0; 427 } 428 else if(ca1>COSZERO) { /** normal incident. **/ 429 *ca2_Ptr = ca1; 430 r = (n2-n1)/(n2+n1); 431 r *= r; 432 } 433 else if(ca1<COS90D) { /** very slant. **/ 434 *ca2_Ptr = 0.0; 435 r = 1.0; 436 } 437 else { /** general. **/ 438 double sa1, sa2; 439 /* sine of the incident and transmission angles. */ 440 double ca2; 441 442 sa1 = sqrt(1-ca1*ca1); 443 sa2 = n1*sa1/n2; 444 if(sa2>=1.0) { 445 /* double check for total internal reflection. */ 446 *ca2_Ptr = 0.0; 447 r = 1.0; 448 } 449 else { 450 double cap, cam; /* cosines of the sum ap or */ 451 /* difference am of the two */ 452 /* angles. ap = a1+a2 */ 453 /* am = a1 - a2. */ 454 double sap, sam; /* sines. */ 455 456 *ca2_Ptr = ca2 = sqrt(1-sa2*sa2); 457 458 cap = ca1*ca2 - sa1*sa2; /* c+ = cc - ss. */ 459 cam = ca1*ca2 + sa1*sa2; /* c- = cc + ss. */ 460 sap = sa1*ca2 + ca1*sa2; /* s+ = sc + cs. */ 461 sam = sa1*ca2 - ca1*sa2; /* s- = sc - cs. */ 462 r = 0.5*sam*sam*(cam*cam+cap*cap)/(sap*sap*cam*cam); 463 /* rearranged for speed. */ 464 }

Section B.4 mcmlgo.c 149

465 } 466 return(r); 467 } 468 469 /*********************************************************** 470 * Record the photon weight exiting the first layer(uz<0), 471 * no matter whether the layer is glass or not, to the 472 * reflection array. 473 * 474 * Update the photon weight as well. 475 ****/ 476 void RecordR(double Refl, /* reflectance. */ 477 InputStruct * In_Ptr, 478 PhotonStruct * Photon_Ptr, 479 OutStruct * Out_Ptr) 480 { 481 double x = Photon_Ptr->x; 482 double y = Photon_Ptr->y; 483 short ir, ia; /* index to r & angle. */ 484 485 ir = (short)(sqrt(x*x+y*y)/In_Ptr->dr); 486 if(ir>In_Ptr->nr-1) ir=In_Ptr->nr-1; 487 488 ia = (short)(acos(-Photon_Ptr->uz)/In_Ptr->da); 489 if(ia>In_Ptr->na-1) ia=In_Ptr->na-1; 490 491 /* assign photon to the reflection array element. */ 492 Out_Ptr->Rd_ra[ir][ia] += Photon_Ptr->w*(1.0-Refl); 493 494 Photon_Ptr->w *= Refl; 495 } 496 497 /*********************************************************** 498 * Record the photon weight exiting the last layer(uz>0), 499 * no matter whether the layer is glass or not, to the 500 * transmittance array. 501 * 502 * Update the photon weight as well. 503 ****/ 504 void RecordT(double Refl, 505 InputStruct * In_Ptr, 506 PhotonStruct * Photon_Ptr, 507 OutStruct * Out_Ptr) 508 { 509 double x = Photon_Ptr->x; 510 double y = Photon_Ptr->y; 511 short ir, ia; /* index to r & angle. */ 512 513 ir = (short)(sqrt(x*x+y*y)/In_Ptr->dr); 514 if(ir>In_Ptr->nr-1) ir=In_Ptr->nr-1; 515 516 ia = (short)(acos(Photon_Ptr->uz)/In_Ptr->da); 517 if(ia>In_Ptr->na-1) ia=In_Ptr->na-1; 518 519 /* assign photon to the transmittance array element. */ 520 Out_Ptr->Tt_ra[ir][ia] += Photon_Ptr->w*(1.0-Refl); 521 522 Photon_Ptr->w *= Refl; 523 } 524 525 /*********************************************************** 526 * Decide whether the photon will be transmitted or 527 * reflected on the upper boundary (uz<0) of the current 528 * layer. 529 * 530 * If "layer" is the first layer, the photon packet will 531 * be partially transmitted and partially reflected if

150 Appendix B Source Code of the Program mcml

532 * PARTIALREFLECTION is set to 1, 533 * or the photon packet will be either transmitted or 534 * reflected determined statistically if PARTIALREFLECTION 535 * is set to 0. 536 * 537 * Record the transmitted photon weight as reflection. 538 * 539 * If the "layer" is not the first layer and the photon 540 * packet is transmitted, move the photon to "layer-1". 541 * 542 * Update the photon parmameters. 543 ****/ 544 void CrossUpOrNot(InputStruct * In_Ptr, 545 PhotonStruct * Photon_Ptr, 546 OutStruct * Out_Ptr) 547 { 548 double uz = Photon_Ptr->uz; /* z directional cosine. */ 549 double uz1; /* cosines of transmission alpha. always */ 550 /* positive. */ 551 double r=0.0; /* reflectance */ 552 short layer = Photon_Ptr->layer; 553 double ni = In_Ptr->layerspecs[layer].n; 554 double nt = In_Ptr->layerspecs[layer-1].n; 555 556 /* Get r. */ 557 if( - uz <= In_Ptr->layerspecs[layer].cos_crit0) 558 r=1.0; /* total internal reflection. */ 559 else r = RFresnel(ni, nt, -uz, &uz1); 560 561 #if PARTIALREFLECTION 562 if(layer == 1 && r<1.0) { /* partially transmitted. */ 563 Photon_Ptr->uz = -uz1; /* transmitted photon. */ 564 RecordR(r, In_Ptr, Photon_Ptr, Out_Ptr); 565 Photon_Ptr->uz = -uz; /* reflected photon. */ 566 } 567 else if(RandomNum() > r) {/* transmitted to layer-1. */ 568 Photon_Ptr->layer--; 569 Photon_Ptr->ux *= ni/nt; 570 Photon_Ptr->uy *= ni/nt; 571 Photon_Ptr->uz = -uz1; 572 } 573 else /* reflected. */ 574 Photon_Ptr->uz = -uz; 575 #else 576 if(RandomNum() > r) { /* transmitted to layer-1. */ 577 if(layer==1) { 578 Photon_Ptr->uz = -uz1; 579 RecordR(0.0, In_Ptr, Photon_Ptr, Out_Ptr); 580 Photon_Ptr->dead = 1; 581 } 582 else { 583 Photon_Ptr->layer--; 584 Photon_Ptr->ux *= ni/nt; 585 Photon_Ptr->uy *= ni/nt; 586 Photon_Ptr->uz = -uz1; 587 } 588 } 589 else /* reflected. */ 590 Photon_Ptr->uz = -uz; 591 #endif 592 } 593 594 /*********************************************************** 595 * Decide whether the photon will be transmitted or be 596 * reflected on the bottom boundary (uz>0) of the current 597 * layer. 598 *

Section B.4 mcmlgo.c 151

599 * If the photon is transmitted, move the photon to 600 * "layer+1". If "layer" is the last layer, record the 601 * transmitted weight as transmittance. See comments for 602 * CrossUpOrNot. 603 * 604 * Update the photon parmameters. 605 ****/ 606 void CrossDnOrNot(InputStruct * In_Ptr, 607 PhotonStruct * Photon_Ptr, 608 OutStruct * Out_Ptr) 609 { 610 double uz = Photon_Ptr->uz; /* z directional cosine. */ 611 double uz1; /* cosines of transmission alpha. */ 612 double r=0.0; /* reflectance */ 613 short layer = Photon_Ptr->layer; 614 double ni = In_Ptr->layerspecs[layer].n; 615 double nt = In_Ptr->layerspecs[layer+1].n; 616 617 /* Get r. */ 618 if( uz <= In_Ptr->layerspecs[layer].cos_crit1) 619 r=1.0; /* total internal reflection. */ 620 else r = RFresnel(ni, nt, uz, &uz1); 621 622 #if PARTIALREFLECTION 623 if(layer == In_Ptr->num_layers && r<1.0) { 624 Photon_Ptr->uz = uz1; 625 RecordT(r, In_Ptr, Photon_Ptr, Out_Ptr); 626 Photon_Ptr->uz = -uz; 627 } 628 else if(RandomNum() > r) {/* transmitted to layer+1. */ 629 Photon_Ptr->layer++; 630 Photon_Ptr->ux *= ni/nt; 631 Photon_Ptr->uy *= ni/nt; 632 Photon_Ptr->uz = uz1; 633 } 634 else /* reflected. */ 635 Photon_Ptr->uz = -uz; 636 #else 637 if(RandomNum() > r) { /* transmitted to layer+1. */ 638 if(layer == In_Ptr->num_layers) { 639 Photon_Ptr->uz = uz1; 640 RecordT(0.0, In_Ptr, Photon_Ptr, Out_Ptr); 641 Photon_Ptr->dead = 1; 642 } 643 else { 644 Photon_Ptr->layer++; 645 Photon_Ptr->ux *= ni/nt; 646 Photon_Ptr->uy *= ni/nt; 647 Photon_Ptr->uz = uz1; 648 } 649 } 650 else /* reflected. */ 651 Photon_Ptr->uz = -uz; 652 #endif 653 } 654 655 /*********************************************************** 656 ****/ 657 void CrossOrNot(InputStruct * In_Ptr, 658 PhotonStruct * Photon_Ptr, 659 OutStruct * Out_Ptr) 660 { 661 if(Photon_Ptr->uz < 0.0) 662 CrossUpOrNot(In_Ptr, Photon_Ptr, Out_Ptr); 663 else 664 CrossDnOrNot(In_Ptr, Photon_Ptr, Out_Ptr); 665 }

152 Appendix B Source Code of the Program mcml

666 667 /*********************************************************** 668 * Move the photon packet in glass layer. 669 * Horizontal photons are killed because they will 670 * never interact with tissue again. 671 ****/ 672 void HopInGlass(InputStruct * In_Ptr, 673 PhotonStruct * Photon_Ptr, 674 OutStruct * Out_Ptr) 675 { 676 double dl; /* step size. 1/cm */ 677 678 if(Photon_Ptr->uz == 0.0) { 679 /* horizontal photon in glass is killed. */ 680 Photon_Ptr->dead = 1; 681 } 682 else { 683 StepSizeInGlass(Photon_Ptr, In_Ptr); 684 Hop(Photon_Ptr); 685 CrossOrNot(In_Ptr, Photon_Ptr, Out_Ptr); 686 } 687 } 688 689 /*********************************************************** 690 * Set a step size, move the photon, drop some weight, 691 * choose a new photon direction for propagation. 692 * 693 * When a step size is long enough for the photon to 694 * hit an interface, this step is divided into two steps. 695 * First, move the photon to the boundary free of 696 * absorption or scattering, then decide whether the 697 * photon is reflected or transmitted. 698 * Then move the photon in the current or transmission 699 * medium with the unfinished stepsize to interaction 700 * site. If the unfinished stepsize is still too long, 701 * repeat the above process. 702 ****/ 703 void HopDropSpinInTissue(InputStruct * In_Ptr, 704 PhotonStruct * Photon_Ptr, 705 OutStruct * Out_Ptr) 706 { 707 StepSizeInTissue(Photon_Ptr, In_Ptr); 708 709 if(HitBoundary(Photon_Ptr, In_Ptr)) { 710 Hop(Photon_Ptr); /* move to boundary plane. */ 711 CrossOrNot(In_Ptr, Photon_Ptr, Out_Ptr); 712 } 713 else { 714 Hop(Photon_Ptr); 715 Drop(In_Ptr, Photon_Ptr, Out_Ptr); 716 Spin(In_Ptr->layerspecs[Photon_Ptr->layer].g, 717 Photon_Ptr); 718 } 719 } 720 721 /*********************************************************** 722 ****/ 723 void HopDropSpin(InputStruct * In_Ptr, 724 PhotonStruct * Photon_Ptr, 725 OutStruct * Out_Ptr) 726 { 727 short layer = Photon_Ptr->layer; 728 729 if((In_Ptr->layerspecs[layer].mua == 0.0) 730 && (In_Ptr->layerspecs[layer].mus == 0.0)) 731 /* glass layer. */ 732 HopInGlass(In_Ptr, Photon_Ptr, Out_Ptr);

Section B.4 mcmlgo.c 153

733 else 734 HopDropSpinInTissue(In_Ptr, Photon_Ptr, Out_Ptr); 735 736 if( Photon_Ptr->w < In_Ptr->Wth && !Photon_Ptr->dead) 737 Roulette(Photon_Ptr); 738 }

154 Appendix B Source Code of the Program mcml

B.5 mcmlnr.c

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * Some routines modified from Numerical Recipes in C, 6 * including error report, array or matrix declaration 7 * and releasing. 8 ****/ 9 #include <stdlib.h> 10 #include <stdio.h> 11 #include <math.h> 12 13 /*********************************************************** 14 * Report error message to stderr, then exit the program 15 * with signal 1. 16 ****/ 17 void nrerror(char error_text[]) 18 19 { 20 fprintf(stderr,"%s\n",error_text); 21 fprintf(stderr,"...now exiting to system...\n"); 22 exit(1); 23 } 24 25 /*********************************************************** 26 * Allocate an array with index from nl to nh inclusive. 27 * 28 * Original matrix and vector from Numerical Recipes in C 29 * don't initialize the elements to zero. This will 30 * be accomplished by the following functions. 31 ****/ 32 double *AllocVector(short nl, short nh) 33 { 34 double *v; 35 short i; 36 37 v=(double *)malloc((unsigned) (nh-nl+1)*sizeof(double)); 38 if (!v) nrerror("allocation failure in vector()"); 39 40 v -= nl; 41 for(i=nl;i<=nh;i++) v[i] = 0.0; /* init. */ 42 return v; 43 } 44 45 /*********************************************************** 46 * Allocate a matrix with row index from nrl to nrh 47 * inclusive, and column index from ncl to nch 48 * inclusive. 49 ****/ 50 double **AllocMatrix(short nrl,short nrh, 51 short ncl,short nch) 52 { 53 short i,j; 54 double **m; 55 56 m=(double **) malloc((unsigned) (nrh-nrl+1) 57 *sizeof(double*)); 58 if (!m) nrerror("allocation failure 1 in matrix()"); 59 m -= nrl; 60 61 for(i=nrl;i<=nrh;i++) { 62 m[i]=(double *) malloc((unsigned) (nch-ncl+1)

Section B.5 mcmlnr.c 155

63 *sizeof(double)); 64 if (!m[i]) nrerror("allocation failure 2 in matrix()"); 65 m[i] -= ncl; 66 } 67 68 for(i=nrl;i<=nrh;i++) 69 for(j=ncl;j<=nch;j++) m[i][j] = 0.0; 70 return m; 71 } 72 73 /*********************************************************** 74 * Release the memory. 75 ****/ 76 void FreeVector(double *v,short nl,short nh) 77 { 78 free((char*) (v+nl)); 79 } 80 81 /*********************************************************** 82 * Release the memory. 83 ****/ 84 void FreeMatrix(double **m,short nrl,short nrh, 85 short ncl,short nch) 86 { 87 short i; 88 89 for(i=nrh;i>=nrl;i--) free((char*) (m[i]+ncl)); 90 free((char*) (m+nrl)); 91 }

156 Appendix C Makefile for the Program mcml

Appendix C. Makefile for the Program mcml

We present the make file used for compiling and linking the code for UNIX users.

This make file (named makefile) can be placed under the same directory as the source

code, and used by the UNIX command make.

CFLAGS = CC=cc RM=/bin/rm -rf LOCAL_LIBRARIES= -lm OBJS = mcmlmain.o mcmlgo.o mcmlio.o mcmlnr.o

.c.o: $(RM) $@ $(CC) -c $(CFLAGS) $*.c #####

all : mcml

mcml: $(OBJS) $(RM) $@ $(CC) -o $@ $(OBJS) $(LOCAL_LIBRARIES)

clean:: $(RM) mcml $(RM) mcmlmain.o

If you use ANSI Standard C compiler (acc) on SPARCstation 2, you need to change cc to

acc in the second line. Similarly, if you want to use GNU C compiler, you need to use gcc

instead of cc. If you need to use a debugger (e.g., dbx), you need to add the option -g to

CFLAGS in the first line, and recompile the source codes using the new make file. Then,

you can call the debugger (e.g., dbx mcml). To compile and link use the makefile, use:

make

Appendix D A Template of mcml Input Data File 157

Appendix D. A Template of mcml Input Data File

This is a template for the input data file. The template file is named as

"template.mci" if nobody has changed its name. You can copy this file to a new file whose

extension is preferably ".mci", and modify the parameters in the new file to solve your

specific problem. Any valid filenames without spaces are acceptable (see Section 9.1 for

detail).

#### # Template of input files for Monte Carlo simulation (mcml). # Anything in a line after "#" is ignored as comments. # Space lines are also ignored. # Lengths are in cm, mua and mus are in 1/cm. ####

1.0 # file version 2 # number of runs

### Specify data for run 1 temp1.mco A # output filename, ASCII/Binary 10 # No. of photons 20E-4 20E-4 # dz, dr 10 20 30 # No. of dz, dr & da.

2 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.3 20 200 0.70 0.01 # layer 1 1.4 10 200 0.90 1.0E+8 # layer 2 1.0 # n for medium below.

### Specify data for run 2 temp2.mco A # output filename, ASCII/Binary 20 # No. of photons 20E-4 20E-4 # dz, dr 80 80 30 # No. of dz, dr & da.

1 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.4 10 200 0.70 1.0E+8 # layer 1 1.0 # n for medium below.

158 Appendix E A Sample Output Data File of mcml

Appendix E. A Sample Output Data File of mcml

This is a sample output data file. To limit the length of the file, we used very small

numbers of grid elements as can be seen in the section of the input parameter in the file.

A1 # Version number of the file format.

#### # Data categories include: # InParm, RAT, # A_l, A_z, Rd_r, Rd_a, Tt_r, Tt_a, # A_rz, Rd_ra, Tt_ra ####

# User time: 0.42 sec = 0.00 hr. Simulation time of this run.

InParm # Input parameters. cm is used. sample.mco A # output file name, ASCII. 100 # No. of photons 0.1 0.1 # dz, dr [cm] 3 3 4 # No. of dz, dr, da.

2 # Number of layers #n mua mus g d # One line for each layer 1 # n for medium above 1.3 5 100 0.7 0.1 # layer 1 1.4 2 10 0 0.2 # layer 2 1 # n for medium below

RAT #Reflectance, absorption, transmission. 0.0170132 #Specular reflectance [-] 0.259251 #Diffuse reflectance [-] 0.708072 #Absorbed fraction [-] 0.0156549 #Transmittance [-]

A_l #Absorption as a function of layer. [-] 0.6418 0.06624

A_z #A[0], [1],..A[nz-1]. [1/cm] 6.4184E+00 4.2203E-01 2.4034E-01

Rd_r #Rd[0], [1],..Rd[nr-1]. [1/cm2] 7.1961E+00 3.1968E-01 1.9419E-02

Rd_a #Rd[0], [1],..Rd[na-1]. [sr-1] 5.5689E-02 6.2845E-02 3.6396E-02 2.9598E-02

Tt_r #Tt[0], [1],..Tt[nr-1]. [1/cm2] 1.5008E-01 4.8650E-02 4.0457E-02

Tt_a #Tt[0], [1],..Tt[na-1]. [sr-1] 1.0965E-03 3.5961E-03

Appendix E A Sample Output Data File of mcml 159

3.1196E-03 1.5692E-03

# A[r][z]. [1/cm3] # A[0][0], [0][1],..[0][nz-1] # A[1][0], [1][1],..[1][nz-1] # ... # A[nr-1][0], [nr-1][1],..[nr-1][nz-1] A_rz 1.7934E+02 5.9590E+00 2.9838E+00 7.4003E+00 1.3974E+00 7.6865E-01 5.5296E-01 6.5647E-01 4.7210E-01

# Rd[r][angle]. [1/(cm2sr)]. # Rd[0][0], [0][1],..[0][na-1] # Rd[1][0], [1][1],..[1][na-1] # ... # Rd[nr-1][0], [nr-1][1],..[nr-1][na-1] Rd_ra 1.7856E+00 2.0312E+00 1.8606E+00 4.0608E+00 0.0000E+00 1.0438E-01 7.3502E-02 2.4768E-01 4.3564E-03 1.2307E-02 8.2665E-04 5.0690E-03

# Tt[r][angle]. [1/(cm2sr)]. # Tt[0][0], [0][1],..[0][na-1] # Tt[1][0], [1][1],..[1][na-1] # ... # Tt[nr-1][0], [nr-1][1],..[nr-1][na-1] Tt_ra 0.0000E+00 2.6871E-02 0.0000E+00 2.5301E-01 0.0000E+00 9.0115E-05 4.2593E-02 0.0000E+00 7.1173E-03 2.2105E-02 1.0191E-02 6.0383E-04

160 Appendix F Several C Shell Scripts

Appendix F. Several C Shell Scripts

F.1 conv.bat for batch processing conv

The C Shell script "conv.bat" is used to batch process the program conv (see

Section 9.4 for description of use of conv.bat).

# Shell script for the convolution program "conv" # Feb. 2, 1992 # # Format: conv.bat filename(s) output_type # output_type includes: Rr, Ra, Az ...

# Check parm, echo the help if something is wrong. if ($#argv == 0 || $#argv >= 3) then echo 'Usage: conv.bat "input_filename(s)" output_type' echo "output_type includes: " echo "I, 3, K" echo "Al, Az, Arz" echo "Rr, Ra, Rra" echo "Tr, Ta, Tra" exit endif

# Check the second parameter. if (! ($2 =~ [Ii3Kk] || \ $2 =~ [Aa][LlZz] || \ $2 =~ [Aa][Rr][Zz] || \ $2 =~ [RrTt][RrAa] || \ $2 =~ [RrTt][Rr][Aa])) then echo "Wrong parm -- $2" echo "output_type includes: " echo "I, 3, K" echo "Al, Az, Arz" echo "Rr, Ra, Rra" echo "Tr, Ta, Tra" exit(3) endif

foreach infile ($1) # make sure the file is existent and readable. if (! -e $infile) then echo "File $infile not exist" exit(2); else if (! -r $infile) then echo "File $infile not readable" exit(2) endif

# remove the existent output files, if any. if ( -e $infile:r.$2) then rm $infile:r.$2 endif

# echo the command sequence to conv. (echo i;echo $infile;\ echo oo;echo $2;echo $infile:r.$2;echo q;echo q;echo y)\ |conv>/dev/null

end # of foreach

Appendix F Several C Shell Scripts 161

F.2 p1 for pasting files of 1D arrays

The C Shell script p1 is used to paste side by side multiple files of 1D arrays,

which are in 2 columns. If the files of 1D arrays share the same first column, p1 will not

duplicate the first column in the pasted file. If the output file is existent, then it is backed

up as the original filename appended with ".bak". The original output file is still kept as

part of the new output file. This script is particularly useful to prepare the data for

processing and presentation by some commercial plotting softwares such as KaleidaGraph.

For example, if you have three mcml output files file1.Rr, file2.Rr, and file3.Rr saved in 2

columns representing the diffuse reflectance as a function of radius r. You can combine

these three files into one file by:

p1 "file?.Rr" filex.Rrs

where filex.Rrs is the filename of the output.

# Paste 1D files (in 2 columns) together side by side. # February 7, 1992.

# Check number of arguments. if ($#argv != 2) then echo 'Usage: p1 "input_file(s)" output_fname' exit(1) endif

onintr catch # Prepare to catch interrpts.

set com = $0 set infiles = $1:q set outfile = $2

# Check validity of arguments for outfile. if ( -e $outfile) then if (! -w $outfile) then echo $outfile not writable exit(2) endif

cp $outfile $outfile.bak # Backup existent files. endif

# Setup temp files with the PID number. set outbuf = /tmp/$com:t.$$.outbuf set buf1 = /tmp/$com:t.$$.buf1 set buf2 = /tmp/$com:t.$$.buf2 set cmp1 = /tmp/$com:t.$$.cmp1 set cmp2 = /tmp/$com:t.$$.cmp2

# Run through each file, keep the results in $outbuf. foreach infile ($infiles) if (! -r $infile) then echo $infile not readable exit(3) endif

162 Appendix F Several C Shell Scripts

# Delimit by tab. awk -F" " '{print $1 "\t" $2}' $infile >! $buf1

if (! -e $outbuf) then cut -f1 $buf1 >! $cmp1 cp $buf1 $outbuf else cut -f1 $buf1 >! $cmp2 diff $cmp1 $cmp2 > /dev/null

if ($status) then # 1st rows are not the same. Paste both columns. paste $outbuf $buf1 >! $buf2; cp $buf2 $outbuf cut -f1 $buf1 >! $cmp1 else # 1st rows are the same. Paste only the 2nd column. cut -f2 $buf1 >! $buf2 paste $outbuf $buf2 >! $buf1; cp $buf1 $outbuf endif endif

end

# Copy $outbuf to $outfile if (! -e $outfile) then cp $outbuf $outfile else paste $outfile $outbuf >! $buf1; cp $buf1 $outfile endif

catch: # jump to here if interrupted rm -f $outbuf $buf1 $buf2 $cmp1 $cmp2 exit(1)

Appendix G Where to Get the Programs mcml and conv 163

Appendix G. Where to Get the Programs mcml and conv

We will eventually set up a bulletin board of our own so that you can download

the software over the network. For now, you can contact Lihong Wang, or Steven L.

Jacques using the following information to get the software.

Lihong Wang, Ph. D.

Laser Biology Research Laboratory – Box 17

University of Texas M. D. Anderson Cancer Center

1515 Holcombe Blvd.

Houston, Texas 77030

Phone: (713)745-1742

Fax: (713)792-3995

email: [email protected]

or

Steven L. Jacques, Ph. D.

Laser Biology Research Laboratory – Box 17

University of Texas M. D. Anderson Cancer Center

1515 Holcombe Blvd.

Houston, Texas 77030

Phone: (713)792-3662

Fax: (713)792-3995

email: [email protected]

Make sure that you tell us the specific machines (including brand and model) you will use

for the simulation, so that we can compile the code correctly for you. If you use IBM PC

compatibles, please also tell us whether you use a math co-processor or not.

164 Appendix H Future Developments of the Package

Appendix H. Future Developments of the Package

The release is by no means the end of the package. We plan to make at least the

following improvements. As we gather comments from users, we may consider even more

improvements.

Collimated responses in mcml

In this version (version 1.0) of mcml, the first photon interactions in the media are

scored into the first grid elements in the r direction together with the later interactions.

The first interactions are all on the z-axis, and should yield a delta function of r (Gardner

et al., 1992b) to be exact. Therefore, they should be scored separately as demonstrated by

Gardner et al.

The specular reflectance is computed analytically using Fresnel's formulas.

However, the reflected photons that are uninteracted inside the tissue are scored into the

first grid elements in the r direction of the diffuse reflectance. To be strict, these photons

should contribute to the specular reflectance rather than the diffuse reflectance although

this is small in thick tissues.

The transmittance in version 1.0 of mcml does not differentiate between diffuse

transmittance and unscattered transmittance. This problem and the problem with the

reflectance can be solved by keeping track of the number of interactions.

Best points for each grid element

As we have discussed in Section 4.3, we should use:

rb = [(n + 0.5) + 1

12 (n + 0.5) ] ∆r

instead of the center of the grid element as the coordinate of the simulated data in the nth

grid element in the r direction. In the case when the radius of a Gaussian beam is

comparable with the grid separation ∆r, this may make a considerably large difference in the convolution program conv.

Appendix H Future Developments of the Package 165

Flexible photon sources

In version 1.0 of mcml, only collimated photon beams incident on the tissue

surface are supported. Several other cylindrically symmetric sources should be able to be

incorporated without substantially modifying the program.

The convolution program conv 1.0 only supports Gaussian beams and circularly

flat beams, it can be easily adopted to convolve over arbitrary beam profiles that are

cylindrically symmetric. For beams that are not cylindrically symmetric, the convolution

still can be done but with longer integration time.

Faster sampling procedures

The sampling of the step size involves an exponential computation which is

computation intensive. Faster approaches can be used as discussed in Section 3.2.

166 References

References

Ahrens, J.H. and U. Dieter, "Computer Methods for Sampling for the Exponential and

Normal Distributions," Comm. ACM, 15, 873 (1972).

Anderson, G., and P. Anderson, "The UNIX C Shell Field Guide," Prentice-Hall (1986).

Arthur, L.J., "UNIX Shell Programming," Second Ed., John Wiley & Sons, Inc. (1990).

Born, M., and E. Wolf, "Principles of Optics: Electromagnetic Theory of Propagation,

Interference and Diffraction of Light," Sixth corrected Ed., Pergamon Press (1986).

Cashwell E.D., C.J. Everett, "A Practical Manual on the Monte Carlo Method for

Random Walk Problems," Pergamon Press, New York (1959).

Cheong W.F., S.A. Prahl, A.J. Welch, "A Review of the Optical Properties of Biological

Tissues," IEEE J Quantum Electronics, 26, 2166-2185 (1990).

Gardner, C.M., and A.J. Welch, private communication, Biomedical Eng. Program, Univ.

of Texas, Austin (1992).

Gardner, C.M., and A.J. Welch, in SPIE Proceedings, Laser-tissue Interaction III, Vol.

1646 (1992b).

Giovanelli, R.G., "Reflection by Semi-Infinite Diffusers," Optica Acta, 2, 153-162 (1955).

Hecht, E., "Optics," Second Ed., Addison-Wesley Publishing Company, Inc. (1987).

Hendricks, J.S., and T.E. Booth, "MCNP Variance Reduction Overview," Lecture Notes

in Physics, 240, 83-92 (1985).

Henyey, L.G., and J.L. Greenstein, "Diffuse Radiation in the Galaxy," Astrophys. J., 93,

70-83 (1941).

Kalos, M.H., and P.A. Whitlock, "Monte Carlo Methods, I: Basics," John Wiley & Sons,

Inc. (1986).

References 167

Keijzer, M., S.L. Jacques, S.A. Prahl, and A.J. Welch, "Light Distributions in Artery

Tissue: Monte Carlo simulations for Finite-Diameter Laser Beams," Lasers in Surg. &.

Med., 9, 148-154 (1989).

Kelley, A., and I. Pohl, "A Book on C: Programming in C," Second Ed.,

Benjamin/Cummings Publishing Company, Inc. (1990).

Lux, I., and L. Koblinger, "Monte Carlo Particle Transport Methods: Neutron and Photon

Calculations," CRC Press (1991).

MacLaren, M.D., G. Marsaglia, and T. Bray, "A Fast Procedure for Generating

Exponential Random Variables," Comm. ACM, 7, 298 (1964).

Marsaglia, G., "Generating Exponential Random Variables," Ann. Math. Stat., 32, 899

(1961).

Plauger, P.J., and J. Brodie, "Standard C," Microsoft Press (1989).

Prahl, S.A., "Calculation of Light Distributions and Optical Properties of Tissue," Ph.D.

Dissertation, Department of Biomedical Engineering, U. Texas at Austin (1988).

Prahl, S.A., M. Keijzer, S.L. Jacques, and A.J. Welch, "A Monte Carlo Model of Light

Propagation in Tissue," Dosimetry of Laser Radiation in Medicine and Biology, SPIE

Institute Series, IS 5, 102-111 (1989). (Note the typo in Eq. (10), where the denominator should be 1 – g0 + 2 g0 ξ).

Press, W.H., B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, "Numerical Recipes in

C," Cambridge Univ. Press (1988).

Spiegel, M. R., "Mathematical Handbook of Formulas and Tables," McGraw-Hill, Inc.,

(1968).

Symantec Corporation, "THINKC User Manual," Symantec Corporation (1991).

van de Hulst, H.C, "Multiple Light Scattering, Volume II," Academic Press, New York

(1980).

Wang, L.-H., and S.L. Jacques, "Hybrid Model of Monte Carlo Simulation Diffusion

Theory for Light Reflectance by Turbid Media," unpublished.

168 References

Wilson, B.C., and S. L. Jacques, "Optical Reflectance and Transmittance of Tissues:

Principles and Applications," IEEE J. of Quant. Electronics, 26 (12), 2186-2199 (1990).

Witt, A.N., "Multiple Scattering in Reflection Nebulae I. a Monte Carlo approach," The

Astrophysical J. Supp. Series, 35, 1-6 (1977).

Wyman, D. R., M. S. Patterson, and B. C. Wilson, "Similarity relations for anisotropic

scattering in Monte Carlo simulations of deeply penetrating neutral particles," J. Comput.

Phys., 81, 137-150 (1989a).

Wyman, D. R., M. S. Patterson, and B. C. Wilson, "Similarity relations for the interaction

parameters in radiation transport," Appl. Opt., 28, 5243-5249 (1989b).

Index 169

Index

A, 37

A_l, 37, 95

A_rz, 36, 96

A_z, 36, 95

absorption, 4

absorption, 15, 24, 37

absorption coefficient, 35

absorption coefficient, 4, 5, 25

Absorption coefficient, 92

absorption-only, 30

angularly resolved diffuse reflectance and

transmittance, 48

anisotropy, 16

anisotropy, 4

anisotropy factor, 35

ANSI C, 87

ANSI Standard C, 87, 91

ANSI Standard C, 32

Apple Edit, 91

arrows, 14

ASCII format, 92

AXUM, 96

azimuthal angle, 15

background job, 95

background processes, 95

batch process, 96

Bessel function, 71, 76

binary files, 91

binary tree, 74

binary tree, 77

bit bucket, 95

Boolean, 35

bugs, 97

buried isotropic photon sources, 110

Cartesian coordinate system, 5

cell, the last, 22

cflow, 41

CHANCE, 33

comment lines, 91, 95

compress, 90

computation results, 47

computation time, 52

constants, 33

contour, 66

Contour, 83

conv, 80

convolution, 67

convolution, 67

convolution error, 85

convolve, 96

coordinate systems, 5

cos_crit0, 35

cos_crit1, 35

COS90D, 33

COSZERO, 33

critical angles, 35

cross the boundary, 38

cumulative distribution function, 7

cylindrical coordinate system, 5

da, 36

Data structures, 34

dead, 34

deflection angle, 15

delta function, 30

depth resolved internal fluence, 50

diffuse reflectance, 22, 37

170 Index

diffuse reflectances, 63, 81, 84

diffuse transmittance, 48

diffuse transmittance, 19

dimensional step size, 14

dimensionless step size, 14

directional cosines, 17

directional cosines, 11, 34

divergence, 71

dr, 36

duplicated filenames, 43

dynamic allocation, 37

dz, 36

editor, 91

eject, 88

electronic mail, 89

EMACS, 91

empirical formulas, 55, 58

empirical formulas, 62

Ethernet, 97

execution, 93

extended trapezoidal integration, 31

extended trapezoidal integration, 74

extraction, subset, 96

extrapolations, 76

file expansion, 97

file format, 92

file of input data, 91

file of output data, 95

File version, 92

filename for data output, 36

first photon interactions, 30

flat beam, 69

flat beams, 72

flow graph, 41

flow of the program, 32

flowchart, 38

fluences, 65, 82, 85

folder, 96

folder, 94

free path, 12

Fresnel reflectances, 12

Fresnel's formulas, 18

FTP, 91, 97

g, 35

Gaussian beam, 69

glass, 12

glass layer, 14, 38

GNUCC, 33

grid line separations, 92

grid system, 5

Henyey and Greenstein, 15

hybrid model, 55

IBM PC compatibles, 94

implicit photon capture, 11

impulse response, 67

impulse response, 66

index range of the array, 37

input data file, 91, 97

input parameters, 35, 95, 97

InputStruct, 36

instructions, 91

integers, 97

integral limits, 77, 79

integrand evaluation, 76, 78

intensity profile, 68

interaction coefficient, 4, 12, 40

interpolations, 76

invariance, 67

isotropic photon sources, 110

isotropic photon sources, buried, 110

job, background, 95

KaleidaGraph, 161

Index 171

KaleidaGraph, 96

Kermit, 91

KERMIT, 97

last cells, 22

Launch photon, 38

layer, 34

Layer parameter, 93

layerspecs, 36

LayerStruct, 35

linear and invariant, 67

linear approximations, 27

linear linked list, 43

linearity, 67

logarithmic operation, 45

log-log plot, 55

log-log scale, 55

long int, 36

Macintosh, 94

mail, 90

mails, 89

make file, 32

matched boundaries, 53

matched boundary, 47

Max, 78, 79

Max, 72

mean free path, 93

mean free path, 13

Mean free path, 2

mean free path, transport, 52, 111

memory, iii, 37, 43, 93

memory, release, 43

Microsoft Word, 91

Min, 78, 79

mismatched boundaries, 58

mismatched boundary, 48

mismatched boundary, 11

MockWrite, 91

modem, 97

modified Bessel function, 71

modify the program, 110

Monte Carlo, 1

mua, 35

multi-layered tissue, 4

multi-layered tissues, 62

multiple runs, 93

multiple simulations, 43

mus, 35

n, 35

na, 36

nesting depth, 41

network, 91

Norton editor, 91

nr, 36

num_layers, 36

num_photons, 36

Number of grid elements, 93

Number of layers, 93

number of photon packets, 36

Number of photon packets, 92

Number of runs, 92

numerical computation, 73

nz, 36

observation point, 68

operating system, 91

out_fformat, 36

out_fname, 35

output data files, 95

Output filename, 92

OutStruct, 37

overflow, 77

overflow, 22

partial reflection approach, 19

172 Index

PARTIALREFLECTION, 33

path, search, 94

photon absorption, 15

photon fluence, 25

photon packet, 11, 34

photon propagation, 11

photon scattering, 15

photon termination, 20

PhotonStruct, 34

physical quantities, 4, 22

position of the photon packet, 14

probability density function, 7

probability of interaction, 12

process, background, 95

profiler, 44, 46

pseudo-random number generator, 7

radially resolved diffuse reflectance, 49

random variable, 7

RAT, 95

Rd, 36

Rd_a, 36, 95

Rd_r, 36, 95

Rd_ra, 36, 96

real time, 43

reflectance, 18

reflection at boundary, 17

reflection at interface, 19

refractive index, 35

Refractive index, 93

release the memory, and continues

execution, 43

resolution, 92

roulette, 36

roulette, 20, 41

Rsp, 36

s, 34

sampling random variables, 7

scattering coefficient, 35

scattering coefficient, 4, 5, 92

scattering function ), 15

script file, 96

search path, 94

search path, 88

semi-infinite turbid medium, 47

shell programming, 96

short, 35

similarity relations, 50, 111

sleft, 34

Snell's law, 18

software installation, 87

solid angle, 23, 48

source, 68

source code, 32

source or error, 79

source point, 68

space lines, 91, 95

specular reflectance, 37, 95

specular reflectance, 12, 22

spherical coordinate system, 5

standard errors, 47

STANDARDTEST, 33

step size, 12, 35

STRLEN, 33

subset extraction, 96

subset of the output data, 96

tar, 90

Taylor series, 28

template file, 91

termination, 20

text editors, 91

text format, 91

THINKCPROFILER, 33

Index 173

threshold weight, 36

time of computation, 52

timer, 97

time-shared system, 53

timing profile, 43

Timing profile, 45

tissue/tissue interface, 19

total absorption, 95

total diffuse reflectance, 95

total diffuse reflectance, 37, 47, 48

total diffuse reflectance and

transmittance, 23

total diffuse transmittance, 48

total transmittance, 95

total transmittance, 47

trajectory, 2

transformation of variables, 69

transmission at boundary, 17

transmission at interface, 19

transmittance, 18

transmittance, 22

transmittances, 63

transmittances, 82, 84

transport mean free path, 52

transport mean free path, 111

trapezoidal rule, 73

Tt, 37

Tt_a, 37, 95

Tt_r, 37, 95

Tt_ra, 37

Tt_ra, 96

uncompress, 89

unit of length, 5

UNIX, 94

unscattered transmittance, 19

unscattered transmittance, 48

user time, 43, 95, 97

user times, 52

uudecode, 89

uuencode, 90

ux, 34

uy, 34

uz, 34

variance, 92

variance reduction technique, 11

vi, 91

w, 34

wave phenomenon,, 2

weight, 11

wild cards, 97

Word, 91

Wth, 36

x, 34

y, 34

z, 34

z0, 35

z1, 35

__MACOSX/FTP-Uploads/ch03MonteCarlo/._Mcman.pdf

FTP-Uploads/ch03MonteCarlo/Mcman.txt

8. Installing mcml and conv This chapter provides the instructions on how to install the software. software installationSince both mcml and conv are written in ANSI Standard C, they in principle should be able to be compiled on any computer systems that support ANSI C. Subject to the computer systems available to this laboratory, we will only provide the executables for Sun workstations, IBM PC compatibles, and Macintoshes. On Sun SPARCstations 2, we have compiled the mcml and conv using the ANSI C (acc). On IBM PC compatibles, we used Microsoft QuickC. And on Macintoshes, we used Symantec THINK C. We will provide the source code, users can feel free to compile them on their computer systems. Consult corresponding manuals for information on how to compile the code. As Monte Carlo simulations are computationally intensive, we suggest that you use workstations such as Sun SPARCstations on which you can submit background jobs and which provide high speed computation. The convolution program is also more pleasant to use if you have a fast computer, although it is not as computation-intensive as Monte Carlo simulations. 8.1 Installing on Sun workstations The distribution disk is an IBM format double density 3 1/2" disk, which Sun SPARCstation 2 should be able to read. The disk includes three directories: mcmlcode, convcode, and Sun. The directory mcmlcode includes all the source code of mcml and the makefile used for acc. The directory convcode includes all the source code of conv and the corresponding makefile. You need to modify the makefiles for other compilers (See Appendix C). The directory Sun includes all the executables, a template file of mcml input (template.mci), a sample mcml output file (sample.mco), and a short manual (mcmlconv.man) which is Chapter 8-10 of this manual. To install the source code of mcml and conv, make two directories called mcml and conv respectively. Then, while in the directories mcml or conv, copy the files under mcmlcode and convcode to the directories mcml and conv correspondingly using command mcopy, e.g.: mkdir mcml cd mcml mcopy -t "a:mcmlcode/*" . where the option "-t" means text transfer and the period "." represents the current directory. More information can be retrieved using "man mtools". Use this command to copy the text files in directory Sun to your working directory. To install the executables, copy the executables to the sub directory ~/bin under your home directory using the command mcopy without options. Then, put the directory ~/bin under the search path in .cshrc or .login if you are using C Shell. Consult manual if you are using other shells. Having finished copying, you can eject the disk using the command eject. If your Sun workstation does not have a floppy drive, you can transfer the files through a networked IBM PC or a compatible. If you have an electronic mail address, we can also send the package to you through mail. 8.2 Installing on IBM PC compatibles For IBM PC's or compatibles, the distribution disk is a double density 3 1/2" disk. An alternative 5 1/4" disk can be sent upon request. The disk includes three directories: mcmlcode, convcode, and IBMPC. The directory mcmlcode includes all the source code of mcml. The directory convcode includes all the source code of conv. The directory IBMPC includes all the executables, a template file of mcml input (template.mci), a sample mcml output file (sample.mco), and a short manual (mcmlconv.man) which is Chapter 8-10 of this manual. The executables include mcml.exe and conv.exe. The code was compiled and linked using Microsoft QuickC 2.5. The executables will be able to detect whether your computer has math coprocessor, and take advantage of the math coprocessors if they are present. The simplest installing process would be: xcopy a: c:/s where /s means copying the subdirectories if they are present. This command will make three directories: mcmlcode, convcode and IBMPC, and copy all the files in each directory. If you want to be able to execute the programs under any directory, you should put the directory IBMPC in the search path. The search path can be changed in the file autoexec.bat. 8.3 Installing on Macintoshes For Macintoshes, the distribution disk is a double density 3 1/2" disk. The disk includes three folders: mcmlcode, convcode, and Mac. The folder mcmlcode includes all the source code of mcml. The folder convcode includes all the source code of conv. The folder Mac includes all the executables, a template file of mcml input (template.mci), a sample mcml output file (sample.mco), and a short manual (mcmlconv.man) which is Chapter 8-10 of this manual. The executables include mcml.fpu, conv.fpu, mcml.020, conv.020, mcml.000, and conv.000 for different types of computers as discussed subsequently. To install the source code of mcml and conv, make two folders called mcml and conv respectively. Then, copy the files under mcmlcode and convcode to the folders mcml and conv correspondingly. Before you install the executables, you need to know what kind of Macintosh you are using. You can test the following conditions to decide which executables to use: A. MC68040 B. MC68020 or MC68030 C. MC68881 or MC68882 If your Macintosh meets condition A, or conditions B and C, you should copy the executables with extensions ".fpu". If your Macintosh meets condition B only, you should copy the executables with extensions ".020". Otherwise, you should use the executables with extensions ".000". We suggest that you remove the extensions of the executables on your hard drive to keep consistency with the manual. 8.4 Installing by Electronic Mail For these users who have electronic mail access on UNIX machines, we can deliver the software package through electronic mails. The package is archived using the command tar, compressed using the command compress, then encoded using the command uuencode before it is mailed out using the mail utilities. After you receive the mail, you need to do the following. 1. Save the mail as a file, e.g., mc.mail. 2. Decode the file (mc.mail) to get a file named mc.tar.Z using uudecode mc.mail 3. Uncompress the file mc.tar.Z to get the file mc.tar uncompress mc.tar.Z 4. Unarchive the file mc.tar to get the package using: tar -xvfo mc.tar At this moment, you should have three directories under the working directory. They are mcmlcode, convcode, and Sun, or IBMPC, or Mac. If you ordered a Sun version of the package, you only need to put the executables under the proper directory., e.g., ~/bin (see Section 8.1). If you ordered an IBM PC version or a Mac version of the package, you need to transfer the files to your local computer using FTP or modem. Then refer to Section 8.2 or 8.3 for details. It is appropriate to describe in more detail how we send the package through electronic mails which is exactly the opposite of the above procedure. We put the package in a working directory which include three subdirectories: mcmlcode, convcode, and Sun, or IBMPC, or Mac. Then: tar -cvf mc.tar compress mc.tar uuencode mc.tar.Z mc.tar.Z > mc.mail mail your_address In the mail utility, you can add in any messages in the beginning of the mail, then you need to use the command r to read in the file mc.mail. Then, you can send the file by typing a period "." and a return in a new line (see the manual page of mail). 9. Instructions for mcml This chapter describes the actual instructions to use mcml. Macintoshes, IBM PC compatibles and UNIX machines are used as examples of computer systems, although mcml can execute on any computer systems that support ANSI Standard C. The reader is assumed to be familiar with the operating system and comfortable with at least one of the text editors on the computer system to be used to execute mcml. Three steps involved in the Monte Carlo simulation using mcml are included in the following sections: preparing the input data file, executing the program mcml with the input data file, processing the output data in the data files named in the input data file. We will also show some known bugs. 9.1 File of input data The first step to do the simulation using mcml is to prepare an input data file (e.g., "filename.mci")file of input data. Any valid filenames on your system without spaces will be acceptable, but extension ".mci" is recommended. In ANSI C, spaces are used as separators. Therefore, filenames with spaces may not be accepted by mcml, although they are allowed by some operating systems themselves such as the Macintosh System. We will use "filename.mci" as an example in the following discussions. This input data file may be edited with any text editors such as Apple Edit, MockWrite or Microsoft Word Wordon Macintoshes, Norton editor NE or Microsoft Word on IBM PC compatibles, vi editor or EMACS on UNIX systems. However, if you use word processors like Microsoft Word to edit the file, make sure that you save the file in text format since mcml does not accept binary files as input. If you are using the UNIX system and are uncomfortable with vi or other editors available on UNIX, you can use editors on your personal computer, then transfer the file using Kermit if you use modem or FTP if your personal computer is on a network. Make sure to use ASCII or text mode when you transfer this file. The best way to write an input data file is to make a copy of the template file called "template.mci" (See Appendix D), then modify the parameters in the file. The input data file is organized line by line. All parameters must be in the right order. The lines with parameters in order must also be in order themselves. However, feel free to insert comment lines or space lines in between to make the file more readable. Comment lines start with the symbol "#". The symbol "#" can also be used after the parameters in a line to mark the start of comments. The parameters in the input data file are read by mcml line by line. If there are multiple parameters in a line, use tabs or spaces to separate them. A tab is preferred, because it aligns the parameters for better readability. All dimensional quantities are in cm or derived from cm. The thickness of each layer is in cm. The grid line separations are also in cm. Absorption coefficient and scattering coefficient are in 1/cm. Each line of the input file is explained in the order that they appear in the input data file as follows. 1. File version of the input data file. Always use "1.0" for now. 2. Number of runs (integer). Each run is an independent simulation. You can specify any number of runs, which is not subject to memory limit. Make sure you use an integer instead of a floating point number for this parameter, e.g., 5 instead of 5.0. 3. Output filename and file format. Extension ".mco" is recommended for the output filenames, e.g., "output1.mco". The program mcml currently only supports ASCII format, therefore always use "A" as the second parameter in this line. Make sure that you use different output filenames if you have multiple runs in an input data file, although mcml checks for this mistake. What is more important is that the filenames should not be the same as the names of existent ones unless you want to overwrite the existent files on purpose. Since the program mcml does not check this error, you will lose the existent files. 4. Number of photon packets to be traced (integer). 5. Separations (in cm) between grid lines in z and r direction of the cylindrical coordinate system. These are floating point numbers. Both z and r originate from the photon incident point on the surface of first layer, and the z axis points down into the turbid medium. Make sure these parameters are large enough to give you an acceptable variance, and small enough to give you an acceptable resolution. These parameters should be determined coordinately with the number of photons to achieve both accuracy and resolution. Also note that users should try to choose grid size in the z direction so that grid boxes do not cross tissue-tissue interfaces or boundaries (see Section 9.5). 6. Number of grid elements (integers) in the z, r directions of the cylindrical coordinate system and in the alpha direction, where alpha is the angle spanned between the photon exiting direction and the surface normal. Since the angle always covers 0 through 90 degrees, the angular separation is 90 degrees divided by the number of angular grid elements specified in this line. Be careful with this line, if the numbers are too large, the output file will be very big because 2D arrays are written into the output file. If you do not need to resolve one of the directions (z or r) or the angle, use 1 (not 0) for that parameter. Make sure to use integers for these three parameters. 7. Number of layers (integer). This number does not include the ambient media above or below the tissue. 8. Refractive index for the top ambient medium above the first layer (e.g., 1.0 for air). 9. Layer parameter lines. One line for each layer. In each line are the refractive index, the absorption coefficient (1/cm), the scattering coefficient (1/cm), the anisotropy factor, and the thickness (cm). To simulate semi-infinite tissue, use a very large thickness (e.g., 1E8 cm) compared with the mean free path of the tissue. 10. Refractive index for the bottom ambient medium below the last layer (e.g., 1.0 for air). 11. Repeat lines 3 through 10 for each additional run if you have multiple runs. Note: Two points are worth noting. The only limit to the number of grid elements and layers is the amount of memory allocated to mcml in your system because the arrays are dynamically allocated according to these parameters. Do not use floating point numbers for the integers. Otherwise, the program may interpret them incorrectly. However, you may use integers for floating point numbers, e.g., 100 instead of 100.0. 9.2 Execution Once the input data file is prepared, the program mcml can be executed using the input data file.execution During the execution, the program mcml will report an output message which gives the number of photons remaining in the simulation, the number of runs left, and the time of ending the job. The first report is after 10 photon packets are traced, then it is updated when every 1/10 of the total number of photon packets are traced. The methods of execution are slightly different on different operating systems. Macintosh To run mcml on Macintosh System 6, you have to copy or move the executable mcml to your working folder where the input data file resides, then double click on the mcml icon to start the program. The program mcml will prompt for the input data filename, which is entered through the keyboard. If the input data file cannot be found, the program will prompt you again until it finds the file or a period "." is typed, where "." is used to abort the program. If you use Macintosh System 7, you may use an alias of mcml instead of a copy of it. IBM PC compatibles For IBM PC compatibles, make sure that the directory with mcml is in the search pathpath, search, which can be checked by typing the command "path" or the file "autoexec.bat". To run mcml with the input data file as a command parameter under DOS command prompt, type: mcml filename.mci If you want to save the output message as a file (e.g., message.out), type: mcml filename.mci > message.out which redirects the output message to the file "message.out". To run mcml in the interactive mode, type the following command without input data filename: mcml Then, the program mcml will prompt for the input data file. UNIX On a UNIX system, you should place the executable mcml in a directory that is in the search path. The directory ~/bin is a good choice. The search path can be found and modified in the file ".cshrc" if you are using C Shell or the file ".login". The three ways of invoking mcml under DOS can be used under UNIX operating systems. Moreover, if you wish to discard the messages during the execution, use the command: mcml filename.mci > /dev/null which redirects the output to the "bit bucket" (/dev/null). You can also simply submit a background job job, backgroundusing: mcml filename.mci > /dev/null & Refer to your UNIX manual for how to inquire about the status of a background job. If you are still in the same session, the command "jobs" can be used in C Shell. Otherwise, you should use the UNIX command "ps" to check for background processesprocess, background. You can also directly look for the output files to check if the job is done. 9.3 File of output data When the job is completed, the results will be written into the output data files file of output dataas you named in your input data file. A sample output data file is shown in Appendix E. The output data files can be read with any text editors if they are ASCII as a result of using "A" for the file format in the input data file. They may be big if your numbers of grid elements are large. The contents of output files are self explanatory. The same policy for the input data file is used for the output data file, that is, comment lines starting with a symbol "#" and space lines are written to the file for clarity. The first line is used for file type identification when the file is read by other applications. Then, the user time spent on the simulation is reported in a comment line. Then, a few categories of data are reported sequentially in the following order: pure numbers, 1D arrays and 2D arrays. The definitions of the output data can be found in Chapter 4. The category "InParm" reports all the input parameters specified in the input data file again so that the output file is a complete reference and the input parameters may also be double checked against any errors in the input data file. The category "RAT" reports the specular reflectance, the total diffuse reflectance, the total absorption, and the total transmittance. The category "A_l" is the absorption as a function of layer. The category "A_z" is the absorption as a function of depth z. The categories "Rd_r" and "Rd_a" are the diffuse reflectances as a function of radius r and angle alpha respectively. The categories "Tt_r" and "Tt_a" are the transmittance as a function of radius r and angle alpha respectively. The 2D arrays A_rz, Rd_ra, and Tt_ra are then reported. The category A_rz is the absorption as a function of depth z and radius r. The categories Rd_ra and Tt_ra are correspondingly the diffuse reflectance and transmittance as a function of radius r and angle alpha. The name of each category is written before the data, such that the data can be easily identified. The units for these data were discussed in Chapter 4. 9.4 Subset of output data Sometimes, only a subset of the output data is needed for presentation or processing. For example, we may need to print a 1D array into a file in XY format, namely two columns of data, or a 2D array in XYZ format. These files can then be read into some commercial applications such as AXUM on IBM PC compatibles and KaleidaGraph on Macintoshes. This subset extraction extraction, subsetcan be done using another program that we have partially completed -- conv. The program conv is intended to read in the output data file of mcml that gives responses of infinitely narrow photon beam, and convolve the output data if the responses of finite size beam are to be computed. The program conv can output the original data or the convolved data in various formats. The convolution part of the program conv has not been finished, although it can be used to extract subsets of the original output data. The program conv is made to be interactive. After the program is invoked, the menu system will direct the data input, output, or process. On Macintosh, copy or move the program conv to your working folder. Start conv by double clicking on the icon. On IBM PC compatibles or UNIX machines, invoke the program conv by typing: conv Follow the menu to input an mcml output file (e.g., "filename.mco"). Then, output specific data to new files. However, for the sake of efficiency, we wrote a C Shell script file "conv.bat" for UNIX users. For shell programming, refer to Anderson et al. (1986) or Arthur (1990). A similar file can be written on MS-DOS operating system. The file "conv.bat" is used for fast batch process. For example, if there are several mcml output files named "outfile1.mco", "outfile2.mco"... "outfilen.mco", and you need to select the diffuse reflectance as a function of radius r of these mcml output files on a UNIX system, then you can use the command: conv.bat "outfile*.mco" Rr This command takes two arguments. The first one gives the mcml output files to be processed. If wild cards, such as * or ?, are used, the argument has to be within quotes to prevent immediate file expansion. The second argument gives the type of subsets to be extracted. In this case, it is the diffuse reflectance as a function of r. The files of the subsets will be named as "outfile*.Rr". In each of these output files, there are two columns, the first one is the radius, and the second one is the reflectance. To check the complete usage of "conv.bat", type "conv.bat" on command line. More examples are: conv.bat "outfile*.mco" Az for 1D absorption as a function of z. In each of the output files of this command, there are two columns representing z and the internal absorption respectively. conv.bat "outfile*.mco" Azr for 2D absorption as a function of z and r. In each of the output files of this command, there are three columns representing z, r, and the internal absorption respectively. If you use UNIX to do the simulation and want to present the results using Macintosh or IBM PC compatibles, transfer the smaller subset files using KERMIT if you use modem or FTP if you use Ethernet. 9.5 Bugs of mcml 1. Users have to be careful with several known bugs about the program mcml version 1.0. If a grid element crosses a medium interface, e.g., a glass/tissue interface, the photon absorption within this grid element is considered to be the absorption in the medium where the center of the grid element is located. Therefore, if the center is on the side of the glass, mcml may report small absorption in the glass. Sometimes, this problem may be avoided by choosing the z-grid system carefully so that the boundaries of elements align with the layer interfaces. 2. The user time of a simulation can be reported as zero if the simulation is long enough to overflow the timer (See the function clock() in the file "mcmlmain.c"). 3. The input parameters in the input data file have to be in the order as specified. Furthermore, you have to use integers for number of photon packets, number of layers, and number of grid elements in the input data file. If floating point numbers are inadvertently used, mcml can not detect the error and may read in the wrong parameters. If you find any new bugs, please report to us using the information in Appendix G -- "Where to Get the Program mcml". It is very important that you provide us enough information about the bug so that we can reproduce it. 10. Instructions for conv This chapter describes the instructions to use the program conv, which is used to convolve the impulse responses of mcml over incident beams of finite size. This program reads the output of mcml, then convolves the impulse responses according to the user specified incident beams. The program can output the original data from mcml or the convolved data in various ASCII formats as discussed subsequently. 10.1 Start conv To start conv on IBM PC compatibles or UNIX machines, invoke the program conv by typing: conv To use conv on Macintoshes, copy or move the program conv to your working folder. Then, double click the conv icon to start it. If you are using System 7, you may take advantage of the alias mechanism. 10.2 Main menu of conv Once conv is started, it is in the main menu of the program after showing some information about the program. In the main menu, the program prompts for a command as: > Main menu (h for help) => To show all the available command, type "h" and return key. It will show you the following information and prompt for the next command. You only need to show the help information when you forget the commands. i = Input filename of mcml output b = specify laser Beam r = convolution Resolution. e = convolution Error. oo = Output Original data oc = Output Convolved data co = Contour output of Original data cc = Contour output of Convolved data so = Scanning output of Original data sc = Scanning output of Convolved data q = Quit * Commands in conv are not case-sensitive > Main menu (h for help) => Each command will be introduced subsequently. 10.3 Command "i" of conv You have to provide the filename of the mcml output to conv. This can be done by typing "i" and return key in the main menu prompt, then type in the filename of the mcml output. For example: > Main menu (h for help) => i Input filename of mcml output(or . to quit): example.mco > Main menu (h for help) => The program returns to the main menu automatically. If the file cannot be located or opened, the program will prompt you to type in another filename. You can also type "." and return key to quit inputting the filename. If the file is not the output of mcml, the program will quit to the operating system. You need to start the program again. 10.4 Command "b" of conv You need to specify the type and parameters of the incident beam. In version 1.0 of conv, only Gaussian beams and circularly flat (rectangular) beams are supported. To enter the incident beam, use command "b". Then you have to choose from "f" for flat beam, "g" for Gaussian beam, or "q" to quit this command. If you choose either flat beam or Gaussian beam., conv asks the total energy and the radius of the beam. For example: > Main menu (h for help) => b Beam profile:f=flat, g=Gaussian. q=quit: f Total energy of the flat beam [J]: 1 Radius of the flat beam [cm]: .1 Total power: 1 J, and radius: 0.1 cm. > Main menu (h for help) => It returns to the main menu automatically. Although we specify units of energy for the incident beam, you can substitute units of power throughout the program. To get reliable results, the radius should be much larger than the grid separation in the r direction of the original mcml output, and much less than the total covered radius by the grid system in the r direction of the original mcml output. As a rule of thumb, the radius should be in the range between about 3 times the grid separation in the r direction and the total grid coverage in the r direction minus the maximum radius of observation (see Eqs. 7.23 & 7.24 in Section 7.4). 10.5 Command "r" of conv This command is used to change the grid separation and the number of grid elements in the r direction for the convolution. Since they take the values of the mcml output as the default, you do not have to enter this command if you do not want to change them. The maximum convolution radius should not be larger than that of the original mcml output to get reliable results. For example: > Main menu (h for help) => r Current resolution: 0.01 cm and number of points: 50 Input resolution in r direction [cm]: .02 Input number of points in r direction: 20 Resolution: 0.02 cm and number of points: 20 > Main menu (h for help) => Note that if the number of points is chosen too large, the program can exit due to the lack of memory. This is a bug in the current version of conv. 10.6 Command "e" of conv The integration is computed iteratively. The iteration stops when the difference between the new estimate and the old estimate of the integration is a small part of the new estimate. This small ratio can be controlled by users using command "e". It ranges between 0 to 1. Small values would give better precision but longer computation time and vice versa. Normally, 0.001 to 0.1 is recommended. The default value is 0.1. For example: > Main menu (h for help) => e Relative convolution error Current value is 0.05 (0.001-0.1 recommended): .01 Special attention has to be paid to this command. The convolution results may have weird discontinuities if the allowed convolution error is too high (see Fig. 7.11), and the convolution process may take too long if the convolution error is too low. The rule of thumb is that you choose the lowest convolution error that does not make the convolution too long to compute. If the convolution results still have any discontinuities which should not be there, you need to decrease the convolution error and redo the convolution. 10.7 Command "oo" of conv After you input the filename of the mcml output , you can output the original data of the mcml output with various formats. One of the formats can be obtained by the command "oo". For example: > Main menu (h for help) => oo > Output mcml data (h for help) => h I = Input parameters of mcml 3 = reflectance, absorption, and transmittance AL = absorption vs layer [-] Az = absorption vs z [1/cm] Arz = absorption vs r & z [1/cm3] Fz = fluence vs z [-] Frz = fluence vs r & z [1/cm2] Rr = diffuse reflectance vs radius r [1/cm2] Ra = diffuse reflectance vs angle alpha [1/sr] Rra = diffuse reflectance vs radius and angle [1/(cm2 sr)] Tr = transmittance vs radius r [1/cm2] Ta = transmittance vs angle alpha [1/sr] Tra = transmittance vs radius and angle [1/(cm2 sr)] K = Keijzer's format Q = Quit to main menu * input filename: example.mco > Output mcml data (h for help) => At this point, you can output various physical quantities by inputting the subcommands, which can be listed by command "h" as shown above. After you type the command, the program will ask you for the output filename. The exact physical meanings of these physical quantities can be found in Chapter 4. The command "i" outputs the input parameters of mcml to a file. The command "3" outputs three quantities to a file including specular reflectance, total diffuse reflectance, absorption probability, and total transmittance, which are actually four numbers. The command "Al" outputs the absorption probability as a function layer to a file. The command "Az" outputs the absorption as a function of z coordinate whose dimension is cm-1. The command "Arz" outputs the absorption probability density as a function of r and z whose dimension is cm-3. The commands "Fz" and "Frz" output the results of the commands "Az" and "Arz" divided by the absorption coefficients. The command "Rr" outputs the diffuse reflectance as a function of r whose unit is cm-2. The command "Ra" outputs the diffuse reflectance as a function of the exit angle a, whose dimension is sr-1. The command "Rra" outputs the diffuse reflectance as a function of r and a, whose unit is cm-2 sr- 1. Similarly, the commands "Tr", "Ta" and "Tra" are the corresponding commands for the transmittance. The command "K" is used to convert the format of the mcml output to the format of Marleen Keijzer's convolution program (in PASCAL on Macintoshes) which was used by our group before the program conv was written. This command is only useful if you have Marleen Keijzer's program. The command "q" will return the program to the main menu. For 1D arrays, the outputs are in two columns. The first column gives the independent variable, and the second column gives the physical quantities. For example, the output of the command "Rr" will have two columns. The first column gives the radius in cm, and the second column gives the diffuse reflectance in cm-2. For 2D arrays, the outputs are in three columns. The first two columns give the first and the second independent variables, and the third column gives the physical quantities. For example, the command "Arz" will give three columns. The first two columns give r and z in cm respectively, and the third column gives the absorption probability density in cm-2 sr-1 as a function of r and z. An example is shown as follows: > Output mcml data (h for help) => Rr Enter output filename with extension .Rr (or . to quit): example.Rr > Output mcml data (h for help) => This command will output the diffuse reflectance as a function of r to the file named "example.Rr". 10.8 Command "oc" of conv After you input the filename of the mcml output and specify the incident photon beam, you can output the convolved data with various formats. One of the formats is writing data in columns, which can be obtained using the command "oc". For example: > Main menu (h for help) => oc > Output convolved data (h for help) => h Arz = absorption vs r & z [J/cm3] Frz = fluence vs r & z [J/cm2] Rr = diffuse reflectance vs radius r [J/cm2] Rra = diffuse reflectance vs radius and angle [J/(cm2 sr)] Tr = transmittance vs radius r [J/cm2] Tra = transmittance vs radius and angle [J/(cm2 sr)] Q = Quit to main menu * input filename: example.mco > Output convolved data (h for help) => At this point, you can output various physical quantities by inputting the subcommands, which can be listed by command "h" as shown above. After you type the command, the program will ask you for the output filename. The exact physical meanings of these physical quantities can be found in Chapters 4 and 7. The command "Arz" outputs the absorption energy density as a function of r and z whose dimension is J cm-3. The command "Frz" outputs the results of the command "Arz" divided by the absorption coefficients, which is the fluence in J cm-2. Since we consider steady- state responses only in mcml and conv, you can systematically replace the energy [Joules] with power [Watts] in conv. The command "Rr" outputs the diffuse reflectance as a function of r whose unit is J cm-2. The command "Rra" outputs the diffuse reflectance as a function of r and a, whose unit is J cm-2 sr-1. Similarly, the commands "Tr" and "Tra" are the corresponding commands for the transmittance. The command "q" will return the program to the main menu. For 1D arrays, the outputs are in two columns. The first column gives the independent variable, and the second column gives the physical quantities. For example, the output of the command "Rr" will have two columns. The first column gives the radius in cm, and the second column gives the diffuse reflectance in J cm-2. For 2D arrays, the outputs are in three columns. The first two columns give the first and the second independent variables respectively, and the third column gives the physical quantities. For example, the command "Arz" will give three columns. The first two columns give r and z in cm respectively, and the third column gives the absorption energy density in J cm-2 sr-1 as a function of r and z. An example is shown as follows: > Output convolved data (h for help) => Rr Enter output filename with extension .Rrc (or . to quit): example.Rrc > Output convolved data (h for help) => This command will output the diffuse reflectance as a function of r to the file named "example.Rrc". 10.9 Command "co" of conv After you input the filename of the mcml output, you can output the original data of the mcml output with various formats. One of the formats for 2D arrays is writing data in contour lines. Every contour line will be given by two columns. This format can be obtained using the command "co" standing for "contours of the original data". Then, the output file can be imported to some plotting software such as KaleidaGraph on Macintoshes, and the contour lines can be drawn. For example: > Main menu (h for help) => co > Contour output of mcml data (h for help) => h A = absorption vs r & z [1/cm3] F = fluence vs r & z [1/cm2] R = diffuse reflectance vs radius and angle [1/(cm2 sr)] T = transmittance vs radius and angle [1/(cm2 sr)] Q = Quit to main menu * input filename: example.mco > Contour output of mcml data (h for help) => Since only the 2D arrays need to be presented in contour lines, there are only four physical quantities. The command "A" outputs the absorption probability density as a function of r and z whose dimension is cm-3. The command "F" outputs the probability fluence as a function of r and z in cm-2. The commands "R" and "T" output diffuse reflectance and transmittance as a function of r and a in cm- 2 sr-1. After you input one of the commands, the program will prompt for the output filename and the isovalues for the contour output. The value range of the physical quantity is shown so that valid isovalues can be provided by users. You can enter as many isovalues as you want. System memory is the only thing that limits the number of isovalues. Stop entering isovalues by inputting a period ".". For example: > Contour output of mcml data (h for help) => A Enter output filename with extension .iso (or . to quit): example.iso The range of the value is 0.156280 to 3294.800000. Input an isovalue or . to stop: 1000 Input an isovalue or . to stop: 100 Input an isovalue or . to stop: 10 Input an isovalue or . to stop: 1 Input an isovalue or . to stop: . > Contour output of mcml data (h for help) => The output file of this example will have eight columns, each pair of columns describe one contour line. The values of the contour lines are 1000, 100, 10, and 1 respectively. 10.10 Command "cc" of conv After you input the filename of the mcml output and specify the incident photon beam, you can output the convolved data with various formats. One of the formats for 2D arrays is writing data in contour lines. Every contour line will be given by two columns. This format can be obtained using the command "cc". The output file can be imported to some plotting software such as KaleidaGraph, and the contour lines can be drawn. For example: > Main menu (h for help) => cc > Contour output of convolved data (h for help) => h A = absorption vs r & z [J/cm3] F = fluence vs r & z [J/cm2] R = diffuse reflectance vs radius and angle [J/(cm2 sr)] T = transmittance vs radius and angle [J/(cm2 sr)] Q = Quit to main menu * input filename: example.mco > Contour output of convolved data (h for help) => Since only the 2D arrays need to be presented in contour lines, there are only four physical quantities. The command "A" outputs the absorption energy density as a function of r and z whose dimension is J cm-3. The command "F" outputs the fluence as a function of r and z in J cm-2. The commands "R" and "T" output diffuse reflectance and transmittance as a function of r and a in J cm-2 sr-1 respectively. After you input one of the commands, the program will prompt for the output filename and the isovalues for the contour output. The value range of the physical quantity is shown so that valid isovalues can be provided by users. You can enter as many isovalues as you want. System memory is the only thing that limits the number of isovalues. Stop entering isovalues by inputting a period ".". For example: > Contour output of convolved data (h for help) => A Enter output filename with extension .iso (or . to quit): exampleAc.iso The range of the value is 0.048200 to 95.624939. Input an isovalue or . to stop: 80 Input an isovalue or . to stop: 8 Input an isovalue or . to stop: 0.8 Input an isovalue or . to stop: . > Contour output of convolved data (h for help) => The output file of this example will have six columns, each pair of columns describe one contour line. The values of the contour lines are 80, 8, and 0.8 respectively. 10.11 Command "so" of conv After you input the filename of the mcml output, you can output the original data of the mcml output with various formats. One of the formats for 2D arrays is writing data in two columns, where the two columns give the physical quantity as a function of one of two independent variables. The other variable is fixed at a certain value which can be chosen by users. This format, we call scanning output, can be obtained using the command "so". The output file can be imported to some plotting software such as KaleidaGraph. For example: > Main menu (h for help) => so > Scans of mcml data (h for help) => h Ar = absorption vs r @ fixed z [1/cm3] Az = absorption vs z @ fixed r [1/cm3] Fr = fluence vs r @ fixed z [1/cm2] Fz = fluence vs z @ fixed r [1/cm2] Rr = diffuse reflectance vs r @ fixed angle [1/(cm2 sr)] Ra = diffuse reflectance vs angle @ fixed r [1/(cm2 sr)] Tr = transmittance vs r @ fixed angle [1/(cm2 sr)] Ta = transmittance vs angle @ fixed r [1/(cm2 sr)] Q = quit * input filename: example.mco > Scans of mcml data (h for help) => The command "Ar" outputs the absorption probability density as a function of r for a fixed z, whose dimension is cm-3. The command "Az" outputs the absorption probability density as a function of z for a fixed r, whose dimension is cm-3. The command "Fr" outputs the probability fluence as a function of r for a fixed z in cm-2. The command "Fz" outputs the probability fluence as a function of z for a fixed r in cm-2. The command "Rr" outputs the diffuse reflectance as a function of r for a fixed a in cm-2sr-1. The command "Ra" outputs the diffuse reflectance as a function of a for a fixed r in cm-2sr-1. The commands "Tr" and "Ta" output the transmittance in the same format as for the diffuse reflectance. The command "q" returns to the main menu. After you input one of the commands, the program will prompt for the output filename and the grid index to the value of the fixed variable. If you want to abort this output, you can input a period "." as the filename. For example: > Scans of mcml data (h for help) => Ar Enter output filename with extension .Ars (or . to quit): example.Ars z grid separation is 0.01 cm. Input fixed z index (0 - 39): 0 > Scans of mcml data (h for help) => This command outputs the absorption as a function of r for a fixed z. The program shows that the z grid separation is 0.01 cm. The number of grid elements in the z direction is 40. The grid index in the z direction is in the range from 0 to 39. The command will generate two columns. The first column is r, and the second is the absorption. 10.12 Command "sc" of conv After you input the filename of the mcml output and specify the incident photon beam, you can output the convolved data with various formats. One of the formats for 2D arrays is writing data in two columns, where the two columns give the physical quantity as a function of one of two independent variables. The other variable is fixed at a certain value which can be chosen by users. This format, we call scanning output, can be obtained using the command "sc". Then, the output file can be imported to some plotting software such as KaleidaGraph. For example: > Main menu (h for help) => sc > Scans of convolved data (h for help) => h Ar = absorption vs r @ fixed z [J/cm3] Az = absorption vs z @ fixed r [J/cm3] Fr = fluence vs r @ fixed z [J/cm2] Fz = fluence vs z @ fixed r [J/cm2] Rr = diffuse reflectance vs r @ fixed angle [J/(cm2 sr)] Ra = diffuse reflectance vs angle @ fixed r [J/(cm2 sr)] Tr = transmittance vs r @ fixed angle [J/(cm2 sr)] Ta = transmittance vs angle @ fixed r [J/(cm2 sr)] Q = quit * input filename: example.mco > Scans of convolved data (h for help) => The command "Ar" outputs the absorption energy density as a function of r for a fixed z, whose dimension is J cm-3. The command "Az" outputs the absorption energy density as a function of z for a fixed r, whose dimension is J cm-3. The command "Fr" outputs the fluence as a function of r for a fixed z in J cm-2. The command "Fz" outputs the fluence as a function of z for a fixed r in J cm-2. The command "Rr" outputs the diffuse reflectance as a function of r for a fixed a in J cm-2 sr-1. The command "Ra" outputs the diffuse reflectance as a function of a for a fixed r in J cm- 2 sr-1. The commands "Tr" and "Ta" output the transmittance in the same format as for the diffuse reflectance. The command "q" returns to the main menu. After you input one of the commands, the program will prompt for the output filename and the grid index to the value of the fixed variable. If you want to abort this output, you can input a period "." as the filename. For example: > Scans of convolved data (h for help) => Ar Enter output filename with extension .Arsc (or . to quit): example.Arsc z grid separation is 0.01 cm. Input fixed z index (0 - 39): 0 > Scans of convolved data (h for help) => This command outputs the absorption as a function of r for a fixed z. The program shows that the z grid separation is 0.01 cm. The number of grids in the z direction is 40. The grid index in the z direction is in the range from 0 to 39. The command will generate two columns. The first column is r, and the second is the absorption. 10.13 Command "q" of conv If you want to quit the program conv, use the command "q" in the main menu. The program will ask you if you really mean to quit. You can answer yes or no. The program will quit if the answer is "y". Otherwise, the program will return to the main menu. For example: > Main menu (h for help) => q Do you really want to quit conv (y/n): n > Main menu (h for help) => q Do you really want to quit conv (y/n): y 10.14 Bugs of conv The convolution results may have weird discontinuities if the allowed convolution error is too high, and the convolution process may take too long if the convolution error is too low. We do not have a good way to predict the best convolution error yet. The rule of thumb is that you choose the lowest convolution error that does not make the convolution too long to compute. If the convolution results still have any discontinuities which should not be there, you need to decrease the convolution error and redo the convolution. As we discussed in Section 7.5, the radius of the incident beam has to be in the right range to get reliable convolution integration due to the spatial resolution and the range of grid system. As a rule of thumb, the radius should be in the range between about 3 times the grid separation in the r direction and the total grid coverage in the r direction minus the maximum radius of observation (see Eqs. 7.23 & 7.24 in Section 7.4). If the number of points in the r direction is chosen too large in the command "r", the program can exit due to the lack of memory. Appendix G. Where to Get the Programs mcml and conv We will eventually set up a bulletin board of our own so that you can download the software over the network. For now, you can contact Lihong Wang, or Steven L. Jacques using the following information to get the software. Lihong Wang, Ph. D. Laser Biology Research Laboratory - 017 University of Texas M. D. Anderson Cancer Center 1515 Holcombe Blvd. Houston, Texas 77030 Fax: (713)792-3995 email: [email protected] or Steven L. Jacques, Ph. D. Laser Biology Research Laboratory - 017 University of Texas M. D. Anderson Cancer Center 1515 Holcombe Blvd. Houston, Texas 77030 Fax: (713)792-3995 email: [email protected] Make sure that you tell us the specific machines (including brand and model) you will use for the simulation, so that we can compile the code correctly for you. If you use IBM PC compatibles, please also tell us whether you use a math co- processor or not. August 20, 1992

__MACOSX/FTP-Uploads/ch03MonteCarlo/._Mcman.txt

FTP-Uploads/ch03MonteCarlo/Mcml.exe

__MACOSX/FTP-Uploads/ch03MonteCarlo/._Mcml.exe

FTP-Uploads/ch03MonteCarlo/MCML.H

/*********************************************************** * Copyright Univ. of Texas M.D. Anderson Cancer Center * 1992. * * Monte Carlo simulation of photon distribution in * multi-layered turbid media in ANSI Standard C. **** * Starting Date: 10/1991. * Current Date: 6/1992. * * Lihong Wang, Ph. D. * Steven L. Jacques, Ph. D. * Laser Biology Research Laboratory - 17 * M.D. Anderson Cancer Center * University of Texas * 1515 Holcombe Blvd. * Houston, TX 77030 * USA * * This program was based on: * (1) The Pascal code written by Marleen Keijzer and * Steven L. Jacques in this laboratory in 1989, which * deals with multi-layered turbid media. * * (2) Algorithm for semi-infinite turbid medium by * S.A. Prahl, M. Keijzer, S.L. Jacques, A.J. Welch, * SPIE Institute Series Vol. IS 5 (1989), and by * A.N. Witt, The Astrophysical journal Supplement * Series 35, 1-6 (1977). * * Major modifications include: * . Conform to ANSI Standard C. * . Removal of limit on number of array elements, * because arrays in this program are dynamically * allocated. This means that the program can accept * any number of layers or gridlines as long as the * memory permits. * . Avoiding global variables whenever possible. This * program has not used global variables so far. * . Grouping variables logically using structures. * . Top-down design, keep each subroutine clear & * short. * . Reflectance and transmittance are angularly * resolved. **** * General Naming Conventions: * Preprocessor names: all capital letters, * e.g. #define PREPROCESSORS * Globals: first letter of each word is capital, no * underscores, * e.g. short GlobalVar; * Dummy variables: first letter of each word is capital, * and words are connected by underscores, * e.g. void NiceFunction(char Dummy_Var); * Local variables: all lower cases, words are connected * by underscores, * e.g. short local_var; * Function names or data types: same as Globals. * **** * Dimension of length: cm. * ****/ #include <math.h> #include <stdlib.h> #include <stdio.h> #include <stddef.h> #include <time.h> #include <string.h> #include <ctype.h> #define PI 3.1415926 #define WEIGHT 1E-4 /* Critical weight for roulette. */ #define CHANCE 0.1 /* Chance of roulette survival. */ #define STRLEN 256 /* String length. */ #define Boolean char #define SIGN(x) ((x)>=0 ? 1:-1) /****************** Stuctures *****************************/ /**** * Structure used to describe a photon packet. ****/ typedef struct { double x, y ,z; /* Cartesian coordinates.[cm] */ double ux, uy, uz;/* directional cosines of a photon. */ double w; /* weight. */ Boolean dead; /* 1 if photon is terminated. */ short layer; /* index to layer where the photon */ /* packet resides. */ double s; /* current step size. [cm]. */ double sleft; /* step size left. dimensionless [-]. */ } PhotonStruct; /**** * Structure used to describe the geometry and optical * properties of a layer. * z0 and z1 are the z coordinates for the upper boundary * and lower boundary respectively. * * cos_crit0 and cos_crit1 are the cosines of the * critical angle of total internal reflection for the * upper boundary and lower boundary respectively. * They are set to zero if no total internal reflection * exists. * They are used for computation speed. ****/ typedef struct { double z0, z1; /* z coordinates of a layer. [cm] */ double n; /* refractive index of a layer. */ double mua; /* absorption coefficient. [1/cm] */ double mus; /* scattering coefficient. [1/cm] */ double g; /* anisotropy. */ double cos_crit0, cos_crit1; } LayerStruct; /**** * Input parameters for each independent run. * * z and r are for the cylindrical coordinate system. [cm] * a is for the angle alpha between the photon exiting * direction and the surface normal. [radian] * * The grid line separations in z, r, and alpha * directions are dz, dr, and da respectively. The numbers * of grid lines in z, r, and alpha directions are * nz, nr, and na respectively. * * The member layerspecs will point to an array of * structures which store parameters of each layer. * This array has (number_layers + 2) elements. One * element is for a layer. * The layers 0 and (num_layers + 1) are for top ambient * medium and the bottom ambient medium respectively. ****/ typedef struct { char out_fname[STRLEN]; /* output file name. */ char out_fformat; /* output file format. */ /* 'A' for ASCII, */ /* 'B' for binary. */ long num_photons; /* to be traced. */ double Wth; /* play roulette if photon */ /* weight < Wth.*/ double dz; /* z grid separation.[cm] */ double dr; /* r grid separation.[cm] */ double da; /* alpha grid separation. */ /* [radian] */ short nz; /* array range 0..nz-1. */ short nr; /* array range 0..nr-1. */ short na; /* array range 0..na-1. */ short num_layers; /* number of layers. */ LayerStruct * layerspecs; /* layer parameters. */ } InputStruct; /**** * Structures for scoring physical quantities. * z and r represent z and r coordinates of the * cylindrical coordinate system. [cm] * a is the angle alpha between the photon exiting * direction and the normal to the surfaces. [radian] * See comments of the InputStruct. * See manual for the physcial quantities. ****/ typedef struct { double Rsp; /* specular reflectance. [-] */ double ** Rd_ra; /* 2D distribution of diffuse */ /* reflectance. [1/(cm2 sr)] */ double * Rd_r; /* 1D radial distribution of diffuse */ /* reflectance. [1/cm2] */ double * Rd_a; /* 1D angular distribution of diffuse */ /* reflectance. [1/sr] */ double Rd; /* total diffuse reflectance. [-] */ double ** A_rz; /* 2D probability density in turbid */ /* media over r & z. [1/cm3] */ double * A_z; /* 1D probability density over z. */ /* [1/cm] */ double * A_l; /* each layer's absorption */ /* probability. [-] */ double A; /* total absorption probability. [-] */ double ** Tt_ra; /* 2D distribution of total */ /* transmittance. [1/(cm2 sr)] */ double * Tt_r; /* 1D radial distribution of */ /* transmittance. [1/cm2] */ double * Tt_a; /* 1D angular distribution of */ /* transmittance. [1/sr] */ double Tt; /* total transmittance. [-] */ } OutStruct; /*********************************************************** * Routine prototypes for dynamic memory allocation and * release of arrays and matrices. * Modified from Numerical Recipes in C. ****/ double *AllocVector(short, short); double **AllocMatrix(short, short,short, short); void FreeVector(double *, short, short); void FreeMatrix(double **, short, short, short, short); void nrerror(char *);

__MACOSX/FTP-Uploads/ch03MonteCarlo/._MCML.H

FTP-Uploads/ch03MonteCarlo/MCMLGO.C

FTP-Uploads/ch03MonteCarlo/MCMLGO.C

/***********************************************************
 *  Copyright Univ. of Texas M.D. Anderson Cancer Center
 *  1992.
 *
 *  Launch, move, and record photon weight.
 ****/

#include   "mcml.h"

#define  STANDARDTEST  0
   /* testing program using fixed rnd seed. */

#define  PARTIALREFLECTION  0      
   /* 1=split photon, 0=statistical reflection. */

#define  COSZERO  ( 1.0 - 1.0E-12 )    
   /* cosine of about 1e-6 rad. */

#define  COS90D   1.0E-6       
   /* cosine of about 1.57 - 1e-6 rad. */


/***********************************************************
 *  A random number generator from Numerical Recipes in C.
 ****/
#define  MBIG  1000000000
#define  MSEED  161803398
#define  MZ  0
#define  FAC  1.0E-9

float  ran3 ( int   * idum )
{
   static   int  inext , inextp ;
   static   long  ma [ 56 ];
   static   int  iff = 0 ;
   long  mj , mk ;
   int  i , ii , k ;
  
   if   ( * idum  <   0   ||  iff  ==   0 )   {
    iff = 1 ;
    mj = MSEED - ( * idum  <   0   ?   -* idum  :   * idum );
    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   ( k = 1 ; k <= 4 ; k ++ )
       for   ( i = 1 ; i <= 55 ; i ++ )   {
    ma [ i ]   -=  ma [ 1 + ( i + 30 )   %   55 ];
     if   ( ma [ i ]   <  MZ )  ma [ i ]   +=  MBIG ;
       }
    inext = 0 ;
    inextp = 31 ;
     * idum = 1 ;
   }
   if   ( ++ inext  ==   56 )  inext = 1 ;
   if   ( ++ inextp  ==   56 )  inextp = 1 ;
  mj = ma [ inext ] - ma [ inextp ];
   if   ( mj  <  MZ )  mj  +=  MBIG ;
  ma [ inext ] = mj ;
   return  mj * FAC ;
}

#undef  MBIG
#undef  MSEED
#undef  MZ
#undef  FAC


/***********************************************************
 *  Generate a random number between 0 and 1.  Take a 
 *  number as seed the first time entering the function.  
 *  The seed is limited to 1<<15.  
 *  We found that when idum is too large, ran3 may return 
 *  numbers beyond 0 and 1.
 ****/
double   RandomNum ( void )
{
   static   Boolean  first_time = 1 ;
   static   int  idum ;    /* seed for ran3. */
  
   if ( first_time )   {
#if  STANDARDTEST  /* Use fixed seed to test the program. */
    idum  =   -   1 ;
#else
    idum  =   - ( int ) time ( NULL ) % ( 1 << 15 );
       /* use 16-bit integer as the seed. */
#endif
    ran3 ( & idum );
    first_time  =   0 ;
    idum  =   1 ;
   }
  
   return (   ( double ) ran3 ( & idum )   );
}

/***********************************************************
 *  Compute the specular reflection. 
 *
 *  If the first layer is a turbid medium, use the Fresnel
 *  reflection from the boundary of the first layer as the 
 *  specular reflectance.
 *
 *  If the first layer is glass, multiple reflections in
 *  the first layer is considered to get the specular
 *  reflectance.
 *
 *  The subroutine assumes the Layerspecs array is correctly 
 *  initialized.
 ****/
double   Rspecular ( LayerStruct   *   Layerspecs_Ptr )
{
   double  r1 ,  r2 ;
     /* direct reflections from the 1st and 2nd layers. */
   double  temp ;
  
  temp  = ( Layerspecs_Ptr [ 0 ]. -   Layerspecs_Ptr [ 1 ]. n )
        / ( Layerspecs_Ptr [ 0 ]. +   Layerspecs_Ptr [ 1 ]. n );
  r1  =  temp * temp ;
  
   if (( Layerspecs_Ptr [ 1 ]. mua  ==   0.0 )  
   &&   ( Layerspecs_Ptr [ 1 ]. mus  ==   0.0 ))    {   /* glass layer. */
    temp  =   ( Layerspecs_Ptr [ 1 ]. -   Layerspecs_Ptr [ 2 ]. n )
           / ( Layerspecs_Ptr [ 1 ]. +   Layerspecs_Ptr [ 2 ]. n );
    r2  =  temp * temp ;
    r1  =  r1  +   ( 1 - r1 ) * ( 1 - r1 ) * r2 / ( 1 - r1 * r2 );
   }
  
   return   ( r1 );   
}

/***********************************************************
 *  Initialize a photon packet.
 ****/
void   LaunchPhoton ( double   Rspecular ,
                   LayerStruct    *   Layerspecs_Ptr ,
                   PhotonStruct   *   Photon_Ptr )
{
   Photon_Ptr -> w      =   1.0   -   Rspecular ;   
   Photon_Ptr -> dead   =   0 ;
   Photon_Ptr -> layer  =   1 ;
   Photon_Ptr -> =   0 ;
   Photon_Ptr -> sleft =   0 ;
  
   Photon_Ptr -> x      =   0.0 ;   
   Photon_Ptr -> y      =   0.0 ;   
   Photon_Ptr -> z      =   0.0 ;   
   Photon_Ptr -> ux     =   0.0 ;   
   Photon_Ptr -> uy     =   0.0 ;   
   Photon_Ptr -> uz     =   1.0 ;   
  
   if (( Layerspecs_Ptr [ 1 ]. mua  ==   0.0 )  
   &&   ( Layerspecs_Ptr [ 1 ]. mus  ==   0.0 ))    {   /* glass layer. */
     Photon_Ptr -> layer    =   2 ;
     Photon_Ptr -> z    =   Layerspecs_Ptr [ 2 ]. z0 ;  
   }
}

/***********************************************************
 *  Choose (sample) a new theta angle for photon propagation
 *  according to the anisotropy.
 *
 *  If anisotropy g is 0, then
 *      cos(theta) = 2*rand-1.
 *  otherwise
 *      sample according to the Henyey-Greenstein function.
 *
 *  Returns the cosine of the polar deflection angle theta.
 ****/
double   SpinTheta ( double  g )
{
   double  cost ;
  
   if ( ==   0.0 )  
    cost  =   2 * RandomNum ()   - 1 ;
   else   {
     double  temp  =   ( 1 - g * g ) / ( 1 - g + 2 * g * RandomNum ());
    cost  =   ( 1 + g * -  temp * temp ) / ( 2 * g );
     if ( cost  <   - 1 )  cost  =   - 1 ;
     else   if ( cost  >   1 )  cost  =   1 ;
   }
   return ( cost );
}


/***********************************************************
 *  Choose a new direction for photon propagation by 
 *  sampling the polar deflection angle theta and the 
 *  azimuthal angle psi.
 *
 *  Note:
 *      theta: 0 - pi so sin(theta) is always positive 
 *      feel free to use sqrt() for cos(theta).
 * 
 *      psi:   0 - 2pi 
 *      for 0-pi  sin(psi) is + 
 *      for pi-2pi sin(psi) is - 
 ****/
void   Spin ( double  g ,
           PhotonStruct   *   Photon_Ptr )
{
   double  cost ,  sint ;      /* cosine and sine of the */
                         /* polar deflection angle theta. */
   double  cosp ,  sinp ;      /* cosine and sine of the */
                         /* azimuthal angle psi. */
   double  ux  =   Photon_Ptr -> ux ;
   double  uy  =   Photon_Ptr -> uy ;
   double  uz  =   Photon_Ptr -> uz ;
   double  psi ;

  cost  =   SpinTheta ( g );
  sint  =  sqrt ( 1.0   -  cost * cost );  
     /* sqrt() is faster than sin(). */

  psi  =   2.0 * PI * RandomNum ();   /* spin psi 0-2pi. */
  cosp  =  cos ( psi );
   if ( psi < PI )
    sinp  =  sqrt ( 1.0   -  cosp * cosp );    
       /* sqrt() is faster than sin(). */
   else
    sinp  =   -  sqrt ( 1.0   -  cosp * cosp );  
  
   if ( fabs ( uz )   >  COSZERO )    {       /* normal incident. */
     Photon_Ptr -> ux  =  sint * cosp ;
     Photon_Ptr -> uy  =  sint * sinp ;
     Photon_Ptr -> uz  =  cost * SIGN ( uz );  
       /* SIGN() is faster than division. */
   }
   else    {         /* regular incident. */
     double  temp  =  sqrt ( 1.0   -  uz * uz );
     Photon_Ptr -> ux  =  sint * ( ux * uz * cosp  -  uy * sinp )
                     / temp  +  ux * cost ;
     Photon_Ptr -> uy  =  sint * ( uy * uz * cosp  +  ux * sinp )
                     / temp  +  uy * cost ;
     Photon_Ptr -> uz  =   - sint * cosp * temp  +  uz * cost ;
   }
}

/***********************************************************
 *  Move the photon s away in the current layer of medium.  
 ****/
void   Hop ( PhotonStruct   *   Photon_Ptr )
{
   double  s  =   Photon_Ptr -> s ;

   Photon_Ptr -> +=  s * Photon_Ptr -> ux ;
   Photon_Ptr -> +=  s * Photon_Ptr -> uy ;
   Photon_Ptr -> +=  s * Photon_Ptr -> uz ;
}            

/***********************************************************
 *  If uz != 0, return the photon step size in glass, 
 *  Otherwise, return 0.
 *
 *  The step size is the distance between the current 
 *  position and the boundary in the photon direction.
 *
 *  Make sure uz !=0 before calling this function.
 ****/
void   StepSizeInGlass ( PhotonStruct   *    Photon_Ptr ,
                      InputStruct    *    In_Ptr )
{
   double  dl_b ;    /* step size to boundary. */
   short   layer  =   Photon_Ptr -> layer ;
   double  uz  =   Photon_Ptr -> uz ;
  
   /* Stepsize to the boundary. */    
   if ( uz > 0.0 )
    dl_b  =   ( In_Ptr -> layerspecs [ layer ]. z1  -   Photon_Ptr -> z )
            / uz ;
   else   if ( uz < 0.0 )
    dl_b  =   ( In_Ptr -> layerspecs [ layer ]. z0  -   Photon_Ptr -> z )
            / uz ;
   else
    dl_b  =   0.0 ;
  
   Photon_Ptr -> =  dl_b ;
}

/***********************************************************
 *  Pick a step size for a photon packet when it is in 
 *  tissue.
 *  If the member sleft is zero, make a new step size 
 *  with: -log(rnd)/(mua+mus).
 *  Otherwise, pick up the leftover in sleft.
 *
 *  Layer is the index to layer.
 *  In_Ptr is the input parameters.
 ****/
void   StepSizeInTissue ( PhotonStruct   *   Photon_Ptr ,
                       InputStruct    *   In_Ptr )
{
   short   layer  =   Photon_Ptr -> layer ;
   double  mua  =   In_Ptr -> layerspecs [ layer ]. mua ;
   double  mus  =   In_Ptr -> layerspecs [ layer ]. mus ;
  
   if ( Photon_Ptr -> sleft  ==   0.0 )   {    /* make a new step. */
     double  rnd ;

     do  rnd  =   RandomNum ();  
       while (  rnd  <=   0.0   );      /* avoid zero. */
     Photon_Ptr -> =   - log ( rnd ) / ( mua + mus );
   }
   else   {      /* take the leftover. */
     Photon_Ptr -> =   Photon_Ptr -> sleft / ( mua + mus );
     Photon_Ptr -> sleft  =   0.0 ;
   }
}

/***********************************************************
 *  Check if the step will hit the boundary.
 *  Return 1 if hit boundary.
 *  Return 0 otherwise.
 *
 *  If the projected step hits the boundary, the members
 *  s and sleft of Photon_Ptr are updated.
 ****/
Boolean   HitBoundary ( PhotonStruct   *    Photon_Ptr ,
                     InputStruct    *    In_Ptr )
{
   double  dl_b ;    /* length to boundary. */
   short   layer  =   Photon_Ptr -> layer ;
   double  uz  =   Photon_Ptr -> uz ;
   Boolean  hit ;
  
   /* Distance to the boundary. */
   if ( uz > 0.0 )
    dl_b  =   ( In_Ptr -> layerspecs [ layer ]. z1 
             -   Photon_Ptr -> z ) / uz ;      /* dl_b>0. */
   else   if ( uz < 0.0 )
    dl_b  =   ( In_Ptr -> layerspecs [ layer ]. z0 
             -   Photon_Ptr -> z ) / uz ;      /* dl_b>0. */
  
   if ( uz  !=   0.0   &&   Photon_Ptr -> >  dl_b )   {
       /* not horizontal & crossing. */
     double  mut  =   In_Ptr -> layerspecs [ layer ]. mua 
                 +   In_Ptr -> layerspecs [ layer ]. mus ;

     Photon_Ptr -> sleft  =   ( Photon_Ptr -> -  dl_b ) * mut ;
     Photon_Ptr -> s     =  dl_b ;
    hit  =   1 ;
   }
   else
    hit  =   0 ;
  
   return ( hit );
}

/***********************************************************
 *  Drop photon weight inside the tissue (not glass).
 *
 *  The photon is assumed not dead. 
 *
 *  The weight drop is dw = w*mua/(mua+mus).
 *
 *  The dropped weight is assigned to the absorption array 
 *  elements.
 ****/
void   Drop ( InputStruct    *      In_Ptr ,  
           PhotonStruct   *      Photon_Ptr ,
           OutStruct   *         Out_Ptr )
{
   double  dwa ;         /* absorbed weight.*/
   double  x  =   Photon_Ptr -> x ;
   double  y  =   Photon_Ptr -> y ;
   double  izd ,  ird ;    /* LW 5/20/98. To avoid out of short range.*/
   short   iz ,  ir ;      /* index to z & r. */
   short   layer  =   Photon_Ptr -> layer ;
   double  mua ,  mus ;       
  
   /* compute array indices. */
  izd  =   Photon_Ptr -> z / In_Ptr -> dz ;
   if ( izd > In_Ptr -> nz - 1 )  iz = In_Ptr -> nz - 1 ;
   else  iz  =  izd ;
  
  ird  =  sqrt ( x * x + y * y ) / In_Ptr -> dr ;
   if ( ird > In_Ptr -> nr - 1 )  ir = In_Ptr -> nr - 1 ;
   else  ir  =  ird ;
  
   /* update photon weight. */
  mua  =   In_Ptr -> layerspecs [ layer ]. mua ;
  mus  =   In_Ptr -> layerspecs [ layer ]. mus ;
  dwa  =   Photon_Ptr -> *  mua / ( mua + mus );
   Photon_Ptr -> -=  dwa ;
  
   /* assign dwa to the absorption array element. */
   Out_Ptr -> A_rz [ ir ][ iz ]   +=  dwa ;
}

/***********************************************************
 *  The photon weight is small, and the photon packet tries 
 *  to survive a roulette.
 ****/
void   Roulette ( PhotonStruct   *   Photon_Ptr )
{
   if ( Photon_Ptr -> ==   0.0 )   
     Photon_Ptr -> dead  =   1 ;
   else   if ( RandomNum ()   <  CHANCE )   /* survived the roulette.*/
     Photon_Ptr -> /=  CHANCE ;
   else  
     Photon_Ptr -> dead  =   1 ;
}

/***********************************************************
 *  Compute the Fresnel reflectance.
 *
 *  Make sure that the cosine of the incident angle a1
 *  is positive, and the case when the angle is greater 
 *  than the critical angle is ruled out.
 *
 *  Avoid trigonometric function operations as much as
 *  possible, because they are computation-intensive.
 ****/
double   RFresnel ( double  n1 ,    /* incident refractive index.*/
                 double  n2 ,    /* transmit refractive index.*/
                 double  ca1 ,   /* cosine of the incident */
                             /* angle. 0<a1<90 degrees. */
                 double   *  ca2_Ptr )    /* pointer to the */
                             /* cosine of the transmission */
                             /* angle. a2>0. */
{
   double  r ;
  
   if ( n1 == n2 )   {                /** matched boundary. **/
     * ca2_Ptr  =  ca1 ;
    r  =   0.0 ;
   }
   else   if ( ca1 > COSZERO )   {      /** normal incident. **/
     * ca2_Ptr  =  ca1 ;
    r  =   ( n2 - n1 ) / ( n2 + n1 );
    r  *=  r ;
   }
   else   if ( ca1 < COS90D )    {      /** very slant. **/
     * ca2_Ptr  =   0.0 ;
    r  =   1.0 ;
   }
   else    {                     /** general. **/
     double  sa1 ,  sa2 ;     
       /* sine of the incident and transmission angles. */
     double  ca2 ;
    
    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 the sum ap or */
                         /* difference 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 );
}

/***********************************************************
 *  Record the photon weight exiting the first layer(uz<0), 
 *  no matter whether the layer is glass or not, to the 
 *  reflection array.
 *
 *  Update the photon weight as well.
 ****/
void   RecordR ( double           Refl ,     /* reflectance. */
              InputStruct    *   In_Ptr ,
              PhotonStruct   *   Photon_Ptr ,
              OutStruct   *      Out_Ptr )
{
   double  x  =   Photon_Ptr -> x ;
   double  y  =   Photon_Ptr -> y ;
   short   ir ,  ia ;      /* index to r & angle. */
   double  ird ,  iad ;    /* LW 5/20/98. To avoid out of short range.*/
  
  ird  =  sqrt ( x * x + y * y ) / In_Ptr -> dr ;
   if ( ird > In_Ptr -> nr - 1 )  ir = In_Ptr -> nr - 1 ;
   else  ir  =  ird ;
  
  iad  =  acos ( - Photon_Ptr -> uz ) / In_Ptr -> da ;
   if ( iad > In_Ptr -> na - 1 )  ia = In_Ptr -> na - 1 ;
   else  ia  =  iad ;
  
   /* assign photon to the reflection array element. */
   Out_Ptr -> Rd_ra [ ir ][ ia ]   +=   Photon_Ptr -> w * ( 1.0 - Refl );
  
   Photon_Ptr -> *=   Refl ;
}

/***********************************************************
 *  Record the photon weight exiting the last layer(uz>0), 
 *  no matter whether the layer is glass or not, to the 
 *  transmittance array.
 *
 *  Update the photon weight as well.
 ****/
void   RecordT ( double           Refl ,
              InputStruct    *   In_Ptr ,
              PhotonStruct   *   Photon_Ptr ,
              OutStruct   *      Out_Ptr )
{
   double  x  =   Photon_Ptr -> x ;
   double  y  =   Photon_Ptr -> y ;
   short   ir ,  ia ;      /* index to r & angle. */
   double  ird ,  iad ;    /* LW 5/20/98. To avoid out of short range.*/
  
  ird  =  sqrt ( x * x + y * y ) / In_Ptr -> dr ;
   if ( ird > In_Ptr -> nr - 1 )  ir = In_Ptr -> nr - 1 ;
   else  ir  =  ird ;
  
  iad  =  acos ( Photon_Ptr -> uz ) / In_Ptr -> da ;   /* LW 1/12/2000. Removed -. */
   if ( iad > In_Ptr -> na - 1 )  ia = In_Ptr -> na - 1 ;
   else  ia  =  iad ;
  
   /* assign photon to the transmittance array element. */
   Out_Ptr -> Tt_ra [ ir ][ ia ]   +=   Photon_Ptr -> w * ( 1.0 - Refl );
  
   Photon_Ptr -> *=   Refl ;
}

/***********************************************************
 *  Decide whether the photon will be transmitted or 
 *  reflected on the upper boundary (uz<0) of the current 
 *  layer.
 *
 *  If "layer" is the first layer, the photon packet will 
 *  be partially transmitted and partially reflected if 
 *  PARTIALREFLECTION is set to 1,
 *  or the photon packet will be either transmitted or 
 *  reflected determined statistically if PARTIALREFLECTION 
 *  is set to 0.
 *
 *  Record the transmitted photon weight as reflection.  
 *
 *  If the "layer" is not the first layer and the photon 
 *  packet is transmitted, move the photon to "layer-1".
 *
 *  Update the photon parmameters.
 ****/
void   CrossUpOrNot ( InputStruct    *      In_Ptr ,  
                   PhotonStruct   *      Photon_Ptr ,
                   OutStruct   *         Out_Ptr )
{
   double  uz  =   Photon_Ptr -> uz ;   /* z directional cosine. */
   double  uz1 ;     /* cosines of transmission alpha. always */
                 /* positive. */
   double  r = 0.0 ;   /* reflectance */
   short   layer  =   Photon_Ptr -> layer ;
   double  ni  =   In_Ptr -> layerspecs [ layer ]. n ;
   double  nt  =   In_Ptr -> layerspecs [ layer - 1 ]. n ;
  
   /* Get r. */
   if (   -  uz  <=   In_Ptr -> layerspecs [ layer ]. cos_crit0 )  
    r = 1.0 ;              /* total internal reflection. */
   else  r  =   RFresnel ( ni ,  nt ,   - uz ,   & uz1 );
  
#if  PARTIALREFLECTION
   if ( layer  ==   1   &&  r < 1.0 )   {   /* partially transmitted. */
     Photon_Ptr -> uz  =   - uz1 ;    /* transmitted photon. */
     RecordR ( r ,   In_Ptr ,   Photon_Ptr ,   Out_Ptr );
     Photon_Ptr -> uz  =   - uz ;     /* reflected photon. */
   }      
   else   if ( RandomNum ()   >  r )   { /* transmitted to layer-1. */
     Photon_Ptr -> layer -- ;
     Photon_Ptr -> ux  *=  ni / nt ;
     Photon_Ptr -> uy  *=  ni / nt ;
     Photon_Ptr -> uz  =   - uz1 ;
   }
   else                        /* reflected. */
     Photon_Ptr -> uz  =   - uz ;
#else
   if ( RandomNum ()   >  r )   {       /* transmitted to layer-1. */
     if ( layer == 1 )    {
       Photon_Ptr -> uz  =   - uz1 ;
       RecordR ( 0.0 ,   In_Ptr ,   Photon_Ptr ,   Out_Ptr );
       Photon_Ptr -> dead  =   1 ;
     }
     else   {
       Photon_Ptr -> layer -- ;
       Photon_Ptr -> ux  *=  ni / nt ;
       Photon_Ptr -> uy  *=  ni / nt ;
       Photon_Ptr -> uz  =   - uz1 ;
     }
   }
   else                        /* reflected. */
     Photon_Ptr -> uz  =   - uz ;
#endif
}

/***********************************************************
 *  Decide whether the photon will be transmitted  or be 
 *  reflected on the bottom boundary (uz>0) of the current 
 *  layer.
 *
 *  If the photon is transmitted, move the photon to 
 *  "layer+1". If "layer" is the last layer, record the 
 *  transmitted weight as transmittance. See comments for 
 *  CrossUpOrNot.
 *
 *  Update the photon parmameters.
 ****/
void   CrossDnOrNot ( InputStruct    *      In_Ptr ,  
                   PhotonStruct   *      Photon_Ptr ,
                   OutStruct   *         Out_Ptr )
{
   double  uz  =   Photon_Ptr -> uz ;   /* z directional cosine. */
   double  uz1 ;     /* cosines of transmission alpha. */
   double  r = 0.0 ;   /* reflectance */
   short   layer  =   Photon_Ptr -> layer ;
   double  ni  =   In_Ptr -> layerspecs [ layer ]. n ;
   double  nt  =   In_Ptr -> layerspecs [ layer + 1 ]. n ;
  
   /* Get r. */
   if (  uz  <=   In_Ptr -> layerspecs [ layer ]. cos_crit1 )  
    r = 1.0 ;        /* total internal reflection. */
   else  r  =   RFresnel ( ni ,  nt ,  uz ,   & uz1 );
  
#if  PARTIALREFLECTION   
   if ( layer  ==   In_Ptr -> num_layers  &&  r < 1.0 )   {
     Photon_Ptr -> uz  =  uz1 ;
     RecordT ( r ,   In_Ptr ,   Photon_Ptr ,   Out_Ptr );
     Photon_Ptr -> uz  =   - uz ;
   }
   else   if ( RandomNum ()   >  r )   { /* transmitted to layer+1. */
     Photon_Ptr -> layer ++ ;
     Photon_Ptr -> ux  *=  ni / nt ;
     Photon_Ptr -> uy  *=  ni / nt ;
     Photon_Ptr -> uz  =  uz1 ;
   }
   else                        /* reflected. */
     Photon_Ptr -> uz  =   - uz ;
#else
   if ( RandomNum ()   >  r )   {       /* transmitted to layer+1. */
     if ( layer  ==   In_Ptr -> num_layers )   {
       Photon_Ptr -> uz  =  uz1 ;
       RecordT ( 0.0 ,   In_Ptr ,   Photon_Ptr ,   Out_Ptr );
       Photon_Ptr -> dead  =   1 ;
     }
     else   {
       Photon_Ptr -> layer ++ ;
       Photon_Ptr -> ux  *=  ni / nt ;
       Photon_Ptr -> uy  *=  ni / nt ;
       Photon_Ptr -> uz  =  uz1 ;
     }
   }
   else                        /* reflected. */
     Photon_Ptr -> uz  =   - uz ;
#endif
}

/***********************************************************
 ****/
void   CrossOrNot ( InputStruct    *    In_Ptr ,  
                 PhotonStruct   *    Photon_Ptr ,
                 OutStruct      *    Out_Ptr )
{
   if ( Photon_Ptr -> uz  <   0.0 )
     CrossUpOrNot ( In_Ptr ,   Photon_Ptr ,   Out_Ptr );
   else
     CrossDnOrNot ( In_Ptr ,   Photon_Ptr ,   Out_Ptr );
}

/***********************************************************
 *  Move the photon packet in glass layer.
 *  Horizontal photons are killed because they will
 *  never interact with tissue again.
 ****/
void   HopInGlass ( InputStruct    *   In_Ptr ,
                 PhotonStruct   *   Photon_Ptr ,
                 OutStruct      *   Out_Ptr )
{
   double  dl ;       /* step size. 1/cm */
  
   if ( Photon_Ptr -> uz  ==   0.0 )   {  
     /* horizontal photon in glass is killed. */
     Photon_Ptr -> dead  =   1 ;
   }
   else   {
     StepSizeInGlass ( Photon_Ptr ,   In_Ptr );
     Hop ( Photon_Ptr );
     CrossOrNot ( In_Ptr ,   Photon_Ptr ,   Out_Ptr );
   }
}

/***********************************************************
 *  Set a step size, move the photon, drop some weight, 
 *  choose a new photon direction for propagation.  
 *
 *  When a step size is long enough for the photon to 
 *  hit an interface, this step is divided into two steps. 
 *  First, move the photon to the boundary free of 
 *  absorption or scattering, then decide whether the 
 *  photon is reflected or transmitted.
 *  Then move the photon in the current or transmission 
 *  medium with the unfinished stepsize to interaction 
 *  site.  If the unfinished stepsize is still too long, 
 *  repeat the above process.  
 ****/
void   HopDropSpinInTissue ( InputStruct    *    In_Ptr ,
                          PhotonStruct   *    Photon_Ptr ,
                          OutStruct      *    Out_Ptr )
{
   StepSizeInTissue ( Photon_Ptr ,   In_Ptr );
  
   if ( HitBoundary ( Photon_Ptr ,   In_Ptr ))   {
     Hop ( Photon_Ptr );      /* move to boundary plane. */
     CrossOrNot ( In_Ptr ,   Photon_Ptr ,   Out_Ptr );
   }
   else   {
     Hop ( Photon_Ptr );
     Drop ( In_Ptr ,   Photon_Ptr ,   Out_Ptr );
     Spin ( In_Ptr -> layerspecs [ Photon_Ptr -> layer ]. g ,  
         Photon_Ptr );
   }
}

/***********************************************************
 ****/
void   HopDropSpin ( InputStruct    *    In_Ptr ,
                  PhotonStruct   *    Photon_Ptr ,
                  OutStruct      *    Out_Ptr )
{
   short  layer  =   Photon_Ptr -> layer ;

   if (( In_Ptr -> layerspecs [ layer ]. mua  ==   0.0 )  
   &&   ( In_Ptr -> layerspecs [ layer ]. mus  ==   0.0 ))  
     /* glass layer. */
     HopInGlass ( In_Ptr ,   Photon_Ptr ,   Out_Ptr );
   else
     HopDropSpinInTissue ( In_Ptr ,   Photon_Ptr ,   Out_Ptr );
  
   if (   Photon_Ptr -> <   In_Ptr -> Wth   &&   ! Photon_Ptr -> dead )  
     Roulette ( Photon_Ptr );
}

__MACOSX/FTP-Uploads/ch03MonteCarlo/._MCMLGO.C

FTP-Uploads/ch03MonteCarlo/MCMLIO.C

FTP-Uploads/ch03MonteCarlo/MCMLIO.C

/***********************************************************
 *  Copyright Univ. of Texas M.D. Anderson Cancer Center
 *  1992.
 *
 *  Input/output of data.
 ****/

#include   "mcml.h"

/***********************************************************
 *  Structure used to check against duplicated file names.
 ****/
struct   NameList   {
   char  name [ STRLEN ];
   struct   NameList   *  next ;
};

typedef   struct   NameList   NameNode ;
typedef   NameNode   *   NameLink ;


/***********************************************************
 *  Center a string according to the column width.
 ****/
char   *   CenterStr ( short    Wid ,
                  char   *   InStr ,
                  char   *   OutStr )
{
  size_t nspaces ;     /* number of spaces to be filled */
                     /* before InStr. */
  
  nspaces  =   ( Wid   -  strlen ( InStr )) / 2 ;
   if ( nspaces < 0 )  nspaces  =   0 ;
  
  strcpy ( OutStr ,   "" );
   while ( nspaces -- )   strcat ( OutStr ,   " " );
  
  strcat ( OutStr ,   InStr );
  
   return ( OutStr );
}

/**************************************************************************
 *  Center a string according to the column width.
 ****/
#define  COLWIDTH  80
void
CtrPuts ( char   * InStr )
{
   short        nspaces ;        /* number of spaces to be left-filled. */
   char         outstr [ STRLEN ];

  nspaces  =   ( COLWIDTH  -  strlen ( InStr ))   /   2 ;
   if   ( nspaces  <   0 )
    nspaces  =   0 ;

  strcpy ( outstr ,   "" );
   while   ( nspaces -- )
    strcat ( outstr ,   " " );

  strcat ( outstr ,   InStr );

  puts ( outstr );
}

/***********************************************************
 *  Print some messages before starting simulation.  
 *  e.g. author, address, program version, year.
 ****/
#define  COLWIDTH  80
void   ShowVersion ( char   * version )
{
   char  str [ STRLEN ];
  
   CtrPuts ( " " );
   CtrPuts (
   "Monte Carlo Simulation of Light Transport in Multi-layered Turbid Media" );
   CtrPuts ( version );
   CtrPuts ( " " );

   CtrPuts ( "Lihong Wang, Ph.D." );
   CtrPuts ( "Biomedical Engineering Program, Texas A&M University, 3120 TAMU" );
   CtrPuts ( "College Station, Texas 77843-3120, USA" );
   CtrPuts ( "Email: [email protected]" );

   CtrPuts ( " " );
   CtrPuts ( "Steven L. Jacques, Ph.D." );
   CtrPuts ( "Oregon Medical Laser Center" );
   CtrPuts ( "Providence/St. Vincent Hospital" );
   CtrPuts ( "9205 SW Barnes Rd., Portland, OR 97225, USA" );
   CtrPuts ( "Email: [email protected]" );

   CtrPuts ( " " );
   CtrPuts ( "The program can be obtained from http://oilab.tamu.edu" );
   CtrPuts ( "Please cite the following article in your publications:" );
  printf ( "\tL.-H. Wang, S. L. Jacques, and L.-Q. Zheng, MCML - Monte \n" );
  printf ( "\tCarlo modeling of photon transport in multi-layered\n" );
  printf ( "\ttissues, Computer Methods and Programs in Biomedicine, 47,\n" );
  printf ( "\t131-146 (1995)\n" );

  puts ( "\n" );    
}
#undef  COLWIDTH

/***********************************************************
 *  Get a filename and open it for reading, retry until 
 *  the file can be opened.  '.' terminates the program.
 *      
 *  If Fname != NULL, try Fname first.
 ****/
FILE  * GetFile ( char   * Fname )
{
  FILE  *  file = NULL ;
   Boolean  firsttime = 1 ;
  
   do   {
     if ( firsttime  &&   Fname [ 0 ] != '\0' )   {  
       /* use the filename from command line */
      firsttime  =   0 ;
     }
     else   {
      printf ( "Input filename(or . to exit):" );
      scanf ( "%s" ,   Fname );
      firsttime  =   0 ;
     }

     if ( strlen ( Fname )   ==   1   &&   Fname [ 0 ]   ==   '.' )  
      exit ( 1 );            /* exit if no filename entered. */
    
    file  =  fopen ( Fname ,   "r" );
   }    while ( file  ==  NULL );
  
   return ( file );
}

/***********************************************************
 *  Kill the ith char (counting from 0), push the following 
 *  chars forward by one.
 ****/
void   KillChar ( size_t i ,   char   *   Str )
{
  size_t sl  =  strlen ( Str );
  
   for (; i < sl ; i ++ )   Str [ i ]   =   Str [ i + 1 ];
}

/***********************************************************
 *  Eliminate the chars in a string which are not printing 
 *  chars or spaces.
 *
 *  Spaces include ' ', '\f', '\t' etc.
 *
 *  Return 1 if no nonprinting chars found, otherwise 
 *  return 0.
 ****/
Boolean   CheckChar ( char   *   Str )
{
   Boolean  found  =   0 ;      /* found bad char. */
  size_t sl  =  strlen ( Str );
  size_t i = 0 ;
  
   while ( i < sl )  
     if   ( Str [ i ] < 0   ||   Str [ i ] > 255 )
      nrerror ( "Non-ASCII file\n" );
     else   if ( isprint ( Str [ i ])   ||  isspace ( Str [ i ]))  
      i ++ ;
     else   {
      found  =   1 ;
       KillChar ( i ,   Str );
      sl -- ;
     }
  
   return ( found );     
}

/***********************************************************
 *  Return 1 if this line is a comment line in which the 
 *  first non-space character is "#".
 *
 *  Also return 1 if this line is space line.
 ****/
Boolean   CommentLine ( char   * Buf )
{
  size_t spn ,  cspn ;
  
  spn  =  strspn ( Buf ,   " \t" );  
   /* length spanned by space or tab chars. */

  cspn  =  strcspn ( Buf ,   "#\n" );
   /* length before the 1st # or return. */

   if ( spn  ==  cspn )     /* comment line or space line. */
     return ( 1 );
   else                /* the line has data. */      
     return ( 0 );       
}

/***********************************************************
 *  Skip space or comment lines and return a data line only.
 ****/
char   *   FindDataLine ( FILE  * File_Ptr )
{
   static   char  buf [ STRLEN ];    /* LW 1/11/2000. Added static. */
  
  buf [ 0 ]   =   '\0' ;
   do   {    /* skip space or comment lines. */
     if ( fgets ( buf ,   255 ,   File_Ptr )   ==  NULL )    {
      printf ( "Incomplete data\n" );
      buf [ 0 ] = '\0' ;
       break ;
     }
     else
       CheckChar ( buf );
   }   while ( CommentLine ( buf ));
  
   return ( buf );
}

/***********************************************************
 *  Skip file version, then read number of runs.
 ****/
short   ReadNumRuns ( FILE *   File_Ptr )
{
   char  buf [ STRLEN ];
   short  n = 0 ;
  
   FindDataLine ( File_Ptr );   /* skip file version. */

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
   if ( buf [ 0 ] == '\0' )  nrerror ( "Reading number of runs\n" );
  sscanf ( buf ,   "%hd" , & n );             
   return ( n );
}

  
/***********************************************************
 *  Read the file name and the file format.
 *
 *  The file format can be either A for ASCII or B for
 *  binary.
 ****/
void   ReadFnameFormat ( FILE  * File_Ptr ,   InputStruct   * In_Ptr )
{
   char  buf [ STRLEN ];

   /** read in file name and format. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
   if ( buf [ 0 ] == '\0' )  
    nrerror ( "Reading file name and format.\n" );
  sscanf ( buf ,   "%s %c" ,  
     In_Ptr -> out_fname ,   & ( In_Ptr -> out_fformat )   );
   if ( toupper ( In_Ptr -> out_fformat )   !=   'B' )  
     In_Ptr -> out_fformat  =   'A' ;
}


/***********************************************************
 *  Read the number of photons.
 ****/
void   ReadNumPhotons ( FILE  * File_Ptr ,   InputStruct   * In_Ptr )
{
   char  buf [ STRLEN ];

   /** read in number of photons. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
   if ( buf [ 0 ] == '\0' )  
    nrerror ( "Reading number of photons.\n" );
  sscanf ( buf ,   "%ld" ,   & In_Ptr -> num_photons );
   if ( In_Ptr -> num_photons <= 0 )  
    nrerror ( "Nonpositive number of photons.\n" );
}


/***********************************************************
 *  Read the members dz and dr.
 ****/
void   ReadDzDr ( FILE  * File_Ptr ,   InputStruct   * In_Ptr )
{
   char  buf [ STRLEN ];

   /** read in dz, dr. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
   if ( buf [ 0 ] == '\0' )  nrerror ( "Reading dz, dr.\n" );
  sscanf ( buf ,   "%lf%lf" ,   & In_Ptr -> dz ,   & In_Ptr -> dr );
   if ( In_Ptr -> dz <= 0 )  nrerror ( "Nonpositive dz.\n" );
   if ( In_Ptr -> dr <= 0 )  nrerror ( "Nonpositive dr.\n" );
}


/***********************************************************
 *  Read the members nz, nr, na.
 ****/
void   ReadNzNrNa ( FILE  * File_Ptr ,   InputStruct   * In_Ptr )
{
   char  buf [ STRLEN ];

   /** read in number of dz, dr, da. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
   if ( buf [ 0 ] == '\0' )  
    nrerror ( "Reading number of dz, dr, da's.\n" );
  sscanf ( buf ,   "%hd%hd%hd" ,  
     & In_Ptr -> nz ,   & In_Ptr -> nr ,   & In_Ptr -> na );
   if ( In_Ptr -> nz <= 0 )  
    nrerror ( "Nonpositive number of dz's.\n" );
   if ( In_Ptr -> nr <= 0 )  
    nrerror ( "Nonpositive number of dr's.\n" );
   if ( In_Ptr -> na <= 0 )  
    nrerror ( "Nonpositive number of da's.\n" );
   In_Ptr -> da  =   0.5 * PI / In_Ptr -> na ;
}


/***********************************************************
 *  Read the number of layers.
 ****/
void   ReadNumLayers ( FILE  * File_Ptr ,   InputStruct   * In_Ptr )
{
   char  buf [ STRLEN ];

   /** read in number of layers. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
   if ( buf [ 0 ] == '\0' )  
    nrerror ( "Reading number of layers.\n" );
  sscanf ( buf ,   "%hd" ,   & In_Ptr -> num_layers );
   if ( In_Ptr -> num_layers <= 0 )  
    nrerror ( "Nonpositive number of layers.\n" );
}


/***********************************************************
 *  Read the refractive index n of the ambient.
 ****/
void   ReadAmbient ( FILE  * File_Ptr ,  
                  LayerStruct   *   Layer_Ptr ,
                  char   * side )
{
   char  buf [ STRLEN ],  msg [ STRLEN ];
   double  n ;

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
   if ( buf [ 0 ] == '\0' )   {
    sprintf ( msg ,   "Rading n of %s ambient.\n" ,  side );
    nrerror ( msg );
   }

  sscanf ( buf ,   "%lf" ,   & );
   if ( n <= 0 )  nrerror ( "Wrong n.\n" );
   Layer_Ptr -> =  n ;
}


/***********************************************************
 *  Read the parameters of one layer.
 *
 *  Return 1 if error detected.
 *  Return 0 otherwise.
 *
 *  *Z_Ptr is the z coordinate of the current layer, which
 *  is used to convert thickness of layer to z coordinates
 *  of the two boundaries of the layer.
 ****/
Boolean   ReadOneLayer ( FILE  * File_Ptr ,  
                      LayerStruct   *   Layer_Ptr ,
                      double   * Z_Ptr )
{
   char  buf [ STRLEN ],  msg [ STRLEN ];
   double  d ,  n ,  mua ,  mus ,  g ;   /* d is thickness. */

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
   if ( buf [ 0 ] == '\0' )   return ( 1 );     /* error. */

  sscanf ( buf ,   "%lf%lf%lf%lf%lf" ,   & n ,   & mua ,   & mus ,   & g ,   & d );
   if ( d < 0   ||  n <= 0   ||  mua < 0   ||  mus < 0   ||  g < 0   ||  g > 1 )  
     return ( 1 );            /* error. */
    
   Layer_Ptr -> n   =  n ;
   Layer_Ptr -> mua  =  mua ;  
   Layer_Ptr -> mus  =  mus ;  
   Layer_Ptr -> g    =  g ;
   Layer_Ptr -> z0  =   * Z_Ptr ;
   * Z_Ptr   +=  d ;
   Layer_Ptr -> z1  =   * Z_Ptr ;

   return ( 0 );
}

/***********************************************************
 *  Read the parameters of one layer at a time.
 ****/
void   ReadLayerSpecs ( FILE  * File_Ptr ,  
                     short   Num_Layers ,
                     LayerStruct   **   Layerspecs_PP )
{
   char  msg [ STRLEN ];
   short  i = 0 ;
   double  z  =   0.0 ;     /* z coordinate of the current layer. */
  
   /* Allocate an array for the layer parameters. */
   /* layer 0 and layer Num_Layers + 1 are for ambient. */
   * Layerspecs_PP   =   ( LayerStruct   * )
    malloc (( unsigned )   ( Num_Layers + 2 ) * sizeof ( LayerStruct ));
   if   ( ! ( * Layerspecs_PP ))  
    nrerror ( "allocation failure in ReadLayerSpecs()" );
  
   ReadAmbient ( File_Ptr ,   & (( * Layerspecs_PP )[ i ]),   "top" );  
   for ( i = 1 ;  i <= Num_Layers ;  i ++ )   
     if ( ReadOneLayer ( File_Ptr ,   & (( * Layerspecs_PP )[ i ]),   & z ))   {
      sprintf ( msg ,   "Error reading %hd of %hd layers\n" ,  
              i ,   Num_Layers );
      nrerror ( msg );
     }
   ReadAmbient ( File_Ptr ,   & (( * Layerspecs_PP )[ i ]),   "bottom" );  
}

/***********************************************************
 *  Compute the critical angles for total internal
 *  reflection according to the relative refractive index
 *  of the layer.
 *  All layers are processed.
 ****/
void   CriticalAngle (   short   Num_Layers ,  
                     LayerStruct   **   Layerspecs_PP )
{
   short  i = 0 ;
   double  n1 ,  n2 ;
  
   for ( i = 1 ;  i <= Num_Layers ;  i ++ )    {
    n1  =   ( * Layerspecs_PP )[ i ]. n ;
    n2  =   ( * Layerspecs_PP )[ i - 1 ]. n ;
     ( * Layerspecs_PP )[ i ]. cos_crit0  =  n1 > n2  ?  
        sqrt ( 1.0   -  n2 * n2 / ( n1 * n1 ))   :   0.0 ;
    
    n2  =   ( * Layerspecs_PP )[ i + 1 ]. n ;
     ( * Layerspecs_PP )[ i ]. cos_crit1  =  n1 > n2  ?  
        sqrt ( 1.0   -  n2 * n2 / ( n1 * n1 ))   :   0.0 ;
   }
}

/***********************************************************
 *  Read in the input parameters for one run.
 ****/
void   ReadParm ( FILE *   File_Ptr ,   InputStruct   *   In_Ptr )
{
   char  buf [ STRLEN ];
  
   In_Ptr -> Wth   =  WEIGHT ;
  
   ReadFnameFormat ( File_Ptr ,   In_Ptr );
   ReadNumPhotons ( File_Ptr ,   In_Ptr );
   ReadDzDr ( File_Ptr ,   In_Ptr );
   ReadNzNrNa ( File_Ptr ,   In_Ptr );
   ReadNumLayers ( File_Ptr ,   In_Ptr );

   ReadLayerSpecs ( File_Ptr ,   In_Ptr -> num_layers ,  
                 & In_Ptr -> layerspecs );
   CriticalAngle ( In_Ptr -> num_layers ,   & In_Ptr -> layerspecs );
}

/***********************************************************
 *  Return 1, if the name in the name list.
 *  Return 0, otherwise.
 ****/
Boolean   NameInList ( char   * Name ,   NameLink   List )
{
   while   ( List   !=  NULL )   {
     if ( strcmp ( Name ,   List -> name )   ==   0 )  
       return ( 1 );
     List   =   List -> next ;
   };
   return ( 0 );
}

/***********************************************************
 *  Add the name to the name list.
 ****/
void   AddNameToList ( char   * Name ,   NameLink   *   List_Ptr )
{
   NameLink  list  =   * List_Ptr ;

   if ( list  ==  NULL )   {      /* first node. */
     * List_Ptr   =  list  =   ( NameLink ) malloc ( sizeof ( NameNode ));
    strcpy ( list -> name ,   Name );
    list -> next  =  NULL ;
   }
   else   {                  /* subsequent nodes. */
     /* Move to the last node. */
     while ( list -> next  !=  NULL )
      list  =  list -> next ;

     /* Append a node to the list. */
    list -> next  =   ( NameLink ) malloc ( sizeof ( NameNode ));
    list  =  list -> next ;
    strcpy ( list -> name ,   Name );
    list -> next  =  NULL ;
   }
}

/***********************************************************
 *  Check against duplicated file names.
 *
 *  A linked list is set up to store the file names used
 *  in this input data file.
 ****/
Boolean   FnameTaken ( char   * fname ,   NameLink   *   List_Ptr )
{
   if ( NameInList ( fname ,   * List_Ptr ))
     return ( 1 );
   else   {
     AddNameToList ( fname ,   List_Ptr );
     return ( 0 );
   }
}

/***********************************************************
 *  Free each node in the file name list.
 ****/
void   FreeFnameList ( NameLink   List )
{
   NameLink  next ;

   while ( List   !=  NULL )   {
    next  =   List -> next ;
    free ( List );
     List   =  next ;
   }
}

/***********************************************************
 *  Check the input parameters for each run.
 ****/
void   CheckParm ( FILE *   File_Ptr ,   InputStruct   *   In_Ptr )
{
   short  i_run ;
   short  num_runs ;     /* number of independent runs. */
   NameLink  head  =  NULL ;
   Boolean  name_taken ; /* output files share the same */
                     /* file name.*/
   char  msg [ STRLEN ];
  
  num_runs  =   ReadNumRuns ( File_Ptr );
   for ( i_run = 1 ;  i_run <= num_runs ;  i_run ++ )    {
    printf ( "Checking input data for run %hd\n" ,  i_run );
     ReadParm ( File_Ptr ,   In_Ptr );

    name_taken  =   FnameTaken ( In_Ptr -> out_fname ,   & head );
     if ( name_taken )  
      sprintf ( msg ,   "file name %s duplicated.\n" ,  
                     In_Ptr -> out_fname );

    free ( In_Ptr -> layerspecs );
     if ( name_taken )  nrerror ( msg );
   }
   FreeFnameList ( head );
  rewind ( File_Ptr );
}


/***********************************************************
 *  Allocate the arrays in OutStruct for one run, and 
 *  array elements are automatically initialized to zeros.
 ****/
void   InitOutputData ( InputStruct   In_Parm ,  
                     OutStruct   *   Out_Ptr )
{
   short  nz  =   In_Parm . nz ;
   short  nr  =   In_Parm . nr ;
   short  na  =   In_Parm . na ;
   short  nl  =   In_Parm . num_layers ;     
   /* remember to use nl+2 because of 2 for ambient. */
  
   if ( nz <= 0   ||  nr <= 0   ||  na <= 0   ||  nl <= 0 )  
    nrerror ( "Wrong grid parameters.\n" );
  
   /* Init pure numbers. */
   Out_Ptr -> Rsp   =   0.0 ;
   Out_Ptr -> Rd    =   0.0 ;
   Out_Ptr -> A    =   0.0 ;
   Out_Ptr -> Tt    =   0.0 ;
  
   /* Allocate the arrays and the matrices. */
   Out_Ptr -> Rd_ra   =   AllocMatrix ( 0 , nr - 1 , 0 , na - 1 );
   Out_Ptr -> Rd_r    =   AllocVector ( 0 , nr - 1 );
   Out_Ptr -> Rd_a    =   AllocVector ( 0 , na - 1 );
  
   Out_Ptr -> A_rz    =   AllocMatrix ( 0 , nr - 1 , 0 , nz - 1 );
   Out_Ptr -> A_z     =   AllocVector ( 0 , nz - 1 );
   Out_Ptr -> A_l     =   AllocVector ( 0 , nl + 1 );
  
   Out_Ptr -> Tt_ra   =   AllocMatrix ( 0 , nr - 1 , 0 , na - 1 );
   Out_Ptr -> Tt_r    =   AllocVector ( 0 , nr - 1 );
   Out_Ptr -> Tt_a    =   AllocVector ( 0 , na - 1 );
}

/***********************************************************
 *  Undo what InitOutputData did.
 *  i.e. free the data allocations.
 ****/
void   FreeData ( InputStruct   In_Parm ,   OutStruct   *   Out_Ptr )
{
   short  nz  =   In_Parm . nz ;
   short  nr  =   In_Parm . nr ;
   short  na  =   In_Parm . na ;
   short  nl  =   In_Parm . num_layers ;     
   /* remember to use nl+2 because of 2 for ambient. */
  
  free ( In_Parm . layerspecs );
  
   FreeMatrix ( Out_Ptr -> Rd_ra ,   0 , nr - 1 , 0 , na - 1 );
   FreeVector ( Out_Ptr -> Rd_r ,   0 , nr - 1 );
   FreeVector ( Out_Ptr -> Rd_a ,   0 , na - 1 );
  
   FreeMatrix ( Out_Ptr -> A_rz ,   0 ,  nr - 1 ,   0 , nz - 1 );
   FreeVector ( Out_Ptr -> A_z ,   0 ,  nz - 1 );
   FreeVector ( Out_Ptr -> A_l ,   0 , nl + 1 );
  
   FreeMatrix ( Out_Ptr -> Tt_ra ,   0 , nr - 1 , 0 , na - 1 );
   FreeVector ( Out_Ptr -> Tt_r ,   0 , nr - 1 );
   FreeVector ( Out_Ptr -> Tt_a ,   0 , na - 1 );
}

/***********************************************************
 *  Get 1D array elements by summing the 2D array elements.
 ****/
void   Sum2DRd ( InputStruct   In_Parm ,   OutStruct   *   Out_Ptr )
{
   short  nr  =   In_Parm . nr ;
   short  na  =   In_Parm . na ;
   short  ir , ia ;
   double  sum ;
  
   for ( ir = 0 ;  ir < nr ;  ir ++ )    {
    sum  =   0.0 ;
     for ( ia = 0 ;  ia < na ;  ia ++ )  sum  +=   Out_Ptr -> Rd_ra [ ir ][ ia ];
     Out_Ptr -> Rd_r [ ir ]   =  sum ;
   }
  
   for ( ia = 0 ;  ia < na ;  ia ++ )    {
    sum  =   0.0 ;
     for ( ir = 0 ;  ir < nr ;  ir ++ )  sum  +=   Out_Ptr -> Rd_ra [ ir ][ ia ];
     Out_Ptr -> Rd_a [ ia ]   =  sum ;
   }
  
  sum  =   0.0 ;
   for ( ir = 0 ;  ir < nr ;  ir ++ )  sum  +=   Out_Ptr -> Rd_r [ ir ];
   Out_Ptr -> Rd   =  sum ;
}

/***********************************************************
 *  Return the index to the layer according to the index
 *  to the grid line system in z direction (Iz).
 *
 *  Use the center of box.
 ****/
short   IzToLayer ( short   Iz ,   InputStruct   In_Parm )
{
   short  i = 1 ;      /* index to layer. */
   short  num_layers  =   In_Parm . num_layers ;
   double  dz  =   In_Parm . dz ;
  
   while (   ( Iz + 0.5 ) * dz  >=   In_Parm . layerspecs [ i ]. z1 
     &&  i < num_layers )  i ++ ;
  
   return ( i );
}

/***********************************************************
 *  Get 1D array elements by summing the 2D array elements.
 ****/
void   Sum2DA ( InputStruct   In_Parm ,   OutStruct   *   Out_Ptr )
{
   short  nz  =   In_Parm . nz ;
   short  nr  =   In_Parm . nr ;
   short  iz , ir ;
   double  sum ;
  
   for ( iz = 0 ;  iz < nz ;  iz ++ )    {
    sum  =   0.0 ;
     for ( ir = 0 ;  ir < nr ;  ir ++ )  sum  +=   Out_Ptr -> A_rz [ ir ][ iz ];
     Out_Ptr -> A_z [ iz ]   =  sum ;
   }
  
  sum  =   0.0 ;
   for ( iz = 0 ;  iz < nz ;  iz ++ )   {
    sum  +=   Out_Ptr -> A_z [ iz ];
     Out_Ptr -> A_l [ IzToLayer ( iz ,   In_Parm )]  
       +=   Out_Ptr -> A_z [ iz ];
   }
   Out_Ptr -> =  sum ;
}

/***********************************************************
 *  Get 1D array elements by summing the 2D array elements.
 ****/
void   Sum2DTt ( InputStruct   In_Parm ,   OutStruct   *   Out_Ptr )
{
   short  nr  =   In_Parm . nr ;
   short  na  =   In_Parm . na ;
   short  ir , ia ;
   double  sum ;
  
   for ( ir = 0 ;  ir < nr ;  ir ++ )    {
    sum  =   0.0 ;
     for ( ia = 0 ;  ia < na ;  ia ++ )  sum  +=   Out_Ptr -> Tt_ra [ ir ][ ia ];
     Out_Ptr -> Tt_r [ ir ]   =  sum ;
   }
  
   for ( ia = 0 ;  ia < na ;  ia ++ )    {
    sum  =   0.0 ;
     for ( ir = 0 ;  ir < nr ;  ir ++ )  sum  +=   Out_Ptr -> Tt_ra [ ir ][ ia ];
     Out_Ptr -> Tt_a [ ia ]   =  sum ;
   }
  
  sum  =   0.0 ;
   for ( ir = 0 ;  ir < nr ;  ir ++ )  sum  +=   Out_Ptr -> Tt_r [ ir ];
   Out_Ptr -> Tt   =  sum ;
}

/***********************************************************
 *  Scale Rd and Tt properly.
 *
 *  "a" stands for angle alpha.
 ****
 *  Scale Rd(r,a) and Tt(r,a) by
 *      (area perpendicular to photon direction)
 *      x(solid angle)x(No. of photons).
 *  or
 *      [2*PI*r*dr*cos(a)]x[2*PI*sin(a)*da]x[No. of photons]
 *  or
 *      [2*PI*PI*dr*da*r*sin(2a)]x[No. of photons]
 ****
 *  Scale Rd(r) and Tt(r) by
 *      (area on the surface)x(No. of photons).
 ****
 *  Scale Rd(a) and Tt(a) by
 *      (solid angle)x(No. of photons).
 ****/
void   ScaleRdTt ( InputStruct   In_Parm ,   OutStruct   *   Out_Ptr )
{
   short  nr  =   In_Parm . nr ;
   short  na  =   In_Parm . na ;
   double  dr  =   In_Parm . dr ;
   double  da  =   In_Parm . da ;
   short  ir , ia ;
   double  scale1 ,  scale2 ;
  
  scale1  =   4.0 * PI * PI * dr * sin ( da / 2 ) * dr * In_Parm . num_photons ;
     /* The factor (ir+0.5)*sin(2a) to be added. */

   for ( ir = 0 ;  ir < nr ;  ir ++ )   
     for ( ia = 0 ;  ia < na ;  ia ++ )   {
      scale2  =   1.0 / (( ir + 0.5 ) * sin ( 2.0 * ( ia + 0.5 ) * da ) * scale1 );
       Out_Ptr -> Rd_ra [ ir ][ ia ]   *=  scale2 ;
       Out_Ptr -> Tt_ra [ ir ][ ia ]   *=  scale2 ;
     }
  
  scale1  =   2.0 * PI * dr * dr * In_Parm . num_photons ;   
     /* area is 2*PI*[(ir+0.5)*dr]*dr.*/  
     /* ir+0.5 to be added. */

   for ( ir = 0 ;  ir < nr ;  ir ++ )   {
    scale2  =   1.0 / (( ir + 0.5 ) * scale1 );
     Out_Ptr -> Rd_r [ ir ]   *=  scale2 ;
     Out_Ptr -> Tt_r [ ir ]   *=  scale2 ;
   }
  
  scale1   =   2.0 * PI * da * In_Parm . num_photons ;
     /* solid angle is 2*PI*sin(a)*da. sin(a) to be added. */

   for ( ia = 0 ;  ia < na ;  ia ++ )   {
    scale2  =   1.0 / ( sin (( ia + 0.5 ) * da ) * scale1 );
     Out_Ptr -> Rd_a [ ia ]   *=  scale2 ;
     Out_Ptr -> Tt_a [ ia ]   *=  scale2 ;
   }
  
  scale2  =   1.0 / ( double ) In_Parm . num_photons ;
   Out_Ptr -> Rd   *=  scale2 ;
   Out_Ptr -> Tt   *=  scale2 ;
}

/***********************************************************
 *  Scale absorption arrays properly.
 ****/
void   ScaleA ( InputStruct   In_Parm ,   OutStruct   *   Out_Ptr )
{
   short  nz  =   In_Parm . nz ;
   short  nr  =   In_Parm . nr ;
   double  dz  =   In_Parm . dz ;
   double  dr  =   In_Parm . dr ;
   short  nl  =   In_Parm . num_layers ;
   short  iz , ir ;
   short  il ;
   double  scale1 ;
  
   /* Scale A_rz. */
  scale1  =   2.0 * PI * dr * dr * dz * In_Parm . num_photons ;  
     /* volume is 2*pi*(ir+0.5)*dr*dr*dz.*/  
     /* ir+0.5 to be added. */
   for ( iz = 0 ;  iz < nz ;  iz ++ )  
     for ( ir = 0 ;  ir < nr ;  ir ++ )  
       Out_Ptr -> A_rz [ ir ][ iz ]   /=   ( ir + 0.5 ) * scale1 ;
  
   /* Scale A_z. */
  scale1  =   1.0 / ( dz * In_Parm . num_photons );
   for ( iz = 0 ;  iz < nz ;  iz ++ )  
     Out_Ptr -> A_z [ iz ]   *=  scale1 ;
  
   /* Scale A_l. Avoid int/int. */
  scale1  =   1.0 / ( double ) In_Parm . num_photons ;  
   for ( il = 0 ;  il <= nl + 1 ;  il ++ )
     Out_Ptr -> A_l [ il ]   *=  scale1 ;
  
   Out_Ptr -> *= scale1 ;
}

/***********************************************************
 *  Sum and scale results of current run.
 ****/
void   SumScaleResult ( InputStruct   In_Parm ,  
                     OutStruct   *   Out_Ptr )
{
   /* Get 1D & 0D results. */
   Sum2DRd ( In_Parm ,   Out_Ptr );
   Sum2DA ( In_Parm ,    Out_Ptr );
   Sum2DTt ( In_Parm ,   Out_Ptr );
  
   ScaleRdTt ( In_Parm ,   Out_Ptr );
   ScaleA ( In_Parm ,   Out_Ptr );
}

/***********************************************************
 *  Write the version number as the first string in the 
 *  file.
 *  Use chars only so that they can be read as either 
 *  ASCII or binary.
 ****/
void   WriteVersion ( FILE  * file ,   char   * Version )
{
  fprintf ( file ,  
     "%s \t# Version number of the file format.\n\n" ,  
     Version );
  fprintf ( file ,   "####\n# Data categories include: \n" );
  fprintf ( file ,   "# InParm, RAT, \n" );
  fprintf ( file ,   "# A_l, A_z, Rd_r, Rd_a, Tt_r, Tt_a, \n" );
  fprintf ( file ,   "# A_rz, Rd_ra, Tt_ra \n####\n\n" );
}

/***********************************************************
 *  Write the input parameters to the file.
 ****/
void   WriteInParm ( FILE  * file ,   InputStruct   In_Parm )
{
   short  i ;
  
  fprintf ( file ,  
     "InParm \t\t\t# Input parameters. cm is used.\n" );
  
  fprintf ( file ,  
     "%s \tA\t\t# output file name, ASCII.\n" ,  
     In_Parm . out_fname );
  fprintf ( file ,  
     "%ld \t\t\t# No. of photons\n" ,   In_Parm . num_photons );
  
  fprintf ( file ,  
     "%G\t%G\t\t# dz, dr [cm]\n" ,   In_Parm . dz , In_Parm . dr );
  fprintf ( file ,   "%hd\t%hd\t%hd\t# No. of dz, dr, da.\n\n" ,  
       In_Parm . nz ,   In_Parm . nr ,   In_Parm . na );
  
  fprintf ( file ,  
     "%hd\t\t\t\t\t# Number of layers\n" ,  
     In_Parm . num_layers );
  fprintf ( file ,  
     "#n\tmua\tmus\tg\td\t# One line for each layer\n" );  
  fprintf ( file ,  
     "%G\t\t\t\t\t# n for medium above\n" ,  
     In_Parm . layerspecs [ 0 ]. n );  
   for ( i = 1 ;  i <= In_Parm . num_layers ;  i ++ )    {
     LayerStruct  s ;
    s  =   In_Parm . layerspecs [ i ];
    fprintf ( file ,   "%G\t%G\t%G\t%G\t%G\t# layer %hd\n" ,
        s . n ,  s . mua ,  s . mus ,  s . g ,  s . z1 - s . z0 ,  i );
   }
  fprintf ( file ,   "%G\t\t\t\t\t# n for medium below\n\n" ,  
     In_Parm . layerspecs [ i ]. n );  
}

/***********************************************************
 *  Write reflectance, absorption, transmission. 
 ****/
void   WriteRAT ( FILE  *  file ,   OutStruct   Out_Parm )
{
  fprintf ( file ,  
     "RAT #Reflectance, absorption, transmission. \n" );
     /* flag. */

  fprintf ( file ,  
     "%-14.6G \t#Specular reflectance [-]\n" ,   Out_Parm . Rsp );
  fprintf ( file ,  
     "%-14.6G \t#Diffuse reflectance [-]\n" ,   Out_Parm . Rd );
  fprintf ( file ,  
     "%-14.6G \t#Absorbed fraction [-]\n" ,   Out_Parm . A );
  fprintf ( file ,  
     "%-14.6G \t#Transmittance [-]\n" ,   Out_Parm . Tt );
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  Write absorption as a function of layer. 
 ****/
void   WriteA_layer ( FILE  *  file ,  
                   short   Num_Layers ,
                   OutStruct   Out_Parm )
{
   short  i ;
  
  fprintf ( file ,  
     "A_l #Absorption as a function of layer. [-]\n" );
     /* flag. */

   for ( i = 1 ;  i <= Num_Layers ;  i ++ )
    fprintf ( file ,   "%12.4G\n" ,   Out_Parm . A_l [ i ]);
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  5 numbers each line.
 ****/
void   WriteRd_ra ( FILE  *  file ,  
                 short   Nr ,
                 short   Na ,
                 OutStruct   Out_Parm )
{
   short  ir ,  ia ;
  
  fprintf ( file ,  
       "%s\n%s\n%s\n%s\n%s\n%s\n" ,     /* flag. */
       "# Rd[r][angle]. [1/(cm2sr)]." ,
       "# Rd[0][0], [0][1],..[0][na-1]" ,
       "# Rd[1][0], [1][1],..[1][na-1]" ,
       "# ..." ,
       "# Rd[nr-1][0], [nr-1][1],..[nr-1][na-1]" ,
       "Rd_ra" );
  
   for ( ir = 0 ; ir < Nr ; ir ++ )
     for ( ia = 0 ; ia < Na ; ia ++ )    {
      fprintf ( file ,   "%12.4E " ,   Out_Parm . Rd_ra [ ir ][ ia ]);
       if (   ( ir * Na   +  ia  +   1 ) % 5   ==   0 )  fprintf ( file ,   "\n" );
     }
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  1 number each line.
 ****/
void   WriteRd_r ( FILE  *  file ,  
                short   Nr ,
                OutStruct   Out_Parm )
{
   short  ir ;
  
  fprintf ( file ,  
     "Rd_r #Rd[0], [1],..Rd[nr-1]. [1/cm2]\n" );    /* flag. */
  
   for ( ir = 0 ; ir < Nr ; ir ++ )   {
    fprintf ( file ,   "%12.4E\n" ,   Out_Parm . Rd_r [ ir ]);
   }
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  1 number each line.
 ****/
void   WriteRd_a ( FILE  *  file ,  
                short   Na ,
                OutStruct   Out_Parm )
{
   short  ia ;
  
  fprintf ( file ,  
     "Rd_a #Rd[0], [1],..Rd[na-1]. [sr-1]\n" );     /* flag. */
  
   for ( ia = 0 ; ia < Na ; ia ++ )   {
    fprintf ( file ,   "%12.4E\n" ,   Out_Parm . Rd_a [ ia ]);
   }
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  5 numbers each line.
 ****/
void   WriteTt_ra ( FILE  *  file ,  
                 short   Nr ,
                 short   Na ,
                 OutStruct   Out_Parm )
{
   short  ir ,  ia ;
  
  fprintf ( file ,  
       "%s\n%s\n%s\n%s\n%s\n%s\n" ,     /* flag. */
       "# Tt[r][angle]. [1/(cm2sr)]." ,
       "# Tt[0][0], [0][1],..[0][na-1]" ,
       "# Tt[1][0], [1][1],..[1][na-1]" ,
       "# ..." ,
       "# Tt[nr-1][0], [nr-1][1],..[nr-1][na-1]" ,
       "Tt_ra" );
  
   for ( ir = 0 ; ir < Nr ; ir ++ )
     for ( ia = 0 ; ia < Na ; ia ++ )    {
      fprintf ( file ,   "%12.4E " ,   Out_Parm . Tt_ra [ ir ][ ia ]);
       if (   ( ir * Na   +  ia  +   1 ) % 5   ==   0 )  fprintf ( file ,   "\n" );
     }
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  5 numbers each line.
 ****/
void   WriteA_rz ( FILE  *  file ,  
                short   Nr ,
                short   Nz ,
                OutStruct   Out_Parm )
{
   short  iz ,  ir ;
  
  fprintf ( file ,  
       "%s\n%s\n%s\n%s\n%s\n%s\n" ,   /* flag. */
       "# A[r][z]. [1/cm3]" ,
       "# A[0][0], [0][1],..[0][nz-1]" ,
       "# A[1][0], [1][1],..[1][nz-1]" ,
       "# ..." ,
       "# A[nr-1][0], [nr-1][1],..[nr-1][nz-1]" ,
       "A_rz" );
  
   for ( ir = 0 ; ir < Nr ; ir ++ )
     for ( iz = 0 ; iz < Nz ; iz ++ )    {
      fprintf ( file ,   "%12.4E " ,   Out_Parm . A_rz [ ir ][ iz ]);
       if (   ( ir * Nz   +  iz  +   1 ) % 5   ==   0 )  fprintf ( file ,   "\n" );
     }
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  1 number each line.
 ****/
void   WriteA_z ( FILE  *  file ,  
               short   Nz ,
               OutStruct   Out_Parm )
{
   short  iz ;
  
  fprintf ( file ,  
     "A_z #A[0], [1],..A[nz-1]. [1/cm]\n" );    /* flag. */
  
   for ( iz = 0 ; iz < Nz ; iz ++ )   {
    fprintf ( file ,   "%12.4E\n" ,   Out_Parm . A_z [ iz ]);
   }
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  1 number each line.
 ****/
void   WriteTt_r ( FILE  *  file ,  
                short   Nr ,
                OutStruct   Out_Parm )
{
   short  ir ;
  
  fprintf ( file ,  
     "Tt_r #Tt[0], [1],..Tt[nr-1]. [1/cm2]\n" );   /* flag. */
  
   for ( ir = 0 ; ir < Nr ; ir ++ )   {
    fprintf ( file ,   "%12.4E\n" ,   Out_Parm . Tt_r [ ir ]);
   }
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 *  1 number each line.
 ****/
void   WriteTt_a ( FILE  *  file ,  
                short   Na ,
                OutStruct   Out_Parm )
{
   short  ia ;
  
  fprintf ( file ,  
     "Tt_a #Tt[0], [1],..Tt[na-1]. [sr-1]\n" );   /* flag. */
  
   for ( ia = 0 ; ia < Na ; ia ++ )   {
    fprintf ( file ,   "%12.4E\n" ,   Out_Parm . Tt_a [ ia ]);
   }
  
  fprintf ( file ,   "\n" );
}

/***********************************************************
 ****/
void   WriteResult ( InputStruct   In_Parm ,  
                  OutStruct   Out_Parm ,
                  char   *   TimeReport )
{
  FILE  * file ;
  
  file  =  fopen ( In_Parm . out_fname ,   "w" );
   if ( file  ==  NULL )  nrerror ( "Cannot open file to write.\n" );
  
   if ( toupper ( In_Parm . out_fformat )   ==   'A' )  
     WriteVersion ( file ,   "A1" );
   else  
     WriteVersion ( file ,   "B1" );

  fprintf ( file ,   "# %s" ,   TimeReport );
  fprintf ( file ,   "\n" );
  
   WriteInParm ( file ,   In_Parm );
   WriteRAT ( file ,   Out_Parm );  
     /* reflectance, absorption, transmittance. */
  
   /* 1D arrays. */
   WriteA_layer ( file ,   In_Parm . num_layers ,   Out_Parm );
   WriteA_z ( file ,   In_Parm . nz ,   Out_Parm );
   WriteRd_r ( file ,   In_Parm . nr ,   Out_Parm );
   WriteRd_a ( file ,   In_Parm . na ,   Out_Parm );
   WriteTt_r ( file ,   In_Parm . nr ,   Out_Parm );
   WriteTt_a ( file ,   In_Parm . na ,   Out_Parm );
  
   /* 2D arrays. */
   WriteA_rz ( file ,   In_Parm . nr ,   In_Parm . nz ,   Out_Parm );
   WriteRd_ra ( file ,   In_Parm . nr ,   In_Parm . na ,   Out_Parm );
   WriteTt_ra ( file ,   In_Parm . nr ,   In_Parm . na ,   Out_Parm );
  
  fclose ( file );
}

__MACOSX/FTP-Uploads/ch03MonteCarlo/._MCMLIO.C

FTP-Uploads/ch03MonteCarlo/MCMLMAIN.C

FTP-Uploads/ch03MonteCarlo/MCMLMAIN.C

/***********************************************************
 *  Copyright Univ. of Texas M.D. Anderson Cancer Center
 *  1992.
 *
 *  main program for Monte Carlo simulation of photon
 *  distribution in multi-layered turbid media.
 *
 ****/

/****
 *  THINKCPROFILER is defined to generate profiler calls in 
 *  Think C. If 1, remember to turn on "Generate profiler 
 *  calls" in the options menu. 
 ****/
#define  THINKCPROFILER  0     

/* GNU cc does not support difftime() and CLOCKS_PER_SEC.*/
#define  GNUCC  0

#if  THINKCPROFILER
#include   < profile . h >
#include   < console . h >
#endif

#include   "mcml.h"

/*  Declare before they are used in main(). */
FILE  * GetFile ( char   * );
short   ReadNumRuns ( FILE *   );
void   ReadParm ( FILE *   ,   InputStruct   *   );
void   CheckParm ( FILE *   ,   InputStruct   *   );
void   InitOutputData ( InputStruct ,   OutStruct   * );
void   FreeData ( InputStruct ,   OutStruct   * );
double   Rspecular ( LayerStruct   *   );
void   LaunchPhoton ( double ,   LayerStruct   * ,   PhotonStruct   * );
void   HopDropSpin ( InputStruct    * , PhotonStruct   * , OutStruct   * );
void   SumScaleResult ( InputStruct ,   OutStruct   * );
void   WriteResult ( InputStruct ,   OutStruct ,   char   * );


/***********************************************************
 *  If F = 0, reset the clock and return 0.
 *
 *  If F = 1, pass the user time to Msg and print Msg on 
 *  screen, return the real time since F=0. 
 *
 *  If F = 2, same as F=1 except no printing.  
 *
 *  Note that clock() and time() return user time and real 
 *  time respectively.
 *  User time is whatever the system allocates to the 
 *  running of the program; 
 *  real time is wall-clock time.  In a time-shared system,
 *  they need not be the same.
 *  
 *  clock() only hold 16 bit integer, which is about 32768 
 *  clock ticks.
 ****/
time_t  PunchTime ( char  F ,   char   * Msg )
{
#if  GNUCC
   return ( 0 );
#else
   static  clock_t ut0 ;     /* user time reference. */
   static  time_t  rt0 ;     /* real time reference. */
   double  secs ;
   char  s [ STRLEN ];
  
   if ( F == 0 )   {
    ut0  =  clock ();
    rt0  =  time ( NULL );
     return ( 0 );
   }
   else   if ( F == 1 )    {
    secs  =   ( clock ()   -  ut0 ) / ( double ) CLOCKS_PER_SEC ;
     if   ( secs < 0 )  secs = 0 ;   /* clock() can overflow. */
    sprintf ( s ,   "User time: %8.0lf sec = %8.2lf hr.  %s\n" ,  
        secs ,  secs / 3600.0 ,   Msg );
    puts ( s );
    strcpy ( Msg ,  s );
     return ( difftime ( time ( NULL ),  rt0 ));
   }
   else   if ( F == 2 )   return ( difftime ( time ( NULL ),  rt0 ));
   else   return ( 0 );
#endif
}

/***********************************************************
 *  Print the current time and the estimated finishing time.
 *
 *  P1 is the number of computed photon packets.
 *  Pt is the total number of photon packets.
 ****/
void   PredictDoneTime ( long  P1 ,   long   Pt )   
{
  time_t now ,  done_time ;
   struct  tm  * date ;
   char  s [ 80 ];
  
  now  =  time ( NULL );
  date  =  localtime ( & now );
  strftime ( s ,   80 ,   "%H:%M %x" ,  date );
  printf ( "Now %s, " ,  s );
  
  done_time  =  now  +  
             ( time_t )   ( PunchTime ( 2 , "" ) / ( double ) P1 * ( Pt - P1 ));
  date  =  localtime ( & done_time );
  strftime ( s ,   80 ,   "%H:%M %x" ,  date );
  printf ( "End %s\n" ,  s );
}

/***********************************************************
 *  Report time and write results. 
 ****/
void   ReportResult ( InputStruct   In_Parm ,   OutStruct   Out_Parm )
{
   char  time_report [ STRLEN ];
  
  strcpy ( time_report ,   " Simulation time of this run." );
   PunchTime ( 1 ,  time_report );

   SumScaleResult ( In_Parm ,   & Out_Parm );
   WriteResult ( In_Parm ,   Out_Parm ,  time_report );
}

/***********************************************************
 *  Get the file name of the input data file from the 
 *  argument to the command line.
 ****/
void   GetFnameFromArgv ( int  argc ,
                       char   *  argv [],
                       char   *  input_filename )
{
   if ( argc >= 2 )   {           /* filename in command line */
    strcpy ( input_filename ,  argv [ 1 ]);
   }
   else
    input_filename [ 0 ]   =   '\0' ;
}  

    
/***********************************************************
 *  Execute Monte Carlo simulation for one independent run.
 ****/
void   DoOneRun ( short   NumRuns ,   InputStruct   * In_Ptr )
{
   register   long  i_photon ;    
     /* index to photon. register for speed.*/
   OutStruct  out_parm ;         /* distribution of photons.*/
   PhotonStruct  photon ;
   long  num_photons  =   In_Ptr -> num_photons ,  photon_rep = 10 ;

#if  THINKCPROFILER
   InitProfile ( 200 , 200 );  cecho2file ( "prof.rpt" , 0 ,  stdout );
#endif
    
   InitOutputData ( * In_Ptr ,   & out_parm );
  out_parm . Rsp   =   Rspecular ( In_Ptr -> layerspecs );  
  i_photon  =  num_photons ;
   PunchTime ( 0 ,   "" );
    
   do   {
     if ( num_photons  -  i_photon  ==  photon_rep )   {
      printf ( "%ld photons & %hd runs left, " ,  i_photon ,   NumRuns );
       PredictDoneTime ( num_photons  -  i_photon ,  num_photons );
      photon_rep  *=   10 ;
     }
     LaunchPhoton ( out_parm . Rsp ,   In_Ptr -> layerspecs ,   & photon );
     do    HopDropSpin ( In_Ptr ,   & photon ,   & out_parm );
     while   ( ! photon . dead );
   }   while ( -- i_photon );
    
#if  THINKCPROFILER
  exit ( 0 );
#endif
    
   ReportResult ( * In_Ptr ,  out_parm );
   FreeData ( * In_Ptr ,   & out_parm );
}

/***********************************************************
 *  The argument to the command line is filename, if any.
 *  Macintosh does not support command line.
 ****/
int
main ( int  argc ,   char   * argv [])  
{
   char  input_filename [ STRLEN ];
  FILE  * input_file_ptr ;
   short  num_runs ;     /* number of independent runs. */
   InputStruct  in_parm ;

   ShowVersion ( "Version 1.2.2, 2000" );
   GetFnameFromArgv ( argc ,  argv ,  input_filename );
  input_file_ptr  =   GetFile ( input_filename );
   CheckParm ( input_file_ptr ,   & in_parm );   
  num_runs  =   ReadNumRuns ( input_file_ptr );
  
   while ( num_runs -- )    {
     ReadParm ( input_file_ptr ,   & in_parm );
     DoOneRun ( num_runs ,   & in_parm );
   }
  
  fclose ( input_file_ptr );
   return ( 0 );
}

__MACOSX/FTP-Uploads/ch03MonteCarlo/._MCMLMAIN.C

FTP-Uploads/ch03MonteCarlo/MCMLNR.C

FTP-Uploads/ch03MonteCarlo/MCMLNR.C

/***********************************************************
 *  Copyright Univ. of Texas M.D. Anderson Cancer Center
 *  1992.
 *
 *  Some routines modified from Numerical Recipes in C,
 *  including error report, array or matrix declaration
 *  and releasing.
 ****/
#include   < stdlib . h >
#include   < stdio . h >
#include   < math . h >

/***********************************************************
 *  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 );
}

/***********************************************************
 *  Allocate an 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 ;
}

/***********************************************************
 *  Allocate a matrix 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 ;
}

/***********************************************************
 *  Release the memory.
 ****/
void   FreeVector ( double   * v , short  nl , short  nh )
{
  free (( char * )   ( v + nl ));
}

/***********************************************************
 *  Release the memory.
 ****/
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 ));
}

__MACOSX/FTP-Uploads/ch03MonteCarlo/._MCMLNR.C

FTP-Uploads/ch03MonteCarlo/ReadmeR5.txt

Last update: 3/1/2000 MCML is a Monte Carlo simulation program for Multi-layered Turbid Media with an infinitely narrow photon beam as the light source. The simulation is specified by an input text file called, for example, "sample.mci", which can be altered by any simple text editor. The output is another text file called, for example, "sample.mco". (The names are arbitrary.) CONV is a convolution program which uses the MCML output file to convolve for photon beams of variable size or shape (Gaussian or flat field). CONV can provide a variety of output formats (reflectance, transmission, iso-fluence contours, etc.) which are compatible with standard graphics applications. HISTORY ====================================================================== MCML 1.2 corrected a bug that sometimes led to a sqrt() domain error due to the finite machine precision. However, this error has not affected previously computed results because if this error happened, the program would have stopped execution and yielded no output. MCML 1.2.1 corrected a bug that sometimes led to memory problems when the grid size was too small. MCML 1.2.2 corrected a bug that was introduced in 1.2.1. CONV 1.1 corrected a bug of the qtrap() function in the convnr.c file. The integration by the original qtrap() sometimes converged incorrectly to zero when the Monte Carlo data was noisy. ====================================================================== Lihong Wang, Ph.D. Associate Professor Biomedical Engineering Program 234C Zachry Engineering Center Texas A&M University College Station, TX 77843-3120 Tel: 979-847-9040 Fax: 979-845-4450 Email: [email protected] URL: http://oilab.tamu.edu Steven L. Jacques, Ph.D. Professor Oregon Medical Laser Center Providence/St. Vincent Hospital 9205 SW Barnes Rd. Portland, OR 97225 Tel: 503-216-4092 Fax: 503-291-2422 Email: [email protected] URL: http://omlc.ogi.edu/staff/jacques.html

__MACOSX/FTP-Uploads/ch03MonteCarlo/._ReadmeR5.txt

FTP-Uploads/ch03MonteCarlo/SAMPLE.MCO

A1 # Version number of the file format. #### # Data categories include: # InParm, RAT, # A_l, A_z, Rd_r, Rd_a, Tt_r, Tt_a, # A_rz, Rd_ra, Tt_ra #### # User time: 1488.13 sec = 0.41 hr. Simulation time of this run. InParm # Input parameters. cm is used. sample.mco A # output file name, ASCII. 1000000 # No. of photons 0.01 0.01 # dz, dr [cm] 40 50 1 # No. of dz, dr, da. 3 # Number of layers #n mua mus g d # One line for each layer 1 # n for medium above 1.37 1 100 0.9 0.1 # layer 1 1.37 1 10 0 0.1 # layer 2 1.37 2 10 0.7 0.2 # layer 3 1 # n for medium below RAT #Reflectance, absorption, transmission. 0.0243729 #Specular reflectance [-] 0.237544 #Diffuse reflectance [-] 0.641594 #Absorbed fraction [-] 0.0964896 #Transmittance [-] A_l #Absorption as a function of layer. [-] 0.2612 0.1488 0.2316 A_z #A[0], [1],..A[nz-1]. [1/cm] 2.7990E+00 2.8099E+00 2.8125E+00 2.7845E+00 2.7265E+00 2.6497E+00 2.5552E+00 2.4446E+00 2.3323E+00 2.2037E+00 2.0549E+00 1.9209E+00 1.7779E+00 1.6612E+00 1.5438E+00 1.4102E+00 1.2974E+00 1.1851E+00 1.0694E+00 9.5971E-01 1.7314E+00 1.6297E+00 1.5446E+00 1.4724E+00 1.4007E+00 1.3238E+00 1.2726E+00 1.2104E+00 1.1565E+00 1.1089E+00 1.0698E+00 1.0307E+00 9.9160E-01 9.5890E-01 9.3477E-01 9.0365E-01 8.7446E-01 8.6162E-01 8.4770E-01 8.3724E-01 Rd_r #Rd[0], [1],..Rd[nr-1]. [1/cm2] 3.0924E+01 1.0071E+01 6.1187E+00 4.4015E+00 3.5138E+00 2.8528E+00 2.4344E+00 2.0692E+00 1.7715E+00 1.5409E+00 1.3425E+00 1.2006E+00 1.0319E+00 8.9424E-01 7.9300E-01 7.0183E-01 6.1493E-01 5.4537E-01 4.8518E-01 4.2576E-01 3.7759E-01 3.2900E-01 2.9997E-01 2.6421E-01 2.3595E-01 2.0775E-01 1.8657E-01 1.6498E-01 1.4860E-01 1.4064E-01 1.2364E-01 1.0955E-01 9.9937E-02 8.9281E-02 8.3917E-02 7.1952E-02 6.5441E-02 5.8384E-02 5.3626E-02 4.8753E-02 4.5503E-02 3.8753E-02 3.6688E-02 3.1858E-02 3.1348E-02 2.6778E-02 2.5402E-02 2.2791E-02 2.0812E-02 3.1424E-01 Rd_a #Rd[0], [1],..Rd[na-1]. [sr-1] 3.4038E-02 Tt_r #Tt[0], [1],..Tt[nr-1]. [1/cm2] 5.8942E-01 6.0085E-01 5.6453E-01 5.5132E-01 5.3655E-01 5.3603E-01 5.2027E-01 4.9193E-01 4.6582E-01 4.3591E-01 4.1109E-01 3.9854E-01 3.6636E-01 3.3945E-01 3.2122E-01 2.9694E-01 2.8041E-01 2.6182E-01 2.3866E-01 2.2141E-01 2.1004E-01 1.9047E-01 1.7161E-01 1.6091E-01 1.5083E-01 1.3239E-01 1.2549E-01 1.1543E-01 9.9789E-02 9.4186E-02 8.4806E-02 7.8703E-02 7.4215E-02 6.6476E-02 6.0723E-02 5.6045E-02 5.1798E-02 4.6792E-02 4.3551E-02 4.0498E-02 3.6332E-02 3.4432E-02 3.0875E-02 2.9627E-02 2.6649E-02 2.3876E-02 2.2584E-02 2.1318E-02 1.8962E-02 3.3392E-01 Tt_a #Tt[0], [1],..Tt[na-1]. [sr-1] 1.3826E-02 # A[r][z]. [1/cm3] # A[0][0], [0][1],..[0][nz-1] # A[1][0], [1][1],..[1][nz-1] # ... # A[nr-1][0], [nr-1][1],..[nr-1][nz-1] A_rz 3.2948E+03 3.0386E+03 2.5033E+03 1.8514E+03 1.2758E+03 8.5363E+02 5.5268E+02 3.6460E+02 2.4357E+02 1.7068E+02 1.1574E+02 8.3167E+01 6.3684E+01 4.9714E+01 3.7366E+01 3.1753E+01 2.3568E+01 2.0270E+01 1.5639E+01 1.3584E+01 1.9635E+01 1.7311E+01 1.4253E+01 1.2972E+01 9.3743E+00 9.7259E+00 8.8083E+00 7.1041E+00 6.9629E+00 5.8110E+00 4.5151E+00 5.3302E+00 3.9514E+00 3.7088E+00 3.5495E+00 3.2786E+00 3.3136E+00 3.2377E+00 2.6409E+00 2.3877E+00 7.7127E+01 1.3435E+02 2.1857E+02 2.8337E+02 3.0338E+02 2.8525E+02 2.4567E+02 1.9975E+02 1.5713E+02 1.2392E+02 9.4105E+01 7.2111E+01 5.4411E+01 4.3708E+01 3.5286E+01 2.7410E+01 2.2960E+01 1.8669E+01 1.5932E+01 1.1772E+01 1.9931E+01 1.6372E+01 1.3586E+01 1.2065E+01 1.1066E+01 9.0454E+00 8.0976E+00 7.3499E+00 6.8339E+00 5.2720E+00 5.5565E+00 4.3873E+00 4.1344E+00 3.8387E+00 3.8148E+00 3.2499E+00 3.0570E+00 2.8812E+00 2.6316E+00 2.3044E+00 4.5798E+01 5.7578E+01 7.8613E+01 1.0230E+02 1.1924E+02 1.2629E+02 1.2437E+02 1.1403E+02 1.0157E+02 8.7131E+01 7.1093E+01 5.7353E+01 4.5795E+01 3.7662E+01 3.0809E+01 2.4446E+01 2.1182E+01 1.7132E+01 1.4133E+01 1.0873E+01 1.8306E+01 1.5789E+01 1.2574E+01 1.1981E+01 9.9820E+00 9.3130E+00 8.0804E+00 7.3127E+00 5.9439E+00 5.5170E+00 5.0146E+00 4.6705E+00 4.3288E+00 3.9200E+00 3.9201E+00 3.3529E+00 3.1221E+00 2.7360E+00 2.6868E+00 2.4713E+00 3.3587E+01 3.7233E+01 4.5398E+01 5.5696E+01 6.4872E+01 7.1115E+01 7.3677E+01 7.1838E+01 6.7887E+01 6.2389E+01 5.4319E+01 4.5239E+01 3.8338E+01 3.2079E+01 2.7404E+01 2.2464E+01 1.8471E+01 1.5310E+01 1.3049E+01 1.0919E+01 1.6960E+01 1.4700E+01 1.2635E+01 1.1121E+01 1.0090E+01 8.5985E+00 7.5797E+00 6.6712E+00 6.0644E+00 5.5476E+00 4.8234E+00 4.7161E+00 3.9237E+00 3.8259E+00 3.5619E+00 3.0617E+00 2.8766E+00 2.5836E+00 2.4047E+00 2.4255E+00 2.6644E+01 2.8171E+01 3.2086E+01 3.6863E+01 4.1943E+01 4.6057E+01 4.8637E+01 4.8976E+01 4.8396E+01 4.5849E+01 4.0957E+01 3.5323E+01 3.1106E+01 2.6812E+01 2.2661E+01 1.9077E+01 1.6448E+01 1.3865E+01 1.1552E+01 9.5818E+00 1.4848E+01 1.3116E+01 1.1504E+01 1.0503E+01 9.6053E+00 8.4464E+00 7.5264E+00 6.7147E+00 5.8786E+00 5.2765E+00 4.7106E+00 4.1899E+00 3.9374E+00 3.5330E+00 3.4797E+00 3.0063E+00 2.8387E+00 2.8062E+00 2.4622E+00 2.3508E+00 2.1769E+01 2.2587E+01 2.4633E+01 2.7557E+01 3.0373E+01 3.2957E+01 3.4955E+01 3.5829E+01 3.5786E+01 3.4572E+01 3.1326E+01 2.8770E+01 2.4797E+01 2.2481E+01 1.9479E+01 1.6438E+01 1.4432E+01 1.2402E+01 1.0480E+01 8.6868E+00 1.4272E+01 1.2484E+01 1.0723E+01 9.7950E+00 8.6256E+00 7.7732E+00 6.8854E+00 6.0300E+00 5.6092E+00 4.9945E+00 4.7133E+00 4.4940E+00 3.9896E+00 3.5302E+00 3.3458E+00 3.0541E+00 2.7935E+00 2.7386E+00 2.5252E+00 2.4299E+00 1.8557E+01 1.9028E+01 2.0184E+01 2.1805E+01 2.3519E+01 2.5307E+01 2.6769E+01 2.7288E+01 2.7692E+01 2.6946E+01 2.5086E+01 2.3027E+01 2.0763E+01 1.8581E+01 1.6527E+01 1.4494E+01 1.2629E+01 1.0987E+01 9.3922E+00 7.6509E+00 1.2637E+01 1.1234E+01 9.9387E+00 8.9271E+00 7.8422E+00 7.4306E+00 6.3949E+00 5.9860E+00 5.5244E+00 4.8229E+00 4.5907E+00 4.0413E+00 3.6031E+00 3.3596E+00 3.1579E+00 3.0289E+00 2.6747E+00 2.6619E+00 2.4597E+00 2.3225E+00 1.5837E+01 1.5968E+01 1.6731E+01 1.7910E+01 1.8979E+01 2.0103E+01 2.0990E+01 2.1492E+01 2.1883E+01 2.1438E+01 2.0009E+01 1.8525E+01 1.7294E+01 1.5881E+01 1.4110E+01 1.2771E+01 1.1079E+01 9.6995E+00 8.2199E+00 7.0207E+00 1.1976E+01 1.0106E+01 9.3992E+00 8.3850E+00 7.5713E+00 6.7604E+00 6.2219E+00 5.7219E+00 5.1321E+00 4.5468E+00 4.1636E+00 3.9668E+00 3.5604E+00 3.4351E+00 3.1329E+00 2.8123E+00 2.5073E+00 2.5055E+00 2.4030E+00 2.2625E+00 1.3692E+01 1.3767E+01 1.4192E+01 1.5102E+01 1.5881E+01 1.6485E+01 1.7048E+01 1.7423E+01 1.7758E+01 1.7489E+01 1.6025E+01 1.5352E+01 1.4124E+01 1.3168E+01 1.2044E+01 1.0632E+01 9.6758E+00 8.7004E+00 7.1331E+00 6.1714E+00 1.0291E+01 9.1579E+00 8.6549E+00 7.7945E+00 7.1404E+00 6.3682E+00 5.9560E+00 5.3392E+00 4.7046E+00 4.3253E+00 4.0107E+00 3.7120E+00 3.3102E+00 2.9969E+00 2.9080E+00 2.8208E+00 2.5675E+00 2.4533E+00 2.3506E+00 2.1292E+00 1.1819E+01 1.1862E+01 1.2249E+01 1.2661E+01 1.3357E+01 1.3784E+01 1.4262E+01 1.4510E+01 1.4560E+01 1.4310E+01 1.3919E+01 1.3218E+01 1.1891E+01 1.1382E+01 1.0392E+01 9.1382E+00 8.4358E+00 7.3946E+00 6.3984E+00 5.4761E+00 9.5240E+00 8.6090E+00 8.0760E+00 7.1709E+00 6.3642E+00 5.7821E+00 5.4007E+00 5.0000E+00 4.6240E+00 4.0829E+00 3.7252E+00 3.4252E+00 3.2010E+00 2.9890E+00 2.7288E+00 2.6249E+00 2.3309E+00 2.2733E+00 2.2435E+00 2.1760E+00 1.0362E+01 1.0415E+01 1.0572E+01 1.0897E+01 1.1371E+01 1.1700E+01 1.1960E+01 1.2075E+01 1.2037E+01 1.1804E+01 1.1215E+01 1.0859E+01 1.0225E+01 9.6035E+00 8.9408E+00 8.2965E+00 7.4993E+00 6.5605E+00 5.6238E+00 5.1037E+00 8.6443E+00 7.9103E+00 7.2582E+00 6.5296E+00 6.0156E+00 5.3984E+00 4.9701E+00 4.7211E+00 4.1764E+00 3.9868E+00 3.6498E+00 3.3606E+00 3.1273E+00 2.8215E+00 2.7774E+00 2.4791E+00 2.3020E+00 2.3076E+00 2.1799E+00 2.0431E+00 9.0281E+00 9.1032E+00 9.3139E+00 9.5394E+00 9.7003E+00 9.9873E+00 1.0214E+01 1.0375E+01 1.0257E+01 9.9403E+00 9.5851E+00 9.1534E+00 8.9164E+00 8.3010E+00 7.8457E+00 7.2651E+00 6.3887E+00 5.9515E+00 5.2296E+00 4.5439E+00 7.9243E+00 7.1457E+00 6.6062E+00 6.1034E+00 5.4878E+00 5.1499E+00 4.6596E+00 4.1946E+00 3.9657E+00 3.7315E+00 3.4249E+00 3.3085E+00 2.9649E+00 2.8133E+00 2.5261E+00 2.4239E+00 2.2333E+00 2.2547E+00 2.1056E+00 1.9888E+00 7.9646E+00 7.9781E+00 8.0863E+00 8.2070E+00 8.5358E+00 8.6571E+00 8.8068E+00 8.8001E+00 8.7034E+00 8.4443E+00 8.1076E+00 7.8013E+00 7.5140E+00 7.2249E+00 6.8036E+00 6.2009E+00 5.6449E+00 5.2171E+00 4.5902E+00 4.0062E+00 7.1777E+00 6.4568E+00 5.8626E+00 5.3574E+00 5.1476E+00 4.6606E+00 4.4206E+00 3.9834E+00 3.7526E+00 3.3636E+00 3.1625E+00 2.8835E+00 2.7793E+00 2.7127E+00 2.5184E+00 2.3613E+00 2.2660E+00 2.0981E+00 1.9176E+00 1.8950E+00 7.0649E+00 7.0114E+00 7.1066E+00 7.2279E+00 7.3939E+00 7.4875E+00 7.6020E+00 7.6677E+00 7.5113E+00 7.2802E+00 7.1935E+00 6.8070E+00 6.5576E+00 6.1879E+00 5.9335E+00 5.4395E+00 4.9940E+00 4.5055E+00 4.0932E+00 3.5941E+00 6.2394E+00 5.8917E+00 5.4310E+00 5.1358E+00 4.7087E+00 4.1338E+00 4.0093E+00 3.6311E+00 3.4929E+00 3.3013E+00 2.9364E+00 2.8346E+00 2.6777E+00 2.5114E+00 2.4420E+00 2.2343E+00 2.0929E+00 2.0433E+00 1.9338E+00 1.8857E+00 6.2266E+00 6.2354E+00 6.2716E+00 6.3906E+00 6.4348E+00 6.5073E+00 6.5240E+00 6.5502E+00 6.4985E+00 6.3182E+00 6.1591E+00 5.9325E+00 5.7944E+00 5.4294E+00 5.2393E+00 4.8229E+00 4.3716E+00 4.0413E+00 3.7395E+00 3.2180E+00 5.6994E+00 5.3573E+00 4.9850E+00 4.5945E+00 4.3285E+00 3.9799E+00 3.7148E+00 3.4853E+00 3.2735E+00 3.0653E+00 2.9187E+00 2.5750E+00 2.4637E+00 2.3800E+00 2.2750E+00 2.1405E+00 1.9644E+00 1.8857E+00 1.9172E+00 1.7609E+00 5.4537E+00 5.5089E+00 5.5399E+00 5.6687E+00 5.6923E+00 5.7056E+00 5.7152E+00 5.6769E+00 5.6501E+00 5.4633E+00 5.3417E+00 5.2286E+00 4.9677E+00 4.8664E+00 4.6801E+00 4.2210E+00 3.9307E+00 3.6070E+00 3.2019E+00 2.9075E+00 5.2872E+00 4.8748E+00 4.4075E+00 4.2307E+00 3.9033E+00 3.6261E+00 3.4584E+00 3.2527E+00 3.0409E+00 2.8763E+00 2.6800E+00 2.5402E+00 2.3760E+00 2.2169E+00 2.0827E+00 1.9918E+00 1.8835E+00 1.8409E+00 1.7515E+00 1.7566E+00 4.8300E+00 4.9039E+00 4.8767E+00 4.9456E+00 5.0058E+00 4.9989E+00 5.0306E+00 5.0415E+00 4.9517E+00 4.7606E+00 4.7016E+00 4.5485E+00 4.4384E+00 4.3015E+00 4.0411E+00 3.8119E+00 3.6438E+00 3.2462E+00 2.9596E+00 2.5896E+00 4.5996E+00 4.4269E+00 4.2537E+00 3.8945E+00 3.6990E+00 3.3238E+00 3.2292E+00 3.0258E+00 2.8117E+00 2.7648E+00 2.5739E+00 2.3794E+00 2.2843E+00 2.1082E+00 2.0670E+00 1.9998E+00 1.8138E+00 1.7746E+00 1.7416E+00 1.7167E+00 4.3227E+00 4.2962E+00 4.3576E+00 4.3955E+00 4.3858E+00 4.4455E+00 4.3667E+00 4.3620E+00 4.2911E+00 4.1866E+00 4.0525E+00 4.2358E+00 3.9172E+00 3.7217E+00 3.5521E+00 3.2899E+00 3.2155E+00 2.9223E+00 2.5808E+00 2.3775E+00 4.2192E+00 4.0754E+00 3.7023E+00 3.4863E+00 3.3427E+00 3.1473E+00 2.9822E+00 2.7715E+00 2.6390E+00 2.4763E+00 2.3538E+00 2.1870E+00 2.1172E+00 1.9862E+00 1.8458E+00 1.8783E+00 1.7053E+00 1.6858E+00 1.5939E+00 1.5487E+00 3.8441E+00 3.8201E+00 3.8322E+00 3.8768E+00 3.8953E+00 3.9315E+00 3.8682E+00 3.8417E+00 3.7866E+00 3.6940E+00 3.5972E+00 3.6598E+00 3.4032E+00 3.2728E+00 3.2002E+00 2.9572E+00 2.7837E+00 2.5807E+00 2.3471E+00 2.0870E+00 3.7585E+00 3.6084E+00 3.4494E+00 3.2375E+00 3.0661E+00 2.8923E+00 2.7718E+00 2.6048E+00 2.4136E+00 2.2818E+00 2.2162E+00 2.0986E+00 2.0066E+00 1.9325E+00 1.7892E+00 1.7223E+00 1.6464E+00 1.5918E+00 1.5976E+00 1.5252E+00 3.4042E+00 3.3939E+00 3.4196E+00 3.3998E+00 3.4399E+00 3.4368E+00 3.4221E+00 3.3850E+00 3.3712E+00 3.2602E+00 3.2158E+00 3.1496E+00 3.1109E+00 3.0433E+00 2.8492E+00 2.6698E+00 2.5418E+00 2.2903E+00 2.1338E+00 1.9124E+00 3.4701E+00 3.3644E+00 3.1269E+00 3.1197E+00 2.8830E+00 2.6198E+00 2.5674E+00 2.3702E+00 2.3236E+00 2.1630E+00 2.0463E+00 1.9430E+00 1.8960E+00 1.7544E+00 1.7251E+00 1.5915E+00 1.5482E+00 1.5379E+00 1.4951E+00 1.4678E+00 3.0228E+00 3.0053E+00 3.0492E+00 3.0233E+00 3.0514E+00 3.1059E+00 3.0431E+00 3.0004E+00 2.9467E+00 2.8778E+00 2.8755E+00 2.8583E+00 2.7004E+00 2.5293E+00 2.5154E+00 2.4103E+00 2.2703E+00 2.0917E+00 1.9551E+00 1.7175E+00 3.1418E+00 2.9918E+00 2.8694E+00 2.7751E+00 2.6233E+00 2.5055E+00 2.3457E+00 2.2310E+00 2.1772E+00 1.9991E+00 1.9690E+00 1.8801E+00 1.7490E+00 1.6873E+00 1.5655E+00 1.5704E+00 1.4470E+00 1.4075E+00 1.4045E+00 1.3554E+00 2.6630E+00 2.6920E+00 2.6873E+00 2.7066E+00 2.7111E+00 2.7203E+00 2.6770E+00 2.6574E+00 2.6217E+00 2.5517E+00 2.5694E+00 2.5422E+00 2.4160E+00 2.3775E+00 2.2898E+00 2.1394E+00 2.0408E+00 1.9010E+00 1.7838E+00 1.5589E+00 2.8598E+00 2.7066E+00 2.6432E+00 2.5265E+00 2.4575E+00 2.2568E+00 2.2319E+00 2.0411E+00 1.9566E+00 1.9285E+00 1.8201E+00 1.7365E+00 1.6620E+00 1.6084E+00 1.4762E+00 1.4000E+00 1.4189E+00 1.4195E+00 1.3489E+00 1.3735E+00 2.3876E+00 2.3626E+00 2.4352E+00 2.4015E+00 2.4079E+00 2.3902E+00 2.3653E+00 2.3408E+00 2.3281E+00 2.2576E+00 2.2886E+00 2.2317E+00 2.1830E+00 2.1600E+00 2.0673E+00 1.9381E+00 1.8657E+00 1.6526E+00 1.5798E+00 1.4443E+00 2.6620E+00 2.5482E+00 2.4804E+00 2.3450E+00 2.2386E+00 2.1159E+00 2.0492E+00 1.9377E+00 1.8545E+00 1.7612E+00 1.7561E+00 1.6344E+00 1.5849E+00 1.5136E+00 1.4112E+00 1.3888E+00 1.3180E+00 1.3103E+00 1.2635E+00 1.2869E+00 2.1119E+00 2.1256E+00 2.1295E+00 2.1536E+00 2.1385E+00 2.1148E+00 2.1143E+00 2.1177E+00 2.0659E+00 2.0042E+00 2.0440E+00 1.9885E+00 1.9518E+00 1.9156E+00 1.8581E+00 1.7383E+00 1.5976E+00 1.5347E+00 1.4418E+00 1.2964E+00 2.4686E+00 2.2954E+00 2.2674E+00 2.0815E+00 2.1004E+00 1.9503E+00 1.8882E+00 1.7545E+00 1.7012E+00 1.6693E+00 1.5983E+00 1.5034E+00 1.4942E+00 1.4012E+00 1.3337E+00 1.3058E+00 1.2432E+00 1.2629E+00 1.2168E+00 1.2610E+00 1.8946E+00 1.9057E+00 1.9044E+00 1.9001E+00 1.9014E+00 1.8965E+00 1.8995E+00 1.8601E+00 1.8465E+00 1.8037E+00 1.8405E+00 1.7795E+00 1.7141E+00 1.7282E+00 1.6805E+00 1.5760E+00 1.4835E+00 1.3979E+00 1.2656E+00 1.1949E+00 2.2488E+00 2.1137E+00 1.9997E+00 2.0246E+00 1.9324E+00 1.8407E+00 1.7628E+00 1.7034E+00 1.5686E+00 1.5391E+00 1.4519E+00 1.4477E+00 1.3778E+00 1.3015E+00 1.3479E+00 1.2863E+00 1.1986E+00 1.1677E+00 1.1879E+00 1.1687E+00 1.6987E+00 1.6827E+00 1.7047E+00 1.7061E+00 1.7158E+00 1.7094E+00 1.6983E+00 1.6846E+00 1.6665E+00 1.6463E+00 1.5849E+00 1.6117E+00 1.5663E+00 1.5510E+00 1.4628E+00 1.4230E+00 1.3551E+00 1.2776E+00 1.1620E+00 1.0669E+00 2.0121E+00 1.9544E+00 1.8696E+00 1.8270E+00 1.7656E+00 1.7353E+00 1.6360E+00 1.5358E+00 1.5129E+00 1.4390E+00 1.3534E+00 1.3619E+00 1.2700E+00 1.2677E+00 1.2437E+00 1.1999E+00 1.1305E+00 1.1560E+00 1.1133E+00 1.0908E+00 1.5207E+00 1.5165E+00 1.5117E+00 1.5359E+00 1.5276E+00 1.5194E+00 1.5301E+00 1.5007E+00 1.4826E+00 1.4596E+00 1.4404E+00 1.4635E+00 1.4691E+00 1.3764E+00 1.3633E+00 1.3128E+00 1.2118E+00 1.1337E+00 1.0491E+00 9.7646E-01 1.8755E+00 1.7494E+00 1.6946E+00 1.6781E+00 1.6460E+00 1.5509E+00 1.5292E+00 1.4754E+00 1.3700E+00 1.3268E+00 1.2798E+00 1.2537E+00 1.2267E+00 1.1463E+00 1.1520E+00 1.1316E+00 1.0604E+00 1.0777E+00 1.0566E+00 1.0649E+00 1.3499E+00 1.3468E+00 1.3543E+00 1.3488E+00 1.3537E+00 1.3669E+00 1.3556E+00 1.3486E+00 1.3079E+00 1.3256E+00 1.3347E+00 1.2939E+00 1.2715E+00 1.2377E+00 1.2155E+00 1.1451E+00 1.0981E+00 1.0534E+00 9.5789E-01 9.0263E-01 1.7257E+00 1.6633E+00 1.6120E+00 1.4884E+00 1.4672E+00 1.4304E+00 1.4086E+00 1.3613E+00 1.2911E+00 1.2122E+00 1.1902E+00 1.1806E+00 1.1569E+00 1.1083E+00 1.1014E+00 1.0783E+00 1.0283E+00 1.0584E+00 9.9759E-01 9.8248E-01 1.2163E+00 1.2074E+00 1.2026E+00 1.2330E+00 1.2098E+00 1.2253E+00 1.2125E+00 1.1995E+00 1.1789E+00 1.1688E+00 1.1565E+00 1.1547E+00 1.1377E+00 1.1110E+00 1.0881E+00 1.0172E+00 1.0387E+00 9.6031E-01 8.6833E-01 8.5469E-01 1.5956E+00 1.5020E+00 1.4598E+00 1.4521E+00 1.3910E+00 1.3254E+00 1.2741E+00 1.2691E+00 1.2072E+00 1.1662E+00 1.1441E+00 1.1013E+00 1.0421E+00 1.0260E+00 1.0301E+00 9.7867E-01 9.9286E-01 9.4405E-01 9.3844E-01 9.1040E-01 1.0755E+00 1.0862E+00 1.0925E+00 1.0933E+00 1.0929E+00 1.1082E+00 1.0942E+00 1.0766E+00 1.0711E+00 1.0551E+00 1.0601E+00 1.0737E+00 1.0516E+00 1.0177E+00 9.9355E-01 9.5875E-01 8.9918E-01 8.7552E-01 8.2110E-01 7.5315E-01 1.3746E+00 1.3826E+00 1.3237E+00 1.3093E+00 1.2601E+00 1.1968E+00 1.1861E+00 1.1802E+00 1.1385E+00 1.0846E+00 1.0894E+00 1.0158E+00 1.0090E+00 9.5829E-01 9.8144E-01 9.4751E-01 9.3959E-01 8.8338E-01 8.9983E-01 9.0228E-01 9.8226E-01 9.6655E-01 9.7084E-01 9.8678E-01 9.8182E-01 9.8059E-01 9.7874E-01 9.6592E-01 9.5401E-01 9.4632E-01 9.4960E-01 9.5398E-01 9.4803E-01 9.0820E-01 8.7932E-01 8.5266E-01 8.6283E-01 7.9537E-01 7.3757E-01 6.8868E-01 1.3076E+00 1.2858E+00 1.2373E+00 1.2098E+00 1.1835E+00 1.1628E+00 1.1642E+00 1.1090E+00 1.0740E+00 1.0222E+00 9.8878E-01 9.9209E-01 9.3850E-01 8.8728E-01 9.1508E-01 8.9674E-01 8.7571E-01 8.6071E-01 8.3970E-01 8.2235E-01 8.8378E-01 8.8026E-01 8.6619E-01 8.8493E-01 8.8189E-01 8.6652E-01 8.7358E-01 8.7070E-01 8.6169E-01 8.5555E-01 8.6059E-01 8.5871E-01 8.3487E-01 8.3882E-01 8.2077E-01 7.9024E-01 7.5210E-01 7.0088E-01 6.6391E-01 6.4512E-01 1.1931E+00 1.1749E+00 1.1711E+00 1.1259E+00 1.0999E+00 1.0295E+00 1.0784E+00 1.0130E+00 9.9259E-01 9.6094E-01 9.3163E-01 9.2330E-01 8.8714E-01 8.8293E-01 8.3708E-01 8.6108E-01 8.4607E-01 8.0628E-01 8.0119E-01 8.0345E-01 7.8553E-01 7.8661E-01 7.7904E-01 7.8155E-01 7.9682E-01 7.8092E-01 7.8820E-01 7.8320E-01 7.7516E-01 7.7212E-01 7.6952E-01 7.6155E-01 7.7033E-01 7.5267E-01 7.3192E-01 7.1749E-01 6.8001E-01 6.5281E-01 6.1977E-01 5.6793E-01 1.0790E+00 1.0672E+00 1.0783E+00 1.0528E+00 1.0127E+00 1.0163E+00 9.4146E-01 9.3910E-01 9.1052E-01 8.8878E-01 9.0447E-01 8.4826E-01 8.1868E-01 8.3064E-01 8.0110E-01 8.0422E-01 7.8086E-01 7.5908E-01 7.7014E-01 7.6925E-01 6.9852E-01 7.0042E-01 7.1061E-01 7.0840E-01 7.1255E-01 7.0605E-01 7.0606E-01 7.0504E-01 7.1180E-01 7.0345E-01 6.9485E-01 6.9859E-01 7.0690E-01 7.0108E-01 6.5780E-01 6.3330E-01 6.1287E-01 5.8704E-01 5.6270E-01 5.3811E-01 9.9312E-01 9.5628E-01 9.5367E-01 9.5048E-01 9.2142E-01 9.5139E-01 9.1865E-01 8.7802E-01 8.2124E-01 8.1866E-01 8.4912E-01 8.1743E-01 8.0547E-01 7.8259E-01 7.5129E-01 7.3167E-01 7.5342E-01 7.2070E-01 7.3100E-01 7.2000E-01 6.2793E-01 6.3130E-01 6.4241E-01 6.3721E-01 6.3572E-01 6.3017E-01 6.4604E-01 6.5014E-01 6.4188E-01 6.2896E-01 6.2253E-01 6.5200E-01 6.0492E-01 6.2282E-01 6.1455E-01 5.8091E-01 5.3239E-01 5.5092E-01 5.0460E-01 4.8490E-01 9.2104E-01 9.0215E-01 8.9772E-01 8.7490E-01 8.5113E-01 8.5053E-01 8.4185E-01 8.0838E-01 7.7554E-01 8.2087E-01 7.7154E-01 7.6526E-01 7.4639E-01 7.2938E-01 7.5572E-01 6.8488E-01 6.6837E-01 6.6650E-01 6.8911E-01 6.7796E-01 5.5644E-01 5.6895E-01 5.7983E-01 5.7082E-01 5.7556E-01 5.6906E-01 5.7558E-01 5.7668E-01 5.6513E-01 5.6780E-01 5.8224E-01 5.8286E-01 5.6018E-01 5.7118E-01 5.4459E-01 5.4363E-01 5.1884E-01 5.0884E-01 4.6147E-01 4.4610E-01 8.7015E-01 8.2078E-01 8.4014E-01 7.8954E-01 8.2303E-01 7.8961E-01 7.8648E-01 7.7380E-01 7.4541E-01 7.3772E-01 7.2885E-01 7.2475E-01 6.6522E-01 6.7419E-01 6.8238E-01 6.6410E-01 6.5399E-01 6.4403E-01 6.4874E-01 6.2729E-01 5.0943E-01 5.0963E-01 5.1031E-01 5.2526E-01 5.2100E-01 5.1810E-01 5.2357E-01 5.1802E-01 5.1339E-01 5.1676E-01 5.1947E-01 5.3179E-01 5.0923E-01 5.1169E-01 5.0501E-01 4.9471E-01 4.6949E-01 4.4864E-01 4.4612E-01 4.0766E-01 7.7757E-01 7.6714E-01 7.6915E-01 7.9140E-01 7.5316E-01 7.4023E-01 7.1869E-01 6.8782E-01 7.1272E-01 6.9100E-01 6.6077E-01 6.7682E-01 6.5487E-01 6.5588E-01 6.3526E-01 6.2053E-01 6.3206E-01 5.9696E-01 6.0290E-01 6.0288E-01 4.6312E-01 4.5592E-01 4.5988E-01 4.5658E-01 4.7361E-01 4.6532E-01 4.7667E-01 4.6669E-01 4.7354E-01 4.7021E-01 4.6650E-01 4.6286E-01 4.6629E-01 4.5910E-01 4.6490E-01 4.4009E-01 4.3222E-01 4.1135E-01 3.8905E-01 3.8488E-01 7.3407E-01 7.4116E-01 7.3564E-01 7.0703E-01 7.1733E-01 6.6825E-01 6.7135E-01 6.4658E-01 6.4286E-01 6.3597E-01 6.3532E-01 6.2851E-01 6.1427E-01 6.0889E-01 5.7856E-01 5.6138E-01 5.5628E-01 5.7625E-01 5.5880E-01 5.7713E-01 4.1546E-01 4.2165E-01 4.1831E-01 4.2485E-01 4.2571E-01 4.2743E-01 4.2204E-01 4.3188E-01 4.2459E-01 4.3150E-01 4.2033E-01 4.3020E-01 4.4004E-01 4.3132E-01 4.0025E-01 4.1214E-01 3.8434E-01 3.8101E-01 3.7252E-01 3.5271E-01 7.0871E-01 6.7876E-01 6.6187E-01 6.5129E-01 6.3112E-01 6.3452E-01 6.2176E-01 6.3575E-01 6.0580E-01 5.6856E-01 5.7384E-01 5.8265E-01 5.7399E-01 5.6861E-01 5.7658E-01 5.3300E-01 5.4568E-01 5.4097E-01 5.5075E-01 5.4747E-01 3.8400E-01 3.7124E-01 3.7390E-01 3.8287E-01 3.8729E-01 3.9144E-01 3.8609E-01 3.8912E-01 3.8752E-01 3.9085E-01 3.9089E-01 3.8951E-01 3.9314E-01 3.7205E-01 3.9235E-01 3.8496E-01 3.6305E-01 3.5103E-01 3.3310E-01 3.3303E-01 6.2538E-01 6.3090E-01 6.1281E-01 6.0221E-01 5.8409E-01 5.6667E-01 5.8976E-01 5.8116E-01 5.8206E-01 5.6291E-01 5.5049E-01 5.2263E-01 5.2076E-01 5.2987E-01 5.2568E-01 5.1520E-01 5.1229E-01 5.1449E-01 5.1163E-01 5.1276E-01 3.4384E-01 3.4108E-01 3.4496E-01 3.4156E-01 3.4664E-01 3.5547E-01 3.4632E-01 3.5435E-01 3.5360E-01 3.4827E-01 3.5309E-01 3.5635E-01 3.5432E-01 3.5190E-01 3.5441E-01 3.4879E-01 3.2866E-01 3.2267E-01 3.0721E-01 2.9461E-01 5.6742E-01 5.7209E-01 5.6009E-01 5.6696E-01 5.6190E-01 5.3777E-01 5.5589E-01 5.2849E-01 5.2315E-01 5.1567E-01 5.2433E-01 5.1233E-01 4.9520E-01 4.8995E-01 4.9659E-01 4.8750E-01 4.8963E-01 4.8021E-01 4.8534E-01 4.6634E-01 3.0806E-01 3.0799E-01 3.1018E-01 3.1007E-01 3.1502E-01 3.1302E-01 3.1655E-01 3.1902E-01 3.2013E-01 3.2031E-01 3.3107E-01 3.4212E-01 3.2550E-01 3.2767E-01 3.2154E-01 3.1860E-01 2.9377E-01 2.8651E-01 2.9222E-01 2.7401E-01 5.1787E-01 5.3139E-01 5.3066E-01 5.3871E-01 5.4626E-01 4.9851E-01 5.1879E-01 4.8874E-01 4.7820E-01 5.0408E-01 4.9476E-01 4.7911E-01 4.6734E-01 4.6498E-01 4.6426E-01 4.4991E-01 4.7411E-01 4.5274E-01 4.4948E-01 4.6219E-01 2.8115E-01 2.7728E-01 2.7933E-01 2.8172E-01 2.8445E-01 2.8792E-01 2.8777E-01 2.8300E-01 2.8853E-01 2.8488E-01 2.9352E-01 3.1281E-01 2.9936E-01 2.9045E-01 2.9236E-01 2.9018E-01 2.9105E-01 2.6581E-01 2.4987E-01 2.4940E-01 5.0305E-01 4.8414E-01 4.8958E-01 4.7494E-01 4.7117E-01 4.9020E-01 4.5751E-01 4.6645E-01 4.5986E-01 4.7193E-01 4.4706E-01 4.5461E-01 4.4148E-01 4.5422E-01 4.2462E-01 4.3328E-01 4.3993E-01 4.2648E-01 4.3482E-01 4.1478E-01 2.5375E-01 2.6027E-01 2.5627E-01 2.5602E-01 2.6133E-01 2.6177E-01 2.6272E-01 2.6656E-01 2.6417E-01 2.6116E-01 2.6939E-01 2.7249E-01 2.6650E-01 2.7403E-01 2.7419E-01 2.6390E-01 2.4596E-01 2.5381E-01 2.5141E-01 2.3328E-01 4.7649E-01 4.5556E-01 4.5460E-01 4.5843E-01 4.4834E-01 4.3513E-01 4.5599E-01 4.3869E-01 4.3234E-01 4.2183E-01 4.2155E-01 4.2712E-01 4.2003E-01 4.3044E-01 3.9761E-01 4.2145E-01 3.9134E-01 3.9165E-01 3.9650E-01 4.1218E-01 2.3098E-01 2.3293E-01 2.3061E-01 2.3307E-01 2.3408E-01 2.3672E-01 2.3346E-01 2.4185E-01 2.3908E-01 2.3487E-01 2.4654E-01 2.4802E-01 2.5061E-01 2.5363E-01 2.4016E-01 2.3884E-01 2.3307E-01 2.2469E-01 2.2653E-01 2.1979E-01 4.0449E-01 4.0526E-01 4.3260E-01 4.1752E-01 4.0555E-01 4.2062E-01 4.2188E-01 4.1986E-01 4.0992E-01 3.9229E-01 4.0806E-01 3.9155E-01 3.8434E-01 3.9899E-01 3.9377E-01 3.5792E-01 3.7871E-01 3.8230E-01 3.8629E-01 3.9637E-01 2.0447E-01 2.0643E-01 2.1242E-01 2.1244E-01 2.1758E-01 2.1741E-01 2.1842E-01 2.2022E-01 2.1847E-01 2.2531E-01 2.2922E-01 2.3357E-01 2.2653E-01 2.2553E-01 2.3334E-01 2.2149E-01 2.1898E-01 2.1404E-01 2.0687E-01 2.0502E-01 3.9051E-01 4.0412E-01 3.9635E-01 3.8839E-01 4.1284E-01 4.0607E-01 3.8462E-01 3.6642E-01 3.7136E-01 3.7867E-01 3.6574E-01 3.6415E-01 3.7138E-01 3.6498E-01 3.7432E-01 3.4829E-01 3.6016E-01 3.5197E-01 3.5562E-01 3.4679E-01 1.9172E-01 1.8714E-01 1.9234E-01 1.9352E-01 1.9191E-01 1.9617E-01 2.0010E-01 1.9858E-01 2.0123E-01 2.0206E-01 2.0635E-01 2.1111E-01 2.0766E-01 2.0181E-01 2.0822E-01 2.0551E-01 2.0924E-01 1.9478E-01 1.9724E-01 1.8091E-01 3.5742E-01 3.5532E-01 3.6807E-01 3.6497E-01 3.7623E-01 3.6948E-01 3.7050E-01 3.6630E-01 3.5067E-01 3.5491E-01 3.4747E-01 3.5843E-01 3.3884E-01 3.5252E-01 3.3743E-01 3.3847E-01 3.4211E-01 3.3848E-01 3.4094E-01 3.3532E-01 1.7413E-01 1.7360E-01 1.7755E-01 1.7530E-01 1.7582E-01 1.7831E-01 1.8249E-01 1.7984E-01 1.8538E-01 1.8713E-01 1.8532E-01 2.0044E-01 1.9360E-01 1.9451E-01 1.9506E-01 1.8139E-01 1.8210E-01 1.7556E-01 1.8051E-01 1.7436E-01 3.3760E-01 3.2984E-01 3.2644E-01 3.3663E-01 3.2413E-01 3.3688E-01 3.2775E-01 3.4090E-01 3.3087E-01 3.3502E-01 3.2118E-01 3.2805E-01 3.3188E-01 3.1202E-01 3.2223E-01 3.0492E-01 3.2158E-01 3.2175E-01 3.2504E-01 3.1561E-01 1.5933E-01 1.5807E-01 1.5628E-01 1.5802E-01 1.6220E-01 1.6400E-01 1.6418E-01 1.6522E-01 1.6499E-01 1.7237E-01 1.7975E-01 1.7732E-01 1.7384E-01 1.7000E-01 1.7503E-01 1.7246E-01 1.7114E-01 1.6874E-01 1.6747E-01 1.5771E-01 3.2620E-01 3.3008E-01 3.1914E-01 3.2056E-01 2.9256E-01 3.2274E-01 3.2099E-01 3.0202E-01 2.9919E-01 3.1900E-01 3.0415E-01 3.0039E-01 3.1161E-01 3.0892E-01 2.9439E-01 3.0298E-01 2.9774E-01 2.8946E-01 2.9899E-01 2.9950E-01 2.2439E+00 2.2397E+00 2.2657E+00 2.3151E+00 2.3519E+00 2.3934E+00 2.4476E+00 2.4972E+00 2.5533E+00 2.6296E+00 2.7373E+00 2.7554E+00 2.8085E+00 2.8325E+00 2.8624E+00 2.8862E+00 2.8557E+00 2.9162E+00 2.8932E+00 2.9101E+00 5.8102E+00 5.8506E+00 5.8606E+00 5.9540E+00 5.9586E+00 5.9470E+00 5.9451E+00 6.0028E+00 6.0099E+00 6.0044E+00 6.0596E+00 6.0260E+00 6.0479E+00 6.0029E+00 6.0446E+00 6.0148E+00 5.9731E+00 5.9715E+00 5.9642E+00 6.0094E+00 # Rd[r][angle]. [1/(cm2sr)]. # Rd[0][0], [0][1],..[0][na-1] # Rd[1][0], [1][1],..[1][na-1] # ... # Rd[nr-1][0], [nr-1][1],..[nr-1][na-1] Rd_ra 6.2665E+00 2.0408E+00 1.2399E+00 8.9192E-01 7.1204E-01 5.7809E-01 4.9331E-01 4.1930E-01 3.5898E-01 3.1224E-01 2.7205E-01 2.4329E-01 2.0911E-01 1.8121E-01 1.6070E-01 1.4222E-01 1.2461E-01 1.1051E-01 9.8317E-02 8.6277E-02 7.6516E-02 6.6669E-02 6.0786E-02 5.3541E-02 4.7814E-02 4.2100E-02 3.7807E-02 3.3432E-02 3.0113E-02 2.8499E-02 2.5054E-02 2.2198E-02 2.0252E-02 1.8092E-02 1.7005E-02 1.4580E-02 1.3261E-02 1.1831E-02 1.0867E-02 9.8794E-03 9.2208E-03 7.8531E-03 7.4345E-03 6.4558E-03 6.3524E-03 5.4264E-03 5.1475E-03 4.6185E-03 4.2175E-03 6.3678E-02 # Tt[r][angle]. [1/(cm2sr)]. # Tt[0][0], [0][1],..[0][na-1] # Tt[1][0], [1][1],..[1][na-1] # ... # Tt[nr-1][0], [nr-1][1],..[nr-1][na-1] Tt_ra 1.1944E-01 1.2176E-01 1.1440E-01 1.1172E-01 1.0873E-01 1.0862E-01 1.0543E-01 9.9685E-02 9.4394E-02 8.8334E-02 8.3305E-02 8.0762E-02 7.4240E-02 6.8787E-02 6.5094E-02 6.0172E-02 5.6823E-02 5.3056E-02 4.8362E-02 4.4868E-02 4.2564E-02 3.8598E-02 3.4775E-02 3.2608E-02 3.0565E-02 2.6829E-02 2.5429E-02 2.3391E-02 2.0222E-02 1.9086E-02 1.7185E-02 1.5949E-02 1.5039E-02 1.3471E-02 1.2305E-02 1.1357E-02 1.0496E-02 9.4820E-03 8.8253E-03 8.2067E-03 7.3625E-03 6.9774E-03 6.2567E-03 6.0037E-03 5.4003E-03 4.8383E-03 4.5766E-03 4.3200E-03 3.8424E-03 6.7666E-02

__MACOSX/FTP-Uploads/ch03MonteCarlo/._SAMPLE.MCO

FTP-Uploads/ch03MonteCarlo/TEMPLATE.MCI

#### # Template of input files for Monte Carlo simulation (mcml). # Anything in a line after "#" is ignored as comments. # Space lines are also ignored. # Lengths are in cm, mua and mus are in 1/cm. #### 1.0 # file version 2 # number of runs ### Specify data for run 1 temp1.mco A # output filename, ASCII/Binary 10 # No. of photons 20E-4 20E-4 # dz, dr 10 20 30 # No. of dz, dr & da. 2 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.3 20 200 0.70 0.01 # layer 1 1.4 10 200 0.90 1.0E+8 # layer 2 1.0 # n for medium below. ### Specify data for run 2 temp2.mco A # output filename, ASCII/Binary 20 # No. of photons 20E-4 20E-4 # dz, dr 80 80 30 # No. of dz, dr & da. 1 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.4 10 200 0.70 1.0E+8 # layer 1 1.0 # n for medium below.

__MACOSX/FTP-Uploads/ch03MonteCarlo/._TEMPLATE.MCI

__MACOSX/FTP-Uploads/._ch03MonteCarlo

FTP-Uploads/ch04Convolution/Authors web.url

[DEFAULT] BASEURL=http://labs.seas.wustl.edu/bme/Wang/mc.html [InternetShortcut] URL=http://labs.seas.wustl.edu/bme/Wang/mc.html Modified=308847F69B6DC701C5

__MACOSX/FTP-Uploads/ch04Convolution/._Authors web.url

FTP-Uploads/ch04Convolution/Conv.exe

__MACOSX/FTP-Uploads/ch04Convolution/._Conv.exe

FTP-Uploads/ch04Convolution/CONV.H

/*********************************************************** * Program Name: conv. * * A program used to process the data from mcml - * A Monte Carlo simulation of photon distribution in * multi-layered turbid media in ANSI Standard C. **** * Creation Date: 11/1991. * Current Date: 6/1992. * * Lihong Wang, Ph. D. * Steven L. Jacques, Ph. D. * Laser Biology Research Laboratory - 17 * M.D. Anderson Cancer Center * University of Texas * 1515 Holcombe Blvd. * Houston, TX 77030 * USA * **** * General Naming Conventions: * Preprocessor names: all capital letters, * e.g. #define PREPROCESSORS * Globals: first letter of each word is capital, no * underscores, * e.g. short GlobalVar; * Dummy variables: first letter of each word is capital, * and words are connected by underscores, * e.g. void NiceFunction(char Dummy_Var); * Local variables: all lower cases, words are connected * by underscores, * e.g. short local_var; * Function names or data types: same as Globals. * **** * Dimension of length: cm. * ****/ #include <math.h> #include <stdlib.h> #include <stdio.h> #include <stddef.h> #include <time.h> #include <string.h> #include <ctype.h> #include <float.h> #include <limits.h> #define PI 3.1415926 #define STRLEN 256 /* String length. */ #define Boolean char #define SIGN(x) ((x)>=0 ? 1:-1) #define MAX(x, y) ((x)>(y) ? (x):(y)) #define MIN(x, y) ((x)<(y) ? (x):(y)) #define NULLCONVSTRUCT {0, 0, 0, 0, 0} #define NULLOUTSTRUCT {0.0,\ NULL,NULL,NULL,0.0,\ NULL,NULL,NULL,0.0,\ NULL,NULL,NULL,0.0,\ NULL,NULL,NULL,NULL,NULL,\ 0, NULLCONVSTRUCT} /****************** Stuctures *****************************/ /**** * Structure used to describe the geometry and optical * properties of a layer. * z0 and z1 are the z coordinates for the upper boundary * and lower boundary respectively. ****/ typedef struct { double z0, z1; /* z coordinates of a layer. [cm] */ double n; /* refractive index of a layer. */ double mua; /* absorption coefficient. [1/cm] */ double mus; /* scattering coefficient. [1/cm] */ double g; /* anisotropy. */ } LayerStruct; /**** * Parameters to describe a photon beam. * Pencil: infinitely narrow beam. This is default for the * beam from the mcml output. * Flat: Flat beam with radius R. * Gaussian: Gaussian with 1/e2 radius R. * Others: general beam described by points with interpolation. ****/ typedef struct { enum { pencil, flat, gaussian, others } type; /* beam type. */ double P; /* total power. [J] */ double R; /* radius. [cm] */ } BeamStruct; /**** * Input parameters for each independent run. * * z and r are for the cylindrical coordinate system. [cm] * a is for the angle alpha between the photon exiting * direction and the surface normal. [radian] * * The grid line separations in z, r, and alpha * directions are dz, dr, and da respectively. The numbers * of grid lines in z, r, and alpha directions are * nz, nr, and na respectively. * * The member layerspecs will point to an array of * structures which store parameters of each layer. * This array has (number_layers + 2) elements. One * element is for a layer. * The layers 0 and (num_layers + 1) are for top ambient * medium and the bottom ambient medium respectively. * * For convolution, the grid line separations in z, and alpha * directions are still dz, and da respectively. The numbers * of grid lines in z, and alpha directions are still * nz, and na respectively. However, the grid line separation * and the number of grid lines in r direction are drc and * nrc respectively. ****/ typedef struct { char in_fname[STRLEN]; /* name of mcml output file. */ char in_fformat; /* format of mcml output file . */ /* 'A' for ASCII, */ /* 'B' for binary. */ long num_photons; /* to be traced. */ double Wth; /* play roulette if photon */ /* weight < Wth. */ double dz; /* z grid separation.[cm] */ double dr; /* r grid separation.[cm] */ double da; /* alpha grid separation. */ /* [radian] */ short nz; /* array range 0..nz-1. */ short nr; /* array range 0..nr-1. */ short na; /* array range 0..na-1. */ short num_layers; /* number of layers. */ LayerStruct *layerspecs; /* layer parameters. */ BeamStruct beam; /* incident beam of finite size. */ double drc; /* convolution r grid separation.[cm] */ short nrc; /* convolution array range 0..nrc-1. */ float eps; /* relative error in convolution. */ } InputStruct; /**** * Structure to keep track of what quantities are convolved. * They are initialized to 0, and are set to 1 when the * represented quantity is convolved. * * When the beam profile is changed, they are all reset to 0. ****/ typedef struct { Boolean Rd_ra; Boolean Rd_r; Boolean A_rz; Boolean Tt_ra; Boolean Tt_r; } ConvStruct; /**** * Structures for scored physical quantities * from mcml and to be convolved for photon * beams of finite size. Therefore, "Out" * here means the output of both mcml and conv. * * The member allocated is used to keep the status * of the arrays. It is set to 1 if all the arrays * are allocated and assigned values. It is set to * 0 otherwise. * * z and r represent z and r coordinates of the * cylindrical coordinate system. [cm] * a is the angle alpha between the photon exiting * direction and the normal to the surfaces. [radian] * See comments of the InputStruct. * See manual for the physcial quantities. ****/ typedef struct { double Rsp; /* specular reflectance. [-] */ double **Rd_ra; /* 2D distribution of diffuse */ /* reflectance. [1/(cm2 sr)] */ double *Rd_r; /* 1D radial distribution of diffuse */ /* reflectance. [1/cm2] */ double *Rd_a; /* 1D angular distribution of diffuse */ /* reflectance. [1/sr] */ double Rd; /* total diffuse reflectance. [-] */ double **A_rz; /* 2D probability density in turbid */ /* media over r & z. [1/cm3] */ double *A_z; /* 1D probability density over z. */ /* [1/cm] */ double *A_l; /* each layer's absorption */ /* probability. [-] */ double A; /* total absorption probability. [-] */ double **Tt_ra; /* 2D distribution of total */ /* transmittance. [1/(cm2 sr)] */ double *Tt_r; /* 1D radial distribution of */ /* transmittance. [1/cm2] */ double *Tt_a; /* 1D angular distribution of */ /* transmittance. [1/sr] */ double Tt; /* total transmittance. [-] */ double **Rd_rac; /* convolved data. [J/(cm2 sr)] */ double *Rd_rc; /* [J/cm2] */ double **A_rzc; /* [J/cm3] */ double **Tt_rac; /* [J/(cm2 sr)] */ double *Tt_rc; /* [J/cm2] */ char allocated; /* set to 1 when arrays are allocated. */ ConvStruct conved; } OutStruct; /*********************************************************** * Routine prototypes for dynamic memory allocation and * release of arrays and matrices. * Modified from Numerical Recipes in C. ****/ double *AllocVector(short, short); double **AllocMatrix(short, short, short, short); void FreeVector(double *, short, short); void FreeMatrix(double **, short, short, short, short); void nrerror(char *); /*********************************************************** * Other prototypes. ****/ void IsoPlot(double **Z, /* the 2D array Z[i][j]. */ long int IXmax, long int IYmax, /* the 0<=i<=IXmax, 0<=j<=IYmax. */ double Dx, double Dy); /* the gridline separations. */ short GetShort(short, short); float GetFloat(float, float);

__MACOSX/FTP-Uploads/ch04Convolution/._CONV.H

FTP-Uploads/ch04Convolution/CONVCONV.C

FTP-Uploads/ch04Convolution/CONVCONV.C

/****************************************************************
 *  Specify photon beam profile, and convolute over the beam.
 *
 ****/

#include   "conv.h"

/****************************************************************
 *  If IBMPC is 1, file extensions are limited to 3 letters
 *  at most.
 ****/
#define  IBMPC  0

/****************************************************************
 *  Used by convolution over Gaussian beam.  Ignore the value
 *  beyond GAUSSLIMIT radius.
 ****/
#define  GAUSSLIMIT  4


/***********************Declarations****************************/
FILE        * GetWriteFile ( char   * );
void          AllocConvData ( InputStruct   * ,   OutStruct   * );
void          FreeConvData ( InputStruct   * ,   OutStruct   * );
float        qtrap ( float   ( * )   ( float ),   float ,   float ,   float );
double        BessI0 ( double );

/****************************************************************
 *  Data structures for the binary tree used to store part of
 *  the integrand evaluation.
 ****/
struct   Node   {
   float        x ,  y ;
   struct   Node   * left ,   * right ;
};

typedef   struct   Node   * LINK ;
typedef  LINK TREE ;

/****************************************************************
 *  A global structure to pass the current coordinate of the
 *  physical quantities being evaluated and the pointers of the
 *  input and output parameters to the integration function.
 ****/
struct   {
   double       r ;
   short        iz ,  ia ;
   InputStruct   * in_ptr ;
   OutStruct    * out_ptr ;
  TREE        tree ;       /* A tree to store ITheta() &*
                 * ExpBessI0(). */
}             ConvVar ;


/***********************Convolution*****************************/
/****************************************************************
 *  Specify the parameters for a flat beam.
 ****/
void
GetFlatBeam ( BeamStruct   *   Beam_Ptr )
{
   Beam_Ptr -> type  =  flat ;

  printf ( "Total energy of the flat beam [J]: " );
   Beam_Ptr -> =   GetFloat ( FLT_MIN ,  FLT_MAX );
  printf ( "Radius of the flat beam [cm]: " );
   Beam_Ptr -> =   GetFloat ( FLT_MIN ,  FLT_MAX );
  printf ( "Total power: %8.2lg J, and radius: %8.2lg cm.\n" ,
      Beam_Ptr -> P ,   Beam_Ptr -> R );
}

/****************************************************************
 *  Specify the parameters for a Gaussian beam.
 ****/
void
GetGaussianBeam ( BeamStruct   *   Beam_Ptr )
{
   Beam_Ptr -> type  =  gaussian ;

  printf ( "Total energy of the Gaussian beam [J]: " );
   Beam_Ptr -> =   GetFloat ( FLT_MIN ,  FLT_MAX );
  printf ( "1/e2 Radius of the Gaussian beam [cm]: " );
   Beam_Ptr -> =   GetFloat ( FLT_MIN ,  FLT_MAX );
  printf ( "Total power: %8.2lg J, and radius: %8.2lg cm.\n" ,
      Beam_Ptr -> P ,   Beam_Ptr -> R );
}

/****************************************************************
 *  Specify the beam profile.
 ****/
void
LaserBeam ( BeamStruct   *   Beam_Ptr ,   OutStruct   *   Out_Ptr )
{
   ConvStruct   null_conved  =  NULLCONVSTRUCT ;
   char         cmd_str [ STRLEN ];

  printf ( "Beam profile:f=flat, g=Gaussian. q=quit: " );
   do
    gets ( cmd_str );
   while   ( ! strlen ( cmd_str ));

   switch   ( toupper ( cmd_str [ 0 ]))   {
   case   'F' :
     Out_Ptr -> conved  =  null_conved ;
     GetFlatBeam ( Beam_Ptr );
     break ;
   case   'G' :
     Out_Ptr -> conved  =  null_conved ;
     GetGaussianBeam ( Beam_Ptr );
     break ;
   default : ;
   }
}


/****************************************************************
 *  Convolution.
 ***************************************************************/

/****************************************************************
 *  Specify the resolution and the number of points in r
 *  direction.
 *  Set the Out_Ptr->conved to null.
 *  Reallocate the arrays for the convolution arrays.
 ****/
void
ConvResolution ( InputStruct   *   In_Ptr ,   OutStruct   *   Out_Ptr )
{
   char         in_str [ STRLEN ];
   ConvStruct   null_conved  =  NULLCONVSTRUCT ;

   if   ( ! Out_Ptr -> allocated )   {
    puts ( "...No mcml output data to work with" );
     return ;
   }
  printf ( "Current resolution: %8.2lg cm and number of points: %hd\n" ,
      In_Ptr -> drc ,   In_Ptr -> nrc );
  printf ( "Input resolution in r direction [cm]: " );
   In_Ptr -> drc  =   GetFloat ( FLT_MIN ,  FLT_MAX );
  printf ( "Input number of points in r direction: " );
   In_Ptr -> nrc  =   GetShort ( 1 ,  SHRT_MAX );

  printf ( "Resolution: %8.2lg cm and number of points: %hd\n" ,
      In_Ptr -> drc ,   In_Ptr -> nrc );

   Out_Ptr -> conved  =  null_conved ;
   FreeConvData ( In_Ptr ,   Out_Ptr );
   AllocConvData ( In_Ptr ,   Out_Ptr );
}

/****************************************************************
 *  Specify the relative convolution error. 0.001-0.1 recomended.
 *  Set the Out_Ptr->conved to null.
 ****/
void
ConvError ( InputStruct   *   In_Ptr ,   OutStruct   *   Out_Ptr )
{
   char         in_str [ STRLEN ];
   ConvStruct   null_conved  =  NULLCONVSTRUCT ;
   float        eps ;

  printf ( "Relative convolution error\n" );
  printf ( "Current value is %8.2g (0.001-0.1 recommended): " ,
      In_Ptr -> eps );
   In_Ptr -> eps  =   GetFloat ( FLT_MIN ,   1 );
   Out_Ptr -> conved  =  null_conved ;
}

/***********************Binary Tree*****************************/
LINK
FillNode ( float  x ,   float  y )
{
  LINK        l ;

  l  =   ( LINK )  malloc ( sizeof ( struct   Node ));
   if   ( l )   {
    l -> =  x ;
    l -> =  y ;
    l -> left  =  l -> right  =  NULL ;
   }
   return   ( l );
}

/****************************************************************
 *  Assume the (x, y) is not in the tree.
 ****/
void
InsertNode ( TREE  *   TreePtr ,   float  x ,   float  y )
{
  LINK        l1 ,  l2 ;         /* l1 buffers l2. */

  l1  =  NULL ;
  l2  =   * TreePtr ;
   while   ( l2  !=  NULL )   {
    l1  =  l2 ;
     if   ( <  l2 -> x )
      l2  =  l2 -> left ;
     else
      l2  =  l2 -> right ;
   }

   if   ( l1  ==  NULL )         /* Empty tree. */
     * TreePtr   =   FillNode ( x ,  y );
   else   if   ( <  l1 -> x )
    l1 -> left  =   FillNode ( x ,  y );
   else
    l1 -> right  =   FillNode ( x ,  y );
}

LINK
SearchNode ( TREE  Tree ,   float  x )
{
  LINK        l  =   Tree ;
   Boolean      found  =   0 ;

   while   ( !=  NULL  &&   ! found )
     if   ( <  l -> x )
      l  =  l -> left ;
     else   if   ( >  l -> x )
      l  =  l -> right ;
     else
      found  =   1 ;

   return   ( l );
}

void
FreeTree ( TREE  Tree )
{
   if   ( Tree )   {
     FreeTree ( Tree -> left );
     FreeTree ( Tree -> right );
    free ( Tree );
   }
}

/***********************Integration*****************************/
/****************************************************************
 *  Compute Itheta shown in the manual.
 ****/
double
ITheta ( double  r ,   double  r2 ,   double  R )
{
   double       temp ;

   if   ( >=  r  +  r2 )
    temp  =   1 ;
   else   if   ( fabs ( -  r2 )   <=  R )   {
    temp  =   ( *  r  +  r2  *  r2  -  R  *  R )   /   ( 2   *  r  *  r2 );
     if   ( fabs ( temp )   >   1 )
      temp  =  SIGN ( temp );
    temp  =  acos ( temp )   /  PI ;
   }   else              /* R < fabs(r-r2) */
    temp  =   0 ;

   return   ( temp );
}

/****************************************************************
 ****/
double
ExpBessI0 ( double  r ,   double  r2 ,   double  R )
{
   double       expbess ;
   double       _RR  =   1   /   ( *  R );
   double       x  =   4   *  r  *  r2  *  _RR ;
   double       y  =   2   *   ( r2  *  r2  +  r  *  r )   *  _RR ;

  expbess  =  exp ( - +  x )   *   BessI0 ( x );
   return   ( expbess );
}

/****************************************************************
 *  Interpolate for the arrays A_rz[].
 ****/
double
A_rzInterp ( double   ** A_rz ,   double  r2 )
{
   double       ir2 ,   A_lo ,   A_hi ,   A_at_r2 ;
   short        iz ,  ir2lo ,  nr  =   ConvVar . in_ptr -> nr ;

  ir2  =  r2  /   ConvVar . in_ptr -> dr ;
  iz  =   ConvVar . iz ;
   if   ( nr  <   3 )
     A_at_r2   =   A_rz [ 0 ][ iz ];
   else   if   ( ir2  <  nr  -   1.5 )   {      /* interpolation. */
    ir2lo  =  MAX ( 0 ,   ( short )   ( ir2  -   0.5 ));      /* truncation. */
     A_lo   =   A_rz [ ir2lo ][ iz ];
     A_hi   =   A_rz [ ir2lo  +   1 ][ iz ];
     A_at_r2   =   A_lo   +   ( A_hi   -   A_lo )   *   ( ir2  -  ir2lo  -   0.5 );
   }   else   {            /* extrapolation. */
    ir2lo  =  nr  -   3 ;
     A_lo   =   A_rz [ ir2lo ][ iz ];
     A_hi   =   A_rz [ ir2lo  +   1 ][ iz ];
     if   ( A_lo   >=   A_hi )         /* Noise test. */
       A_at_r2   =   A_lo   +   ( A_hi   -   A_lo )   *   ( ir2  -  ir2lo  -   0.5 );
     else
       A_at_r2   =   0.0 ;
   }

   return   ( MAX ( 0 ,   A_at_r2 ));
}

/****************************************************************
 *  Interpolate for the arrays Rd_ra[] or Tt_ra[].
 ****/
double
RT_raInterp ( double   ** RT_ra ,   double  r2 )
{
   double       ir2 ,   RT_lo ,   RT_hi ,   RT_at_r2 ;
   short        ia ,  ir2lo ,  nr  =   ConvVar . in_ptr -> nr ;

  ir2  =  r2  /   ConvVar . in_ptr -> dr ;
  ia  =   ConvVar . ia ;
   if   ( nr  <   3 )
     RT_at_r2   =   RT_ra [ 0 ][ ia ];
   else   if   ( ir2  <  nr  -   1.5 )   {      /* interpolation. */
    ir2lo  =  MAX ( 0 ,   ( short )   ( ir2  -   0.5 ));      /* truncation. */
     RT_lo   =   RT_ra [ ir2lo ][ ia ];
     RT_hi   =   RT_ra [ ir2lo  +   1 ][ ia ];
     RT_at_r2   =   RT_lo   +   ( RT_hi   -   RT_lo )   *   ( ir2  -  ir2lo  -   0.5 );
   }   else   {            /* extrapolation. */
    ir2lo  =  nr  -   3 ;
     RT_lo   =   RT_ra [ ir2lo ][ ia ];
     RT_hi   =   RT_ra [ ir2lo  +   1 ][ ia ];
     if   ( RT_lo   >=   RT_hi )       /* Noise test. */
       RT_at_r2   =   RT_lo   +   ( RT_hi   -   RT_lo )   *   ( ir2  -  ir2lo  -   0.5 );
     else
       RT_at_r2   =   0.0 ;
   }

   return   ( MAX ( 0 ,   RT_at_r2 ));
}

/****************************************************************
 *  Interpolate for the arrays Rd_r[] or Tt_r[].
 ****/
double
RT_rInterp ( double   * RT_r ,   double  r2 )
{
   double       ir2 ,   RT_lo ,   RT_hi ,   RT_at_r2 ;
   short        ir2lo ,  nr  =   ConvVar . in_ptr -> nr ;

  ir2  =  r2  /   ConvVar . in_ptr -> dr ;
   if   ( nr  <   3 )
     RT_at_r2   =   RT_r [ 0 ];
   else   if   ( ir2  <  nr  -   1.5 )   {      /* interpolation. */
    ir2lo  =  MAX ( 0 ,   ( short )   ( ir2  -   0.5 ));      /* truncation. */
     RT_lo   =   RT_r [ ir2lo ];
     RT_hi   =   RT_r [ ir2lo  +   1 ];
     RT_at_r2   =   RT_lo   +   ( RT_hi   -   RT_lo )   *   ( ir2  -  ir2lo  -   0.5 );
   }   else   {            /* extrapolation. */
    ir2lo  =  nr  -   3 ;
     RT_lo   =   RT_r [ ir2lo ];
     RT_hi   =   RT_r [ ir2lo  +   1 ];
     if   ( RT_lo   >=   RT_hi )       /* Noise test. */
       RT_at_r2   =   RT_lo   +   ( RT_hi   -   RT_lo )   *   ( ir2  -  ir2lo  -   0.5 );
     else
       RT_at_r2   =   0.0 ;
   }

   return   ( MAX ( 0 ,   RT_at_r2 ));
}

/****************************************************************
 *  Convolution integrand for either flat or gaussian beams.
 *  Return the integrand for the convolution integral.
 *  r2 is the r" in the formula shown in the manual.
 *  When r2 is in the range of recorded array, interpolation
 *  is used to evaluate the diffuse reflectance at r2.
 *  Note that since the last grid elements collect all the
 *  photon weight that falls beyond the grid system, we should
 *  avoid using them in the convolution.
 ****/
float
A_rzFGIntegrand ( float  r2 )
{                 /* r" in the integration. */
   float        f ;
   short        nr  =   ConvVar . in_ptr -> nr ;
   double       R ,  r ,   A_at_r2 ;
  LINK        link ;

   A_at_r2   =   A_rzInterp ( ConvVar . out_ptr -> A_rz ,  r2 );

  R  =   ConvVar . in_ptr -> beam . R ;
  r  =   ConvVar . r ;
   if   (( link  =   SearchNode ( ConvVar . tree ,  r2 )))      /* f in tree. */
    f  =  link -> y ;
   else   {
     if   ( ConvVar . in_ptr -> beam . type  ==  flat )
      f  =   ITheta ( r ,  r2 ,  R );
     else              /* Gaussian. */
      f  =   ExpBessI0 ( r ,  r2 ,  R );
     InsertNode ( & ConvVar . tree ,  r2 ,  f );
   }

  f  *=   A_at_r2   *  r2 ;
   return   ( f );
}

/****************************************************************
 *  Convolution integrand for either flat or gaussian beams.
 *  See comments for A_rzFGIntegrand().
 ****/
float
Rd_raFGIntegrand ( float  r2 )
{                 /* r" in the integration. */
   float        f ;
   short        nr  =   ConvVar . in_ptr -> nr ;
   double       R ,  r ,   Rd_at_r2 ;
  LINK        link ;

   Rd_at_r2   =   RT_raInterp ( ConvVar . out_ptr -> Rd_ra ,  r2 );

  R  =   ConvVar . in_ptr -> beam . R ;
  r  =   ConvVar . r ;
   if   (( link  =   SearchNode ( ConvVar . tree ,  r2 )))      /* f in tree. */
    f  =  link -> y ;
   else   {
     if   ( ConvVar . in_ptr -> beam . type  ==  flat )
      f  =   ITheta ( r ,  r2 ,  R );
     else              /* Gaussian. */
      f  =   ExpBessI0 ( r ,  r2 ,  R );
     InsertNode ( & ConvVar . tree ,  r2 ,  f );
   }

  f  *=   Rd_at_r2   *  r2 ;
   return   ( f );
}

/****************************************************************
 *  Convolution integrand for either flat or gaussian beams.
 *  See comments for A_rzFGIntegrand().
 ****/
float
Rd_rFGIntegrand ( float  r2 )
{                 /* r" in the integration. */
   float        f ;
   short        nr  =   ConvVar . in_ptr -> nr ;
   double       R ,  r ,   Rd_at_r2 ;

   Rd_at_r2   =   RT_rInterp ( ConvVar . out_ptr -> Rd_r ,  r2 );

  R  =   ConvVar . in_ptr -> beam . R ;
  r  =   ConvVar . r ;
   if   ( ConvVar . in_ptr -> beam . type  ==  flat )
    f  =   Rd_at_r2   *   ITheta ( r ,  r2 ,  R )   *  r2 ;
   else                /* Gaussian. */
    f  =   Rd_at_r2   *   ExpBessI0 ( r ,  r2 ,  R )   *  r2 ;

   return   ( f );
}

/****************************************************************
 *  Convolution integrand for either flat or gaussian beams.
 *  See comments for A_rzFGIntegrand().
 ****/
float
Tt_raFGIntegrand ( float  r2 )
{                 /* r" in the integration. */
   float        f ;
   short        nr  =   ConvVar . in_ptr -> nr ;
   double       R ,  r ,   Tt_at_r2 ;
  LINK        link ;

   Tt_at_r2   =   RT_raInterp ( ConvVar . out_ptr -> Tt_ra ,  r2 );

  R  =   ConvVar . in_ptr -> beam . R ;
  r  =   ConvVar . r ;
   if   (( link  =   SearchNode ( ConvVar . tree ,  r2 )))      /* f in tree. */
    f  =  link -> y ;
   else   {
     if   ( ConvVar . in_ptr -> beam . type  ==  flat )
      f  =   ITheta ( r ,  r2 ,  R );
     else              /* Gaussian. */
      f  =   ExpBessI0 ( r ,  r2 ,  R );
     InsertNode ( & ConvVar . tree ,  r2 ,  f );
   }

  f  *=   Tt_at_r2   *  r2 ;
   return   ( f );
}

/****************************************************************
 *  Convolution integrand for either flat or gaussian beams.
 *  See comments for A_rzFGIntegrand().
 ****/
float
Tt_rFGIntegrand ( float  r2 )
{                 /* r" in the integration. */
   float        f ;
   short        nr  =   ConvVar . in_ptr -> nr ;
   double       R ,  r ,   Tt_at_r2 ;

   Tt_at_r2   =   RT_rInterp ( ConvVar . out_ptr -> Tt_r ,  r2 );

  R  =   ConvVar . in_ptr -> beam . R ;
  r  =   ConvVar . r ;
   if   ( ConvVar . in_ptr -> beam . type  ==  flat )
    f  =   Tt_at_r2   *   ITheta ( r ,  r2 ,  R )   *  r2 ;
   else                /* Gaussian. */
    f  =   Tt_at_r2   *   ExpBessI0 ( r ,  r2 ,  R )   *  r2 ;

   return   ( f );
}

/****************************************************************
 ****/
double
FlatIntegration ( float   ( * Func )   ( float ))
{
   double       rc  =   ConvVar . r ;
   double       R  =   ConvVar . in_ptr -> beam . R ;
   double       b_max  =   ( ConvVar . in_ptr -> nr  -   0.5 )   *   ConvVar . in_ptr -> dr ;
   double       a  =  MAX ( 0 ,  rc  -  R );
   double       b  =  MIN ( b_max ,  rc  +  R );

   if   ( >=  b )
     return   ( 0 );
   else
     return   ( qtrap ( Func ,  a ,  b ,   ConvVar . in_ptr -> eps ));
}

/****************************************************************
 ****/
double
GaussIntegration ( float   ( * Func )   ( float ))
{
   double       rc  =   ConvVar . r ;
   double       R  =   ConvVar . in_ptr -> beam . R ;
   double       b_max  =   ( ConvVar . in_ptr -> nr  -   0.5 )   *   ConvVar . in_ptr -> dr ;
   double       a  =  MAX ( 0 ,  rc  -  GAUSSLIMIT  *  R );
   double       b  =  MIN ( b_max ,  rc  +  GAUSSLIMIT  *  R );

   if   ( >=  b )
     return   ( 0 );
   else
     return   ( qtrap ( Func ,  a ,  b ,   ConvVar . in_ptr -> eps ));
}

/****************************************************************
 ****/
void
ConvA_rz ( InputStruct   *   In_Ptr ,
      OutStruct   *   Out_Ptr )
{
   short        irc ,  iz ;
   double       rc ,  P  =   In_Ptr -> beam . P ,  R  =   In_Ptr -> beam . R ;

  puts ( "The convolution may take a little while. Wait..." );
   for   ( irc  =   0 ;  irc  <   In_Ptr -> nrc ;  irc ++ )   {
    rc  =   ( irc  +   0.5 )   *   In_Ptr -> drc ;
     ConvVar . =  rc ;
     ConvVar . tree  =  NULL ;      /* init the tree. */
     for   ( iz  =   0 ;  iz  <   In_Ptr -> nz ;  iz ++ )   {
       ConvVar . iz  =  iz ;
       if   ( In_Ptr -> beam . type  ==  flat )
     Out_Ptr -> A_rzc [ irc ][ iz ]   =   2   *  P  /   ( *  R )
       *   FlatIntegration ( A_rzFGIntegrand );
       else            /* Gaussian. */
     Out_Ptr -> A_rzc [ irc ][ iz ]   =   4   *  P  /   ( *  R )
       *   GaussIntegration ( A_rzFGIntegrand );
     }
     FreeTree ( ConvVar . tree );
   }

   Out_Ptr -> conved . A_rz   =   1 ;
}

/****************************************************************
 ****/
void
ConvRd_ra ( InputStruct   *   In_Ptr ,
       OutStruct   *   Out_Ptr )
{
   short        irc ,  ia ;
   double       rc ,  P  =   In_Ptr -> beam . P ,  R  =   In_Ptr -> beam . R ;
   double       b_max  =   ( In_Ptr -> nr  -   1 )   *   In_Ptr -> dr ;

  puts ( "The convolution may take a little while. Wait..." );
   for   ( irc  =   0 ;  irc  <   In_Ptr -> nrc ;  irc ++ )   {
    rc  =   ( irc  +   0.5 )   *   In_Ptr -> drc ;
     ConvVar . =  rc ;
     ConvVar . tree  =  NULL ;      /* init the tree. */
     for   ( ia  =   0 ;  ia  <   In_Ptr -> na ;  ia ++ )   {
       ConvVar . ia  =  ia ;
       if   ( In_Ptr -> beam . type  ==  flat )
     Out_Ptr -> Rd_rac [ irc ][ ia ]   =   2   *  P  /   ( *  R )
       *   FlatIntegration ( Rd_raFGIntegrand );
       else            /* Gaussian. */
     Out_Ptr -> Rd_rac [ irc ][ ia ]   =   4   *  P  /   ( *  R )
       *   GaussIntegration ( Rd_raFGIntegrand );
     }
     FreeTree ( ConvVar . tree );
   }

   Out_Ptr -> conved . Rd_ra   =   1 ;
}

/****************************************************************
 ****/
void
ConvRd_r ( InputStruct   *   In_Ptr ,   OutStruct   *   Out_Ptr )
{
   short        irc ;
   double       rc ,  P  =   In_Ptr -> beam . P ,  R  =   In_Ptr -> beam . R ;
   double       b_max  =   ( In_Ptr -> nr  -   1 )   *   In_Ptr -> dr ;

   for   ( irc  =   0 ;  irc  <   In_Ptr -> nrc ;  irc ++ )   {
    rc  =   ( irc  +   0.5 )   *   In_Ptr -> drc ;
     ConvVar . =  rc ;
     if   ( In_Ptr -> beam . type  ==  flat )
       Out_Ptr -> Rd_rc [ irc ]   =   2   *  P  /   ( *  R )
     *   FlatIntegration ( Rd_rFGIntegrand );
     else              /* Gaussian. */
       Out_Ptr -> Rd_rc [ irc ]   =   4   *  P  /   ( *  R )
     *   GaussIntegration ( Rd_rFGIntegrand );
   }

   Out_Ptr -> conved . Rd_r   =   1 ;
}

/****************************************************************
 ****/
void
ConvTt_ra ( InputStruct   *   In_Ptr ,   OutStruct   *   Out_Ptr )
{
   short        irc ,  ia ;
   double       rc ,  P  =   In_Ptr -> beam . P ,  R  =   In_Ptr -> beam . R ;
   double       b_max  =   ( In_Ptr -> nr  -   1 )   *   In_Ptr -> dr ;

  puts ( "The convolution may take a little while. Wait..." );
   for   ( irc  =   0 ;  irc  <   In_Ptr -> nrc ;  irc ++ )   {
    rc  =   ( irc  +   0.5 )   *   In_Ptr -> drc ;
     ConvVar . =  rc ;
     ConvVar . tree  =  NULL ;      /* init the tree. */
     for   ( ia  =   0 ;  ia  <   In_Ptr -> na ;  ia ++ )   {
       ConvVar . ia  =  ia ;
       if   ( In_Ptr -> beam . type  ==  flat )
     Out_Ptr -> Tt_rac [ irc ][ ia ]   =   2   *  P  /   ( *  R )
       *   FlatIntegration ( Tt_raFGIntegrand );
       else            /* Gaussian. */
     Out_Ptr -> Tt_rac [ irc ][ ia ]   =   4   *  P  /   ( *  R )
       *   GaussIntegration ( Tt_raFGIntegrand );
     }
     FreeTree ( ConvVar . tree );
   }

   Out_Ptr -> conved . Tt_ra   =   1 ;
}

/****************************************************************
 ****/
void
ConvTt_r ( InputStruct   *   In_Ptr ,   OutStruct   *   Out_Ptr )
{
   short        irc ;
   double       rc ,  P  =   In_Ptr -> beam . P ,  R  =   In_Ptr -> beam . R ;
   double       b_max  =   ( In_Ptr -> nr  -   1 )   *   In_Ptr -> dr ;

   for   ( irc  =   0 ;  irc  <   In_Ptr -> nrc ;  irc ++ )   {
    rc  =   ( irc  +   0.5 )   *   In_Ptr -> drc ;
     ConvVar . =  rc ;
     if   ( In_Ptr -> beam . type  ==  flat )
       Out_Ptr -> Tt_rc [ irc ]   =   2   *  P  /   ( *  R )
     *   FlatIntegration ( Tt_rFGIntegrand );
     else              /* Gaussian. */
       Out_Ptr -> Tt_rc [ irc ]   =   4   *  P  /   ( *  R )
     *   GaussIntegration ( Tt_rFGIntegrand );
   }

   Out_Ptr -> conved . Tt_r   =   1 ;
}

/****************************************************************
 ****/
void
ShowOutConvMenu ( char   * in_fname )
{
  printf ( "Arz = absorption vs r & z [J/cm3]\n" );
  printf ( "Frz = fluence vs r & z [J/cm2]\n" );

  printf ( "Rr  = diffuse reflectance vs radius r [J/cm2]\n" );
  printf ( "Rra = diffuse reflectance vs radius and angle [J/(cm2 sr)]\n" );

  printf ( "Tr  = transmittance vs radius r [J/cm2]\n" );
  printf ( "Tra = transmittance vs radius and angle [J/(cm2 sr)]\n" );

  printf ( "Q   = Quit to main menu\n" );
  printf ( "* input filename: %s \n" ,  in_fname );
}


/****************************************************************
 *  3 numbers each line: r, z, A[r][z].
 ****/
void
WriteA_rzc ( InputStruct   *   In_Ptr ,
        double   ** A_rzc )
{
  FILE        * file ;
   short        ir ,  iz ,  nr  =   In_Ptr -> nr ,  nz  =   In_Ptr -> nz ;
   double       r ,  z ,  dr  =   In_Ptr -> dr ,  dz  =   In_Ptr -> dz ;
   char         fname [ STRLEN ];

#if  IBMPC
  strcpy ( fname ,   "Arz" );
#else
  strcpy ( fname ,   "Arzc" );
#endif
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-12s\t%-s[J/cm3]\n" ,
       "r[cm]" ,   "z[cm]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
     for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
      z  =   ( iz  +   0.5 )   *  dz ;
      fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,
          r ,  z ,   A_rzc [ ir ][ iz ]);
     }
   }

  fclose ( file );
}

/****************************************************************
 *  3 numbers each line: r, z, F[r][z].
 ****/
void
WriteF_rzc ( InputStruct   *   In_Ptr ,
        double   ** A_rzc )
{
  FILE        * file ;
   short        ir ,  iz ,  nr  =   In_Ptr -> nr ,  nz  =   In_Ptr -> nz ;
   double       mua ,  r ,  z ,  dr  =   In_Ptr -> dr ,  dz  =   In_Ptr -> dz ;
   char         fname [ STRLEN ];

#if  IBMPC
  strcpy ( fname ,   "Frz" );
#else
  strcpy ( fname ,   "Frzc" );
#endif
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-12s\t%-s[J/cm3]\n" ,
       "r[cm]" ,   "z[cm]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
     for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
      z  =   ( iz  +   0.5 )   *  dz ;
      mua  =   In_Ptr -> layerspecs [ IzToLayer ( iz ,   In_Ptr )]. mua ;
       if   ( mua  >   0.0 )
    fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,
        r ,  z ,   A_rzc [ ir ][ iz ]   /  mua );
       else
    fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,  r ,  z ,   0.0 );
     }
   }

  fclose ( file );
}

/****************************************************************
 *  3 numbers each line: r, a, Rd[r][a].
 ****/
void
WriteRd_rac ( InputStruct   *   In_Ptr ,
         double   ** Rd_rac )
{
   short        ir ,  ia ,  nr  =   In_Ptr -> nr ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  dr  =   In_Ptr -> dr ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

#if  IBMPC
  strcpy ( fname ,   "Rra" );
#else
  strcpy ( fname ,   "Rrac" );
#endif
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-12s\t%-s[J/(cm2sr)]\n" ,
       "r[cm]" ,   "a[rad]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
     for   ( ia  =   0 ;  ia  <  na ;  ia ++ )   {
      a  =   ( ia  +   0.5 )   *  da ;
      fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,
          r ,  a ,   Rd_rac [ ir ][ ia ]);
     }
   }

  fclose ( file );
}

/****
 *  2 numbers each line: r, Rd[r]
 ****/
void
WriteRd_rc ( InputStruct   *   In_Ptr ,
        double   * Rd_rc )
{
   short        ir ,  nr  =   In_Ptr -> nr ;
   double       dr  =   In_Ptr -> dr ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Rrc" );
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-s[J/cm2]\n" ,   "r[cm]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( ir  +   0.5 )   *  dr ,   Rd_rc [ ir ]);

  fclose ( file );
}

/****************************************************************
 *  3 numbers each line:r, a, Tt[r][a]. a = theta.
 ****/
void
WriteTt_rac ( InputStruct   *   In_Ptr ,
         double   ** Tt_rac )
{
   short        ir ,  ia ,  nr  =   In_Ptr -> nr ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  dr  =   In_Ptr -> dr ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

#if  IBMPC
  strcpy ( fname ,   "Tra" );
#else
  strcpy ( fname ,   "Trac" );
#endif
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-12s\t%-s[J/(cm2sr)]\n" ,
       "r[cm]" ,   "a[rad]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
     for   ( ia  =   0 ;  ia  <  na ;  ia ++ )   {
      a  =   ( ia  +   0.5 )   *  da ;
      fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,
          r ,  a ,   Tt_rac [ ir ][ ia ]);
     }
   }

  fclose ( file );
}

/****
 *  2 numbers each line: r, Tt[r].
 ****/
void
WriteTt_rc ( InputStruct   *   In_Ptr ,
        double   * Tt_rc )
{
   short        ir ,  nr  =   In_Ptr -> nr ;
   double       dr  =   In_Ptr -> dr ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Trc" );
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-s[J/cm2]\n" ,   "r[cm]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( ir  +   0.5 )   *  dr ,   Tt_rc [ ir ]);

  fclose ( file );
}

/****************************************************************
 ****/
void
BranchOutConvA ( char   * Cmd_Str ,
            InputStruct   *   In_Ptr ,
            OutStruct   *   Out_Ptr )
{
   switch   ( toupper ( Cmd_Str [ 1 ]))   {
     case   'R' :
     if   ( toupper ( Cmd_Str [ 2 ])   ==   'Z' )   {     /* A_rzc. */
       if   ( ! Out_Ptr -> conved . A_rz )
     ConvA_rz ( In_Ptr ,   Out_Ptr );
       WriteA_rzc ( In_Ptr ,   Out_Ptr -> A_rzc );
     }   else
      puts ( "...Wrong command" );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void
BranchOutConvF ( char   * Cmd_Str ,
            InputStruct   *   In_Ptr ,
            OutStruct   *   Out_Ptr )
{
   switch   ( toupper ( Cmd_Str [ 1 ]))   {
     case   'R' :
     if   ( toupper ( Cmd_Str [ 2 ])   ==   'Z' )   {     /* F_rzc. */
       if   ( ! Out_Ptr -> conved . A_rz )
     ConvA_rz ( In_Ptr ,   Out_Ptr );
       WriteF_rzc ( In_Ptr ,   Out_Ptr -> A_rzc );
     }   else
      puts ( "...Wrong command" );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void
BranchOutConvR ( char   * Cmd_Str ,
            InputStruct   *   In_Ptr ,
            OutStruct   *   Out_Ptr )
{
   char         ch ;

   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'R' :
    ch  =  toupper ( Cmd_Str [ 2 ]);
     if   ( ch  ==   '\0' )   {         /* Rd_rc. */
       if   ( ! Out_Ptr -> conved . Rd_r )
     ConvRd_r ( In_Ptr ,   Out_Ptr );
       WriteRd_rc ( In_Ptr ,   Out_Ptr -> Rd_rc );
     }   else   if   ( ch  ==   'A' )   {   /* Rd_rac. */
       if   ( ! Out_Ptr -> conved . Rd_ra )
     ConvRd_ra ( In_Ptr ,   Out_Ptr );
       WriteRd_rac ( In_Ptr ,   Out_Ptr -> Rd_rac );
     }   else
      puts ( "...Wrong command" );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void
BranchOutConvT ( char   * Cmd_Str ,
            InputStruct   *   In_Ptr ,
            OutStruct   *   Out_Ptr )
{
   char         ch ;

   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'R' :
    ch  =  toupper ( Cmd_Str [ 2 ]);
     if   ( ch  ==   '\0' )   {         /* Tt_rc. */
       if   ( ! Out_Ptr -> conved . Tt_r )
     ConvTt_r ( In_Ptr ,   Out_Ptr );
       WriteTt_rc ( In_Ptr ,   Out_Ptr -> Tt_rc );
     }   else   if   ( ch  ==   'A' )   {   /* Tt_rac. */
       if   ( ! Out_Ptr -> conved . Tt_ra )
     ConvTt_ra ( In_Ptr ,   Out_Ptr );
       WriteTt_rac ( In_Ptr ,   Out_Ptr -> Tt_rac );
     }   else
      puts ( "...Wrong command" );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 *  The global ConvVar is used.
 ****/
void
BranchOutConvCmd ( char   * Cmd_Str ,
          InputStruct   *   In_Ptr ,
          OutStruct   *   Out_Ptr )
{
   char         ch ;

   ConvVar . in_ptr  =   In_Ptr ;
   ConvVar . out_ptr  =   Out_Ptr ;

   switch   ( toupper ( Cmd_Str [ 0 ]))   {
   case   'A' :
     BranchOutConvA ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'F' :
     BranchOutConvF ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'R' :
     BranchOutConvR ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'T' :
     BranchOutConvT ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'H' :
     ShowOutConvMenu ( In_Ptr -> in_fname );
     break ;
   case   'Q' :
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void
OutputConvData ( InputStruct   *   In_Ptr ,
            OutStruct   *   Out_Ptr )
{
   char         cmd_str [ STRLEN ];

   if   ( ! Out_Ptr -> allocated )
    puts ( "...No data to output" );
   else   if   ( In_Ptr -> beam . type  ==  pencil )
    puts ( "...No incident beam specified" );
   else
     do   {
      printf ( "\n> Output convolved data (h for help) => " );
       do
    gets ( cmd_str );
       while   ( ! strlen ( cmd_str ));   /* avoid null string. */
       BranchOutConvCmd ( cmd_str ,   In_Ptr ,   Out_Ptr );
     }   while   ( toupper ( cmd_str [ 0 ])   !=   'Q' );
}

/****************************Contours***************************/
/****************************************************************
 *  Absorption density to fluence. A = F/mua;
 ****/
void
A2Fconv ( InputStruct   *   In_Ptr ,   double   ** A_rz )
{
   short        nz  =   In_Ptr -> nz ,  nrc  =   In_Ptr -> nrc ;
   short        ir ,  iz ;
   double       mua ;

   for   ( ir  =   0 ;  ir  <  nrc ;  ir ++ )
     for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
      mua  =   In_Ptr -> layerspecs [ IzToLayer ( iz ,   In_Ptr )]. mua ;
       if   ( mua  >   0.0 )
     A_rz [ ir ][ iz ]   /=  mua ;
     }
}

/****************************************************************
 *  Fluence to absorption density. F = A*mua;
 ****/
void
F2Aconv ( InputStruct   *   In_Ptr ,   double   ** A_rz )
{
   short        nz  =   In_Ptr -> nz ,  nrc  =   In_Ptr -> nrc ;
   short        ir ,  iz ;
   double       mua ;

   for   ( ir  =   0 ;  ir  <  nrc ;  ir ++ )
     for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
      mua  =   In_Ptr -> layerspecs [ IzToLayer ( iz ,   In_Ptr )]. mua ;
       if   ( mua  >   0.0 )
     A_rz [ ir ][ iz ]   *=  mua ;
     }
}

/****************************************************************
 ****/
void
ShowContConvMenu ( char   * in_fname )
{
  printf ( "A = absorption vs r & z [J/cm3]\n" );
  printf ( "F = fluence vs r & z [J/cm2]\n" );
  printf ( "R = diffuse reflectance vs radius and angle [J/(cm2 sr)]\n" );
  printf ( "T = transmittance vs radius and angle [J/(cm2 sr)]\n" );
  printf ( "Q   = Quit to main menu\n" );
  printf ( "* input filename: %s \n" ,  in_fname );
}

/****************************************************************
 ****/
void
BranchContConvCmd ( char   * Cmd_Str ,
           InputStruct   *   In_Ptr ,
           OutStruct   *   Out_Ptr )
{
   char         ch ;

   ConvVar . in_ptr  =   In_Ptr ;
   ConvVar . out_ptr  =   Out_Ptr ;

   switch   ( toupper ( Cmd_Str [ 0 ]))   {
   case   'A' :
     if   ( ! Out_Ptr -> conved . A_rz )
       ConvA_rz ( In_Ptr ,   Out_Ptr );
     IsoPlot ( Out_Ptr -> A_rzc ,   In_Ptr -> nrc  -   1 ,   In_Ptr -> nz  -   1 ,
         In_Ptr -> drc ,   In_Ptr -> dz );
     break ;
   case   'F' :
     if   ( ! Out_Ptr -> conved . A_rz )
       ConvA_rz ( In_Ptr ,   Out_Ptr );
     A2Fconv ( In_Ptr ,   Out_Ptr -> A_rzc );
     IsoPlot ( Out_Ptr -> A_rzc ,   In_Ptr -> nrc  -   1 ,   In_Ptr -> nz  -   1 ,
         In_Ptr -> drc ,   In_Ptr -> dz );
     F2Aconv ( In_Ptr ,   Out_Ptr -> A_rzc );
     break ;
   case   'R' :
     if   ( ! Out_Ptr -> conved . Rd_ra )
       ConvRd_ra ( In_Ptr ,   Out_Ptr );
     IsoPlot ( Out_Ptr -> Rd_rac ,   In_Ptr -> nrc  -   1 ,   In_Ptr -> na  -   1 ,
         In_Ptr -> drc ,   In_Ptr -> da );
     break ;
   case   'T' :
     if   ( ! Out_Ptr -> conved . Tt_ra )
       ConvTt_ra ( In_Ptr ,   Out_Ptr );
     IsoPlot ( Out_Ptr -> Tt_rac ,   In_Ptr -> nrc  -   1 ,   In_Ptr -> na  -   1 ,
         In_Ptr -> drc ,   In_Ptr -> da );
     break ;
   case   'H' :
     ShowContConvMenu ( In_Ptr -> in_fname );
     break ;
   case   'Q' :
     break ;
   default :
    puts ( "...Wrong command" );
   }
}
/****************************************************************
 ****/
void
ContourConvData ( InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   char         cmd_str [ STRLEN ];

   if   ( ! Out_Ptr -> allocated )
    puts ( "...No data to output" );
   else   if   ( In_Ptr -> beam . type  ==  pencil )
    puts ( "...No incident beam specified" );
   else
     do   {
      printf ( "\n> Contour output of convolved data (h for help) => " );
       do
    gets ( cmd_str );
       while   ( ! strlen ( cmd_str ));   /* avoid null string. */
       BranchContConvCmd ( cmd_str ,   In_Ptr ,   Out_Ptr );
     }   while   ( toupper ( cmd_str [ 0 ])   !=   'Q' );
}


/****************************Scanning***************************/
/****************************************************************
 ****/
void
ShowScanConvMenu ( char   * in_fname )
{
  printf ( "Ar = absorption vs r @ fixed z [J/cm3]\n" );
  printf ( "Az = absorption vs z @ fixed r [J/cm3]\n" );
  printf ( "Fr = fluence vs r @ fixed z [J/cm2]\n" );
  printf ( "Fz = fluence vs z @ fixed r [J/cm2]\n" );
  printf ( "Rr = diffuse reflectance vs r @ fixed angle [J/(cm2 sr)]\n" );
  printf ( "Ra = diffuse reflectance vs angle @ fixed r [J/(cm2 sr)]\n" );
  printf ( "Tr = transmittance vs r @ fixed angle [J/(cm2 sr)]\n" );
  printf ( "Ta = transmittance vs angle @ fixed r [J/(cm2 sr)]\n" );
  printf ( "Q  = quit\n" );
  printf ( "* input filename: %s \n" ,  in_fname );
}

/****************************************************************
 *  Ext is either "Ars" or "Frs".
 ****/
void
ScanConvA_r ( char   * Ext ,   InputStruct   *   In_Ptr ,   double   ** A_rzc )
{
   short        irc ,  iz ,  nrc  =   In_Ptr -> nrc ,  nz  =   In_Ptr -> nz ;
   double       r ,  z ,  drc  =   In_Ptr -> drc ,  dz  =   In_Ptr -> dz ;
  FILE        * file ;

  file  =   GetWriteFile ( Ext );
   if   ( file  ==  NULL )
     return ;

  printf ( "z grid separation is %-10.4lg cm.\n" ,  dz );
  printf ( "Input fixed z index (0 - %2hd): " ,  nz  -   1 );
  iz  =   GetShort ( 0 ,  nz  -   1 );
  fprintf ( file ,   "%-12s\t%-s@z=%-9.3lg\n" ,   "r[cm]" ,   Ext ,  dz  *   ( iz  +   0.5 ));
   for   ( irc  =   0 ;  irc  <  nrc ;  irc ++ )   {
    r  =   ( irc  +   0.5 )   *  drc ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  r ,   A_rzc [ irc ][ iz ]);
   }

  fclose ( file );
}

/****************************************************************
 *  Ext is either "Azs" or "Fzs".
 ****/
void
ScanConvA_z ( char   * Ext ,   InputStruct   *   In_Ptr ,   double   ** A_rzc )
{
   short        irc ,  iz ,  nrc  =   In_Ptr -> nrc ,  nz  =   In_Ptr -> nz ;
   double       r ,  z ,  drc  =   In_Ptr -> drc ,  dz  =   In_Ptr -> dz ;
  FILE        * file ;

  file  =   GetWriteFile ( Ext );
   if   ( file  ==  NULL )
     return ;

  printf ( "r grid separation is %-10.4lg cm.\n" ,  drc );
  printf ( "Input fixed r index (0 - %2hd): " ,  nrc  -   1 );
  irc  =   GetShort ( 0 ,  nrc  -   1 );
  fprintf ( file ,   "%-12s\t%-s@r=%-9.3lg\n" ,   "z[cm]" ,   Ext ,  drc  *   ( irc  +   0.5 ));
   for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
    z  =   ( iz  +   0.5 )   *  dz ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  z ,   A_rzc [ irc ][ iz ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void
ScanConvRd_r ( InputStruct   *   In_Ptr ,   double   ** Rd_rac )
{
   short        irc ,  ia ,  nrc  =   In_Ptr -> nrc ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  drc  =   In_Ptr -> drc ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

#if  IBMPC
  strcpy ( fname ,   "Rrs" );
#else
  strcpy ( fname ,   "Rrsc" );
#endif
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  printf ( "Angle grid separation is %-10.4lg rad.\n" ,  da );
  printf ( "Input fixed angle index (0 - %2hd): " ,  na  -   1 );
  ia  =   GetShort ( 0 ,  na  -   1 );

  fprintf ( file ,   "%-12s\t%-s@a=%-9.3lg\n" ,   "r[cm]" ,  fname ,  da  *   ( ia  +   0.5 ));
   for   ( irc  =   0 ;  irc  <  nrc ;  irc ++ )   {
    r  =   ( irc  +   0.5 )   *  drc ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  r ,   Rd_rac [ irc ][ ia ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void
ScanConvRd_a ( InputStruct   *   In_Ptr ,   double   ** Rd_rac )
{
   short        irc ,  ia ,  nrc  =   In_Ptr -> nrc ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  drc  =   In_Ptr -> drc ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

#if  IBMPC
  strcpy ( fname ,   "Ras" );
#else
  strcpy ( fname ,   "Rasc" );
#endif
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  printf ( "r grid separation is %-10.4lg cm.\n" ,  drc );
  printf ( "Input fixed r index (0 - %2hd): " ,  nrc  -   1 );
  irc  =   GetShort ( 0 ,  nrc  -   1 );

  fprintf ( file ,   "%-12s\t%-s@r=%-9.3lg\n" ,   "a[rad]" ,  fname ,  drc  *   ( irc  +   0.5 ));
   for   ( ia  =   0 ;  ia  <  na ;  ia ++ )   {
    a  =   ( ia  +   0.5 )   *  da ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  a ,   Rd_rac [ irc ][ ia ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void
ScanConvTt_r ( InputStruct   *   In_Ptr ,   double   ** Tt_rac )
{
   short        irc ,  ia ,  nrc  =   In_Ptr -> nrc ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  drc  =   In_Ptr -> drc ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

#if  IBMPC
  strcpy ( fname ,   "Trs" );
#else
  strcpy ( fname ,   "Trsc" );
#endif
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  printf ( "Angle grid separation is %-10.4lg rad.\n" ,  da );
  printf ( "Input fixed angle index (0 - %2hd): " ,  na  -   1 );
  ia  =   GetShort ( 0 ,  na  -   1 );

  fprintf ( file ,   "%-12s\t%-s@a=%-9.3lg\n" ,   "r[cm]" ,  fname ,  da  *   ( ia  +   0.5 ));
   for   ( irc  =   0 ;  irc  <  nrc ;  irc ++ )   {
    r  =   ( irc  +   0.5 )   *  drc ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  r ,   Tt_rac [ irc ][ ia ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void
ScanConvTt_a ( InputStruct   *   In_Ptr ,   double   ** Tt_rac )
{
   short        irc ,  ia ,  nrc  =   In_Ptr -> nrc ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  drc  =   In_Ptr -> drc ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

#if  IBMPC
  strcpy ( fname ,   "Tas" );
#else
  strcpy ( fname ,   "Tasc" );
#endif
   if   (( file  =   GetWriteFile ( fname ))   ==  NULL )
     return ;

  printf ( "r grid separation is %-10.4lg cm.\n" ,  drc );
  printf ( "Input fixed r index (0 - %2hd): " ,  nrc  -   1 );
  irc  =   GetShort ( 0 ,  nrc  -   1 );

  fprintf ( file ,   "%-12s\t%-s@r=%-9.3lg\n" ,   "a[rad]" ,  fname ,  drc  *   ( irc  +   0.5 ));
   for   ( ia  =   0 ;  ia  <  na ;  ia ++ )   {
    a  =   ( ia  +   0.5 )   *  da ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  a ,   Tt_rac [ irc ][ ia ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void
BranchScanConvA ( char   * Cmd_Str ,
         InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   char         fname [ STRLEN ];

   if   ( ! Out_Ptr -> conved . A_rz )
     ConvA_rz ( In_Ptr ,   Out_Ptr );

   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'R' :
#if  IBMPC
    strcpy ( fname ,   "Ars" );
     ScanConvA_r ( fname ,   In_Ptr ,   Out_Ptr -> A_rzc );
#else
    strcpy ( fname ,   "Arsc" );
     ScanConvA_r ( fname ,   In_Ptr ,   Out_Ptr -> A_rzc );
#endif
     break ;
   case   'Z' :
#if  IBMPC
    strcpy ( fname ,   "Azs" );
     ScanConvA_z ( fname ,   In_Ptr ,   Out_Ptr -> A_rzc );
#else
    strcpy ( fname ,   "Azsc" );
     ScanConvA_z ( fname ,   In_Ptr ,   Out_Ptr -> A_rzc );
#endif
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void
BranchScanConvF ( char   * Cmd_Str ,
         InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   char         fname [ STRLEN ];

   if   ( ! Out_Ptr -> conved . A_rz )
     ConvA_rz ( In_Ptr ,   Out_Ptr );
   A2Fconv ( In_Ptr ,   Out_Ptr -> A_rzc );

   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'R' :
#if  IBMPC
    strcpy ( fname ,   "Frs" );
     ScanConvA_r ( fname ,   In_Ptr ,   Out_Ptr -> A_rzc );
#else
    strcpy ( fname ,   "Frsc" );
     ScanConvA_r ( fname ,   In_Ptr ,   Out_Ptr -> A_rzc );
#endif
     break ;
   case   'Z' :
#if  IBMPC
    strcpy ( fname ,   "Fzs" );
     ScanConvA_z ( fname ,   In_Ptr ,   Out_Ptr -> A_rzc );
#else
    strcpy ( fname ,   "Fzsc" );
     ScanConvA_z ( fname ,   In_Ptr ,   Out_Ptr -> A_rzc );
#endif
     break ;
   default :
    puts ( "...Wrong command" );
   }

   F2Aconv ( In_Ptr ,   Out_Ptr -> A_rzc );
}

/****************************************************************
 ****/
void
BranchScanConvR ( char   * Cmd_Str ,
         InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   if   ( ! Out_Ptr -> conved . Rd_ra )
     ConvRd_ra ( In_Ptr ,   Out_Ptr );
   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'R' :
     ScanConvRd_r ( In_Ptr ,   Out_Ptr -> Rd_rac );
     break ;
   case   'A' :
     ScanConvRd_a ( In_Ptr ,   Out_Ptr -> Rd_rac );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void
BranchScanConvT ( char   * Cmd_Str ,
         InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   if   ( ! Out_Ptr -> conved . Tt_ra )
     ConvTt_ra ( In_Ptr ,   Out_Ptr );
   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'R' :
     ScanConvTt_r ( In_Ptr ,   Out_Ptr -> Tt_rac );
     break ;
   case   'A' :
     ScanConvTt_a ( In_Ptr ,   Out_Ptr -> Tt_rac );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void
BranchScanConvCmd ( char   * Cmd_Str ,
           InputStruct   *   In_Ptr ,
           OutStruct   *   Out_Ptr )
{
   char         ch ;

   ConvVar . in_ptr  =   In_Ptr ;
   ConvVar . out_ptr  =   Out_Ptr ;

   switch   ( toupper ( Cmd_Str [ 0 ]))   {
   case   'A' :
     BranchScanConvA ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'F' :
     BranchScanConvF ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'R' :
     BranchScanConvR ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'T' :
     BranchScanConvT ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'H' :
     ShowScanConvMenu ( In_Ptr -> in_fname );
     break ;
   case   'Q' :
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void
ScanConvData ( InputStruct   *   In_Ptr ,
          OutStruct   *   Out_Ptr )
{
   char         cmd_str [ STRLEN ];

   if   ( ! Out_Ptr -> allocated )
    puts ( "...No data to output" );
   else   if   ( In_Ptr -> beam . type  ==  pencil )
    puts ( "...No incident beam specified" );
   else
     do   {
      printf ( "\n> Scans of convolved data (h for help) => " );
       do
    gets ( cmd_str );
       while   ( ! strlen ( cmd_str ));   /* avoid null string. */
       BranchScanConvCmd ( cmd_str ,   In_Ptr ,   Out_Ptr );
     }   while   ( toupper ( cmd_str [ 0 ])   !=   'Q' );
}

__MACOSX/FTP-Uploads/ch04Convolution/._CONVCONV.C

FTP-Uploads/ch04Convolution/CONVI.C

FTP-Uploads/ch04Convolution/CONVI.C

/****************************************************************
 *  Functions for reading files.
 ****/

#include   "conv.h"

/****************************************************************
 *  Get the filename and open it for reading, retry until the
 *  file can be opened, or a '.' is input.
 ****/
FILE        *
GetFile ( char   * Fname )
{
  FILE        * file  =  NULL ;

   do   {
    printf ( "Input filename of mcml output(or . to quit): " );
    scanf ( "%s" ,   Fname );
     if   ( strlen ( Fname )   ==   1   &&   Fname [ 0 ]   ==   '.' )
       break ;

    file  =  fopen ( Fname ,   "r" );
   }   while   ( file  ==  NULL );

   return   ( file );
}

/****************************************************************
 *  Allocate the arrays for the original data from mcml
 *  computation in OutStruct for each run, and they are
 *  automatically initialized to zeros.
 *
 *  Returns 1 if successful, otherwise 0.
 ****/
Boolean  
AllocOrigData ( InputStruct   *   In_Ptr ,
           OutStruct   *   Out_Ptr )
{
   short        nz  =   In_Ptr -> nz ;
   short        nr  =   In_Ptr -> nr ;
   short        na  =   In_Ptr -> na ;
   short        nl  =   In_Ptr -> num_layers ;    /* remember +2 for ambient. */

   if   ( nz  <=   0   ||  nr  <=   0   ||  na  <=   0   ||  nl  <=   0 )   {
    printf ( "Wrong grid parameters.\n" );
     return   ( 0 );
   }
   /* Allocate the arrays and the matrices. */
   Out_Ptr -> Rd_ra   =   AllocMatrix ( 0 ,  nr  -   1 ,   0 ,  na  -   1 );
   Out_Ptr -> Rd_r   =   AllocVector ( 0 ,  nr  -   1 );
   Out_Ptr -> Rd_a   =   AllocVector ( 0 ,  na  -   1 );

   Out_Ptr -> A_rz   =   AllocMatrix ( 0 ,  nr  -   1 ,   0 ,  nz  -   1 );
   Out_Ptr -> A_z   =   AllocVector ( 0 ,  nz  -   1 );
   Out_Ptr -> A_l   =   AllocVector ( 0 ,  nl  +   1 );

   Out_Ptr -> Tt_ra   =   AllocMatrix ( 0 ,  nr  -   1 ,   0 ,  na  -   1 );
   Out_Ptr -> Tt_r   =   AllocVector ( 0 ,  nr  -   1 );
   Out_Ptr -> Tt_a   =   AllocVector ( 0 ,  na  -   1 );

   return   ( 1 );
}

/****************************************************************
 *  Allocate the arrays for convolution in OutStruct for each
 *  run, and they are automatically initialized to zeros.
 *
 *  Returns 1 if successful, otherwise 0.
 ****/
Boolean  
AllocConvData ( InputStruct   *   In_Ptr ,
           OutStruct   *   Out_Ptr )
{
   short        nz  =   In_Ptr -> nz ;
   short        nr  =   In_Ptr -> nrc ;   /* use nrc instead of nr. */
   short        na  =   In_Ptr -> na ;

   if   ( nz  <=   0   ||  nr  <=   0   ||  na  <=   0 )   {
    printf ( "Wrong grid parameters.\n" );
     return   ( 0 );
   }
   Out_Ptr -> Rd_rac   =   AllocMatrix ( 0 ,  nr  -   1 ,   0 ,  na  -   1 );
   Out_Ptr -> Rd_rc   =   AllocVector ( 0 ,  nr  -   1 );
   Out_Ptr -> A_rzc   =   AllocMatrix ( 0 ,  nr  -   1 ,   0 ,  nz  -   1 );
   Out_Ptr -> Tt_rac   =   AllocMatrix ( 0 ,  nr  -   1 ,   0 ,  na  -   1 );
   Out_Ptr -> Tt_rc   =   AllocVector ( 0 ,  nr  -   1 );

   return   ( 1 );
}

/****************************************************************
 *  Free the arrays in OutStruct.
 ****/
void  
FreeOrigData ( InputStruct   *   In_Ptr ,
          OutStruct   *   Out_Ptr )
{
   short        nz  =   In_Ptr -> nz ;
   short        nr  =   In_Ptr -> nr ;
   short        na  =   In_Ptr -> na ;
   short        nl  =   In_Ptr -> num_layers ;    /* remember +2 for ambient. */

   if   ( nz  <=   0   ||  nr  <=   0   ||  na  <=   0   ||  nl  <=   0 )   {
    printf ( "Wrong grid parameters.\n" );
     return ;
   }
   if   ( Out_Ptr -> Rd_ra   !=  NULL )
     FreeMatrix ( Out_Ptr -> Rd_ra ,   0 ,  nr  -   1 ,   0 ,  na  -   1 );
   if   ( Out_Ptr -> Rd_r   !=  NULL )
     FreeVector ( Out_Ptr -> Rd_r ,   0 ,  nr  -   1 );
   if   ( Out_Ptr -> Rd_a   !=  NULL )
     FreeVector ( Out_Ptr -> Rd_a ,   0 ,  na  -   1 );

   if   ( Out_Ptr -> A_rz   !=  NULL )
     FreeMatrix ( Out_Ptr -> A_rz ,   0 ,  nr  -   1 ,   0 ,  nz  -   1 );
   if   ( Out_Ptr -> A_z   !=  NULL )
     FreeVector ( Out_Ptr -> A_z ,   0 ,  nz  -   1 );
   if   ( Out_Ptr -> A_l   !=  NULL )
     FreeVector ( Out_Ptr -> A_l ,   0 ,  nl  +   1 );

   if   ( Out_Ptr -> Tt_ra   !=  NULL )
     FreeMatrix ( Out_Ptr -> Tt_ra ,   0 ,  nr  -   1 ,   0 ,  na  -   1 );
   if   ( Out_Ptr -> Tt_r   !=  NULL )
     FreeVector ( Out_Ptr -> Tt_r ,   0 ,  nr  -   1 );
   if   ( Out_Ptr -> Tt_a   !=  NULL )
     FreeVector ( Out_Ptr -> Tt_a ,   0 ,  na  -   1 );
}

/****************************************************************
 *  Free the arrays in OutStruct.
 ****/
void  
FreeConvData ( InputStruct   *   In_Ptr ,
          OutStruct   *   Out_Ptr )
{
   short        nz  =   In_Ptr -> nz ;
   short        nr  =   In_Ptr -> nrc ;
   short        na  =   In_Ptr -> na ;

   if   ( nz  <=   0   ||  nr  <=   0   ||  na  <=   0 )   {
    printf ( "Wrong grid parameters.\n" );
     return ;
   }
   if   ( Out_Ptr -> Rd_rac   !=  NULL )
     FreeMatrix ( Out_Ptr -> Rd_rac ,   0 ,  nr  -   1 ,   0 ,  na  -   1 );
   if   ( Out_Ptr -> Rd_rc   !=  NULL )
     FreeVector ( Out_Ptr -> Rd_rc ,   0 ,  nr  -   1 );
   if   ( Out_Ptr -> A_rzc   !=  NULL )
     FreeMatrix ( Out_Ptr -> A_rzc ,   0 ,  nr  -   1 ,   0 ,  nz  -   1 );
   if   ( Out_Ptr -> Tt_rac   !=  NULL )
     FreeMatrix ( Out_Ptr -> Tt_rac ,   0 ,  nr  -   1 ,   0 ,  na  -   1 );
   if   ( Out_Ptr -> Tt_rc   !=  NULL )
     FreeVector ( Out_Ptr -> Tt_rc ,   0 ,  nr  -   1 );
}

/****************************************************************
 *  Kill the ith char (counting from 0), push the following chars
 *  forward by one.
 ****/
void  
KillChar ( size_t i ,   char   * Str )
{
  size_t      sl  =  strlen ( Str );

   for   (;  i  <  sl ;  i ++ )
     Str [ i ]   =   Str [ +   1 ];
}

/****************************************************************
 *  Eliminate the chars in a string which are not printing chars
 *  or spaces.
 *
 *  Space include ' ', '\f', '\t' etc.
 *
 *  Return 1 if no nonprinting chars found, otherwise return 0.
 ****/
Boolean  
CheckChar ( char   * Str )
{
   Boolean      found  =   0 ;
  size_t      sl  =  strlen ( Str );
  size_t      i  =   0 ;

   while   ( <  sl )
     if   ( isprint ( Str [ i ])   ||  isspace ( Str [ i ]))
      i ++ ;
     else   {
      found  =   1 ;
       KillChar ( i ,   Str );
      sl -- ;
     }

   return   ( found );

}

/****************************************************************
 *  Return 1 if this line is comment line in which the first
 *  non-space character is "#".
 *
 *  Also return 1 if this line is space line.
 ****/
Boolean  
CommentLine ( char   * Buf )
{
  size_t      spn ,  cspn ;

  spn  =  strspn ( Buf ,   " \t" );
   /* length spanned by space or tab chars. */
  cspn  =  strcspn ( Buf ,   "#\n" );
   /* length before the 1st # or return. */
   if   ( spn  ==  cspn )
     return   ( 1 );
   /* comment line or space line. */
   else
     return   ( 0 );           /* the line has data. */
}

/****************************************************************
 *  Skip space or comment lines and return data line only.
 ****/
char         *
FindDataLine ( FILE  *   File_Ptr )
{
   static   char         buf [ STRLEN ];

   do   {                /* skip space or comment lines. */
     if   ( fgets ( buf ,  STRLEN ,   File_Ptr )   ==  NULL )   {
      printf ( "Incomplete data.\n" );
      exit ( 1 );
     }
     CheckChar ( buf );
   }   while   ( CommentLine ( buf ));

   return   ( buf );
}

/****************************************************************
 *  Look for the key word, which is the 1st word in the line.
 ****/
Boolean  
FoundKeyWord ( char   * LineBuf ,   char   * Key )
{
   char         * sub_str ;
   Boolean      found  =   0 ;

   if   (( sub_str  =  strstr ( LineBuf ,   Key ))   !=  NULL )
     if   ( sub_str  ==   LineBuf )
      found  =   1 ;

   return   ( found );
}

/****************************************************************
 *
 ****/
void  
SeekKey ( FILE  *   File_Ptr ,   char   * Key )
{
   char         buf [ STRLEN ];

   do
    strcpy ( buf ,   FindDataLine ( File_Ptr ));
   while   ( ! FoundKeyWord ( buf ,   Key ));
}

/****************************************************************
 *
 ****/
void  
ReadLayerParm ( FILE  *   File_Ptr ,
           short   Num_Layers ,
           LayerStruct   **   Layers_PP )
{
   char         buf [ STRLEN ];
   short        i  =   0 ;
   double       d ,  n ,  mua ,  mus ,  g ;
   double       z  =   0.0 ;        /* z coordinate of the current layer. */

   /* layer 0 and layer Num_Layers + 1 are for ambient. */
   * Layers_PP   =   ( LayerStruct   * )
    malloc (( unsigned )   ( Num_Layers   +   2 )   *   sizeof ( LayerStruct ));
   if   ( ! ( * Layers_PP ))
    nrerror ( "allocation failure in ReadLayerParm()" );

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%lf" ,   & n );
   ( * Layers_PP )[ i ]. =  n ;
   for   ( =   1 ;  i  <=   Num_Layers ;  i ++ )   {
    strcpy ( buf ,   FindDataLine ( File_Ptr ));
    sscanf ( buf ,   "%lf%lf%lf%lf%lf" ,   & n ,   & mua ,   & mus ,   & g ,   & d );
     ( * Layers_PP )[ i ]. =  n ;
     ( * Layers_PP )[ i ]. mua  =  mua ;
     ( * Layers_PP )[ i ]. mus  =  mus ;
     ( * Layers_PP )[ i ]. =  g ;
     ( * Layers_PP )[ i ]. z0  =  z ;
    z  +=  d ;
     ( * Layers_PP )[ i ]. z1  =  z ;
   }
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%lf" ,   & n );
   ( * Layers_PP )[ i ]. =  n ;
}

/****************************************************************
 *  Read in the input parameters which were used for Monte Carlo
 *  simulations.
 ****/
void  
ReadInParm ( FILE  *   File_Ptr ,   InputStruct   *   In_Ptr )
{
   char         buf [ STRLEN ];

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%c" ,   & ( In_Ptr -> in_fformat ));
   if   ( toupper ( In_Ptr -> in_fformat )   !=   'B' )
     In_Ptr -> in_fformat  =   'A' ;

   /** Find the key word "InParm". */
   do
    strcpy ( buf ,   FindDataLine ( File_Ptr ));
   while   ( ! FoundKeyWord ( buf ,   "InParm" ));

   /** Escape filename & file format. */
   FindDataLine ( File_Ptr );

   /** read in number of photons. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%ld" ,   & In_Ptr -> num_photons );

   /** assign in Wth (critical weight). **/
   In_Ptr -> Wth   =   1E-4 ;

   /** read in dz, dr. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%lf%lf" ,   & In_Ptr -> dz ,   & In_Ptr -> dr );

   /** read in nz, nr, na and compute da. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%hd%hd%hd" ,   & In_Ptr -> nz ,
      & In_Ptr -> nr ,   & In_Ptr -> na );
   In_Ptr -> da  =   0.5   *  PI  /   In_Ptr -> na ;

   /** read in number of layers. **/
  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%hd" ,   & In_Ptr -> num_layers );

   ReadLayerParm ( File_Ptr ,   In_Ptr -> num_layers ,
         & In_Ptr -> layerspecs );
}

/****************************************************************
 *  Read reflectance, absorbed fraction, transmittance.
 ****/
void  
ReadRAT ( FILE  *   File_Ptr ,
     OutStruct   *   Out_Ptr )
{
   char         buf [ STRLEN ];

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> Rsp ));

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> Rd ));

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> A ));

  strcpy ( buf ,   FindDataLine ( File_Ptr ));
  sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> Tt ));
}

/****************************************************************
 ****/
void  
ReadA_layer ( FILE  *   File_Ptr ,
         short   Num_Layers ,
         OutStruct   *   Out_Ptr )
{
   char         buf [ STRLEN ];
   short        i ;

   for   ( =   1 ;  i  <=   Num_Layers ;  i ++ )   {
    strcpy ( buf ,   FindDataLine ( File_Ptr ));
    sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> A_l [ i ]));
   }
}

/****************************************************************
 ****/
void  
ReadA_z ( FILE  *   File_Ptr ,
     short   Nz ,
     OutStruct   *   Out_Ptr )
{
   char         buf [ STRLEN ];
   short        i ;

   for   ( =   0 ;  i  <   Nz ;  i ++ )   {
    strcpy ( buf ,   FindDataLine ( File_Ptr ));
    sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> A_z [ i ]));
   }
}

/****************************************************************
 ****/
void  
ReadRd_r ( FILE  *   File_Ptr ,
      short   Nr ,
      OutStruct   *   Out_Ptr )
{
   char         buf [ STRLEN ];
   short        i ;

   for   ( =   0 ;  i  <   Nr ;  i ++ )   {
    strcpy ( buf ,   FindDataLine ( File_Ptr ));
    sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> Rd_r [ i ]));
   }
}

/****************************************************************
 ****/
void  
ReadRd_a ( FILE  *   File_Ptr ,
      short   Na ,
      OutStruct   *   Out_Ptr )
{
   char         buf [ STRLEN ];
   short        i ;

   for   ( =   0 ;  i  <   Na ;  i ++ )   {
    strcpy ( buf ,   FindDataLine ( File_Ptr ));
    sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> Rd_a [ i ]));
   }
}

/****************************************************************
 ****/
void  
ReadTt_r ( FILE  *   File_Ptr ,
      short   Nr ,
      OutStruct   *   Out_Ptr )
{
   char         buf [ STRLEN ];
   short        i ;

   for   ( =   0 ;  i  <   Nr ;  i ++ )   {
    strcpy ( buf ,   FindDataLine ( File_Ptr ));
    sscanf ( buf ,   "%lf" ,   & ( Out_Ptr -> Tt_r [ i ]));
   }
}

/****************************************************************
 ****/
void  
ReadTt_a ( FILE  *   File_Ptr ,
      short   Na ,
      OutStruct   *   Out_Ptr )
{
   char         buf [ STRLEN ];
   short        i ;

   for   ( =   0 ;  i  <   Na ;  i ++ )   {
     /*
     * strcpy(buf, FindDataLine(File_Ptr)); sscanf(buf, "%lf",
     * &(Out_Ptr->Tt_a[i]));
     */
    fscanf ( File_Ptr ,   "%lf" ,   & ( Out_Ptr -> Tt_a [ i ]));
   }
}

/****************************************************************
 ****/
void  
ReadA_rz ( FILE  *   File_Ptr ,
      short   Nr ,
      short   Nz ,
      OutStruct   *   Out_Ptr )
{
   short        iz ,  ir ;

   for   ( ir  =   0 ;  ir  <   Nr ;  ir ++ )
     for   ( iz  =   0 ;  iz  <   Nz ;  iz ++ )
      fscanf ( File_Ptr ,   "%lf " ,   & ( Out_Ptr -> A_rz [ ir ][ iz ]));
}

/****************************************************************
 ****/
void  
ReadRd_ra ( FILE  *   File_Ptr ,
       short   Nr ,
       short   Na ,
       OutStruct   *   Out_Ptr )
{
   short        ir ,  ia ;

   for   ( ir  =   0 ;  ir  <   Nr ;  ir ++ )
     for   ( ia  =   0 ;  ia  <   Na ;  ia ++ )
      fscanf ( File_Ptr ,   "%lf " ,   & ( Out_Ptr -> Rd_ra [ ir ][ ia ]));
}

/****************************************************************
 ****/
void  
ReadTt_ra ( FILE  *   File_Ptr ,
       short   Nr ,
       short   Na ,
       OutStruct   *   Out_Ptr )
{
   short        ir ,  ia ;

   for   ( ir  =   0 ;  ir  <   Nr ;  ir ++ )
     for   ( ia  =   0 ;  ia  <   Na ;  ia ++ )
      fscanf ( File_Ptr ,   "%lf " ,   & ( Out_Ptr -> Tt_ra [ ir ][ ia ]));
}

/****************************************************************
 *  Read in the Monte Carlo output parameters.
 ****/
void  
ReadOutMC ( FILE  *   File_Ptr ,
       InputStruct   *   In_Ptr ,
       OutStruct   *   Out_Ptr )
{
   ConvStruct   null_conved  =  NULLCONVSTRUCT ;

   SeekKey ( File_Ptr ,   "RAT" );
   ReadRAT ( File_Ptr ,   Out_Ptr );     /* refl.,absorption,transmission. */

   /* 1D arrays. */
   SeekKey ( File_Ptr ,   "A_l" );
   ReadA_layer ( File_Ptr ,   In_Ptr -> num_layers ,   Out_Ptr );

   SeekKey ( File_Ptr ,   "A_z" );
   ReadA_z ( File_Ptr ,   In_Ptr -> nz ,   Out_Ptr );

   SeekKey ( File_Ptr ,   "Rd_r" );
   ReadRd_r ( File_Ptr ,   In_Ptr -> nr ,   Out_Ptr );

   SeekKey ( File_Ptr ,   "Rd_a" );
   ReadRd_a ( File_Ptr ,   In_Ptr -> na ,   Out_Ptr );

   SeekKey ( File_Ptr ,   "Tt_r" );
   ReadTt_r ( File_Ptr ,   In_Ptr -> nr ,   Out_Ptr );

   SeekKey ( File_Ptr ,   "Tt_a" );
   ReadTt_a ( File_Ptr ,   In_Ptr -> na ,   Out_Ptr );

   /* 2D arrays. */
   SeekKey ( File_Ptr ,   "A_rz" );
   ReadA_rz ( File_Ptr ,   In_Ptr -> nr ,   In_Ptr -> nz ,   Out_Ptr );

   SeekKey ( File_Ptr ,   "Rd_ra" );
   ReadRd_ra ( File_Ptr ,   In_Ptr -> nr ,   In_Ptr -> na ,   Out_Ptr );

   SeekKey ( File_Ptr ,   "Tt_ra" );
   ReadTt_ra ( File_Ptr ,   In_Ptr -> nr ,   In_Ptr -> na ,   Out_Ptr );

   Out_Ptr -> conved  =  null_conved ;
}

/****************************************************************
 *  After the Input parameters are read in, the parameters drc
 *  and nrc are initialized to dr and nr respectively.
 ****/
void  
ReadMcoFile ( InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
  FILE        * infile ;

  infile  =   GetFile ( In_Ptr -> in_fname );
   if   ( infile  ==  NULL )
     return ;

   if   ( Out_Ptr -> allocated )   {
     OutStruct    null_out  =  NULLOUTSTRUCT ;
     FreeOrigData ( In_Ptr ,   Out_Ptr );
     * Out_Ptr   =  null_out ;
   }
   ReadInParm ( infile ,   In_Ptr );
   In_Ptr -> beam . type  =  pencil ;
   In_Ptr -> drc  =   In_Ptr -> dr ;
   In_Ptr -> nrc  =   In_Ptr -> nr ;

   AllocOrigData ( In_Ptr ,   Out_Ptr );
   AllocConvData ( In_Ptr ,   Out_Ptr );
   Out_Ptr -> allocated  =   1 ;

   ReadOutMC ( infile ,   In_Ptr ,   Out_Ptr );
  fclose ( infile );
}

__MACOSX/FTP-Uploads/ch04Convolution/._CONVI.C

FTP-Uploads/ch04Convolution/CONVISO.C

FTP-Uploads/ch04Convolution/CONVISO.C

/****
 *  This files plot a series of contour lines for a 2D array of double.
 *  4/14/92.
 ****/

#include   < math . h >
#include   < stdlib . h >
#include   < stdio . h >
#include   < stddef . h >
#include   < time . h >
#include   < string . h >
#include   < ctype . h >

#ifndef  PI
#define  PI  3.1415926
#endif

/****
 *  Return true is val is between Z[i][j] and Z[i+1][j].
 ****/
#define  INTERSECTI ( val ,  Z ,  i ,  j )  \
   ( Z [ i ][ j ]   <=  val  &&  val  <=  Z [ i + 1 ][ j ]   ||  \
   Z [ i ][ j ]   >=  val  &&  val  >=  Z [ i + 1 ][ j ])

/****
 *  Return true is val is between Z[i][j] and Z[i][j+1].
 ****/
#define  INTERSECTJ ( val ,  Z ,  i ,  j )  \
   ( Z [ i ][ j ]   <=  val  &&  val  <=  Z [ i ][ j + 1 ]   ||  \
   Z [ i ][ j ]   >=  val  &&  val  >=  Z [ i ][ j + 1 ])

FILE        * GetWriteFile ( char   * Ext );      /* in conho.c. */

struct  XY  {
   double       x ,  y ;
};

struct   XYpairs   {
   double       x ,  y ;
   struct   XYpairs   * next ;
};

typedef   struct   XYpairs   * PairList ;

/* set up a que for printing the pairs. */
typedef   PairList  QDATA ;

struct   Qlinked_list   {
  QDATA       d ;
   struct   Qlinked_list   * next ;
};

typedef   struct   Qlinked_list  QELEMENT ;
typedef  QELEMENT  * QLINK ;

typedef   struct   {          /* a que of pairs. */
  QLINK       front ,  rear ;
}            QUE ;
/* end of que definition. */


struct   Isolines   {
   double       iso_val ;        /* z value of the contour line. */
   PairList     pairs ;
   struct   Isolines   * next ;
};

typedef   struct   Isolines   * IsoList ;



double  
ZMin ( double   ** Z ,
      long   IXmax ,
      long   IYmax ,
      double   Dx ,
      double   Dy ,
      struct  XY  *   Pmin_Ptr )
{
   double       zmin ;
   long         i ,  j ;

  zmin  =  Z [ 0 ][ 0 ];
   for   ( =   0 ;  i  <   IXmax ;  i ++ )
     for   ( =   0 ;  j  <   IYmax ;  j ++ )
       if   ( Z [ i ][ j ]   <  zmin )   {
    zmin  =  Z [ i ][ j ];
     Pmin_Ptr -> =  i  *   Dx ;
     Pmin_Ptr -> =  j  *   Dy ;
       }
   return   ( zmin );
}


double  
ZMax ( double   ** Z ,
      long   IXmax ,
      long   IYmax ,
      double   Dx ,
      double   Dy ,
      struct  XY  *   Pmax_Ptr )
{
   double       zmax ;
   long         i ,  j ;

  zmax  =  Z [ 0 ][ 0 ];
   for   ( =   0 ;  i  <   IXmax ;  i ++ )
     for   ( =   0 ;  j  <   IYmax ;  j ++ )
       if   ( Z [ i ][ j ]   >  zmax )   {
    zmax  =  Z [ i ][ j ];
     Pmax_Ptr -> =  i  *   Dx ;
     Pmax_Ptr -> =  j  *   Dy ;
       }
   return   ( zmax );
}


/* Get the isovalues from user. */
IsoList  
GetIsoValues ( double   Z_Min ,   double   Z_Max )
{
   IsoList      head ;
   char         in_str [ 256 ];

  printf ( "Input an isovalue or . to stop: " );
  scanf ( "%s" ,  in_str );
   if   ( strlen ( in_str )   ==   1   &&  in_str [ 0 ]   ==   '.' )
     return   ( NULL );

  head  =   ( IsoList )  malloc ( sizeof ( struct   Isolines ));
   if   ( head  ==  NULL )
     return   ( NULL );

   /* get the elements for the node. */
  sscanf ( in_str ,   "%lf" ,   & head -> iso_val );
   if   ( head -> iso_val  <   Z_Min )
    head -> iso_val  =   Z_Min ;
   else   if   ( head -> iso_val  >   Z_Max )
    head -> iso_val  =   Z_Max ;
  head -> pairs  =  NULL ;
  head -> next  =   GetIsoValues ( Z_Min ,   Z_Max );
   return   ( head );
}


/****
 *  The isoposition is between [i][j] & [i+1][j]
 *
 *  Linear interpolation is used to locate the y component of
 *  the iso position.
 ****/
void  
IsoPositionI ( PairList  pair ,
          double  iso_val ,   double   ** Z ,
          long  i ,   long  j ,
          double   Dx ,   double   Dy )
{
  pair -> =   ( +   0.5 )   *   Dy ;
   if   ( Z [ i ][ j ]   !=  Z [ +   1 ][ j ])
    pair -> =   ( +   0.5 )   *   Dx   +   Dx   *   ( iso_val  -  Z [ i ][ j ])   /   ( Z [ +   1 ][ j ]   -  Z [ i ][ j ]);
   else
    pair -> =   ( +   1 )   *   Dx ;   /* take the mid point. */
}


/****
 *  The isoposition is between [i][j] & [i][j+1]
 *
 *  Linear interpolation is used to locate the y component of
 *  the iso position.
 ****/
void  
IsoPositionJ ( PairList  pair ,
          double  iso_val ,   double   ** Z ,
          long  i ,   long  j ,
          double   Dx ,   double   Dy )
{
  pair -> =   ( +   0.5 )   *   Dx ;
   if   ( Z [ i ][ +   1 ]   !=  Z [ i ][ j ])
    pair -> =   ( +   0.5 )   *   Dy   +   Dy   *   ( iso_val  -  Z [ i ][ j ])   /   ( Z [ i ][ +   1 ]   -  Z [ i ][ j ]);
   else
    pair -> =   ( +   1 )   *   Dy ;   /* take the mid point. */
}

void  
IsoPosition ( PairList  pair ,
         double  iso_val ,   double   ** Z ,
         long  i ,   long  j ,
         double   Dx ,   double   Dy )
{

   if   ( INTERSECTI ( iso_val ,  Z ,  i ,  j ))
     IsoPositionI ( pair ,  iso_val ,  Z ,  i ,  j ,   Dx ,   Dy );
   else   if   ( INTERSECTJ ( iso_val ,  Z ,  i ,  j ))
     IsoPositionJ ( pair ,  iso_val ,  Z ,  i ,  j ,   Dx ,   Dy );
}

PairList  
AllocPair ( void )
{
   PairList     pair ;

  pair  =   ( PairList )  malloc ( sizeof ( struct   XYpairs ));
   if   ( pair  ==  NULL )
    puts ( "...malloc error. Contour lines are not complete" );
   return   ( pair );
}

void  
GetAnIsoLine ( IsoList   IsoNode ,   double   ** Z ,
          long   IXmax ,   long   IYmax ,
          double   Dx ,   double   Dy )
{
   long         i ,  j ;
   double       ival  =   IsoNode -> iso_val ;
   PairList     pair_tail ;

   for   ( =   0 ;  j  <   IYmax   -   1 ;  j ++ )
     for   ( =   0 ;  i  <   IXmax   -   2 ;  i ++ )
       if   ( INTERSECTI ( ival ,  Z ,  i ,  j )   ||
      INTERSECTJ ( ival ,  Z ,  i ,  j ))   {    /* found a pair. */
     if   ( IsoNode -> pairs  ==  NULL )   {     /* 1st pair. */
       if   ( ! ( IsoNode -> pairs  =   AllocPair ()))
         return ;
      pair_tail  =   IsoNode -> pairs ;
     }   else   {          /* subsequent  pairs. */
       if   ( ! ( pair_tail -> next  =   AllocPair ()))
         return ;
      pair_tail  =  pair_tail -> next ;
     }
     IsoPosition ( pair_tail ,  ival ,  Z ,  i ,  j ,   Dx ,   Dy );
       }
  pair_tail -> next  =  NULL ;     /* end of the pair list. */
}


/****
 *  Return the quadrant.
 *  For Frz or Arz, the peak point is usually on z axis which is
 *  y here. We want start the isoline from the 4th quadrant.
 *  Therefore, we use -1 for the 4th quadrant instead of 4.
 ****/
short  
GetQuadrant ( double  x ,   double  y )
{
   if   ( >   0   &&  y  >=   0 )
     return   ( 1 );           /* Include +x-axis. */
   else   if   ( <=   0   &&  y  >   0 )
     return   ( 2 );           /* Include +y-axis. */
   else   if   ( <   0   &&  y  <=   0 )
     return   ( 3 );           /* Include -x-axis. */
   else   if   ( >=   0   &&  y  <   0 )
     return   ( - 1 );          /* Include -y-axis. */
}

/****
 *  Compare the angle wrt Pmax.  If the angle of "This" is larger
 *  than that of the "Next", return 1.  Otherwise, return 0.
 *
 *  Although atan2 returns value in the range -pi to pi, we want
 *  to avoid it because it is slow.  We compare the quadrants of
 *  the two points first.  If they are in the same quadrant, we
 *  compare the relative positions.
 ****/
char  
OutOfOrder ( PairList   This ,     /* current pair. */
        PairList   Next ,
        struct  XY  Pmax )
{
   double       x0 ,  y0 ,  x1 ,  y1 ;
   short        q0 ,  q1 ;         /* Quadrants. */
   char         out_order ;

   if   ( This   ==  NULL  ||   Next   ==  NULL )
     return   ( 0 );           /* This shouldn't happen. */
  x0  =   This -> -   Pmax . x ;
  y0  =   This -> -   Pmax . y ;
  q0  =   GetQuadrant ( x0 ,  y0 );
  x1  =   Next -> -   Pmax . x ;
  y1  =   Next -> -   Pmax . y ;
  q1  =   GetQuadrant ( x1 ,  y1 );

   if   ( q0  <  q1 )
    out_order  =   0 ;
   else   if   ( q0  >  q1 )
    out_order  =   1 ;
   else   {              /* In the same quadrant. */
     if   ( y0  *  x1  <  y1  *  x0 )
      out_order  =   0 ;
     else
      out_order  =   1 ;
   }

   return   ( out_order );
}


/****
 *  Sort the isopositions according to the angle with respect to
 *  the position of the maximum value.
 ****/
void  
SortAnIsoLine ( PairList   *   PairHeadPtr ,
           struct  XY  Pmax )
{
   char         sorted  =   0 ;
   PairList      this ,  last ;     /* this=the pair being compared w/ the
                 * next one. */
   PairList     sorted_head  =  NULL ;     /* the head to the sublist of
                     * sorted pairs. */

   while   ( ! sorted  &&  sorted_head  !=   * PairHeadPtr )   {
    sorted  =   1 ;           /* assume sublist is sorted. */
    last  =  NULL ;
     this   =   * PairHeadPtr ;      /* sublist starts at *PairHeadPtr, ends at
                 * sorted_head. */

     while   ( this -> next  !=  sorted_head )   {   /* traverse the sublist. */
       if   ( OutOfOrder ( this ,   this -> next ,   Pmax ))   {   /* swap and move to next. */
    sorted  =   0 ;
     if   ( last  ==  NULL )
       * PairHeadPtr   =   this -> next ;
     else
      last -> next  =   this -> next ;

    last  =   this -> next ;
     this -> next  =  last -> next ;
    last -> next  =   this ;
       }   else   {            /* move to next. */
    last  =   this ;
     this   =   this -> next ;
       }
     }                 /* end of sublist traversing. */

    sorted_head  =   this ;
   }               /* end of big while. */
}

#if   0
/****
 *  Repeat the first pair of the isoline at the end of the list
 *  so that the isoline is a loop.
 *  Sometimes this may not be desired.
 ****/
void  
LoopAnIsoLine ( PairList   *   PairHeadPtr )
{
   PairList     tail ;

  tail  =   * PairHeadPtr ;
   while   ( tail -> next  !=  NULL )
    tail  =  tail -> next ;

  tail -> next  =   ( PairList )  malloc ( sizeof ( struct   XYpairs ));
   if   ( tail -> next  ==  NULL )
     return ;
  tail  =  tail -> next ;
  tail -> =   ( * PairHeadPtr ) -> x ;
  tail -> =   ( * PairHeadPtr ) -> y ;
  tail -> next  =  NULL ;
}
#endif

void  
GetIsoLines ( IsoList  isos ,
         double   ** Z ,
         long   IXmax ,
         long   IYmax ,
         double   Dx ,
         double   Dy ,
         struct  XY  Pmax )
{
   IsoList      node  =  isos ;

   while   ( node  !=  NULL )   {
     GetAnIsoLine ( node ,  Z ,   IXmax ,   IYmax ,   Dx ,   Dy );
     SortAnIsoLine ( & node -> pairs ,   Pmax );
     /* LoopAnIsoLine(&node->pairs); */
    node  =  node -> next ;
   }
}

char  
IsEmpty ( QUE q )
{
   return   ( q . front  ==  NULL );
}


void  
Deque ( QUE  *  q ,  QDATA  *  x )
{
  QLINK       temp  =  q -> front ;

   if   ( ! IsEmpty ( * q ))   {
     * =  temp -> d ;
    q -> front  =  temp -> next ;
    free ( temp );
   }   else
    printf ( "Empty que.\n" );
}

void  
Enque ( QUE  *  q ,  QDATA x )
{
  QLINK       temp ;

  temp  =   ( QLINK )  malloc ( sizeof ( QELEMENT ));
  temp -> =  x ;
  temp -> next  =  NULL ;
   if   ( IsEmpty ( * q ))
    q -> front  =  q -> rear  =  temp ;
   else   {
    q -> rear -> next  =  temp ;
    q -> rear  =  temp ;
   }
}


void  
WriteIsoLines ( FILE  *   Isofile ,
           IsoList   IsoHead )
{
  QUE         qprint  =   { NULL ,  NULL };      /* que to be printed. */
  QUE         qsave  =   { NULL ,  NULL };
  QDATA       p ;          /* QDATA is PairList. */
   char         all_nulls ;

   while   ( IsoHead   !=  NULL )   {
    fprintf ( Isofile ,   "X%-8lG\tY%-8lG\t" ,   IsoHead -> iso_val ,   IsoHead -> iso_val );
     Enque ( & qprint ,   IsoHead -> pairs );
     IsoHead   =   IsoHead -> next ;
   }
  fprintf ( Isofile ,   "\n" );

   do   {
    all_nulls  =   1 ;
     while   ( ! IsEmpty ( qprint ))   {
       Deque ( & qprint ,   & p );
       if   ( !=  NULL )   {
    fprintf ( Isofile ,   "%9.2lE\t%9.2lE\t" ,  p -> x ,  p -> y );
     Enque ( & qsave ,  p -> next );   /* add the next to a new que for a new
                 * row. */
     if   ( all_nulls  ==   1 )
      all_nulls  =   0 ;
       }   else   {
     /* fprintf(Isofile, "%9s\t%9s\t", " ", " "); */
    fprintf ( Isofile ,   "\t\t" );
     Enque ( & qsave ,  p );     /* add a null to the new que. */
       }
     }
    fprintf ( Isofile ,   "\n" );
    qprint  =  qsave ;
    qsave . front  =  NULL ;
    qsave . rear  =  NULL ;
   }   while   ( ! all_nulls );
}

void  
FreePairs ( PairList   Pairs )
{
   PairList     this_pair ;

   while   ( Pairs   !=  NULL )   {
    this_pair  =   Pairs ;
     Pairs   =   Pairs -> next ;
    free ( this_pair );
   }
}

void  
FreeIsoLines ( IsoList   IsoHead )
{
   IsoList      this_iso ;
   PairList     pair_head ;

   while   ( IsoHead   !=  NULL )   {
     FreePairs ( IsoHead -> pairs );
    this_iso  =   IsoHead ;
     IsoHead   =   IsoHead -> next ;
    free ( this_iso );
   }
}


void  
IsoPlot ( double   ** Z ,       /* the 2D array Z[i][j]. */
     long   int   IXmax ,
     long   int   IYmax ,       /* the 0<=i<=IXmax, 0<=j<=IYmax. */
     double   Dx ,
     double   Dy )
{                 /* the gridline separations. */
  FILE        * isofile ;        /* send isolines to this file. */
   struct  XY   pmin ,  pmax ;     /* the xy positions of the min & max. */
   double       zmin ,  zmax ;
   IsoList      isos ;
   char         fname [ 128 ]   =   "iso" ;

   /* Locate the min & max. */
  zmin  =   ZMin ( Z ,   IXmax ,   IYmax ,   Dx ,   Dy ,   & pmin );
  zmax  =   ZMax ( Z ,   IXmax ,   IYmax ,   Dx ,   Dy ,   & pmax );

   if   ( zmin  ==  zmax  ||   IXmax   *   IYmax   <   4 )   {
    printf ( "...Not enough data for contour plot\n" );
     return ;
   }
  isofile  =   GetWriteFile ( fname );
   if   ( isofile  ==  NULL )
     return ;

  printf ( "The range of the value is %lf to %lf.\n" ,  zmin ,  zmax );
  isos  =   GetIsoValues ( zmin ,  zmax );

   GetIsoLines ( isos ,  Z ,   IXmax ,   IYmax ,   Dx ,   Dy ,  pmax );
   WriteIsoLines ( isofile ,  isos );

   FreeIsoLines ( isos );

  fclose ( isofile );
}

__MACOSX/FTP-Uploads/ch04Convolution/._CONVISO.C

FTP-Uploads/ch04Convolution/Convmain.c

/**************************************************************** * Convolution program for Monte Carlo simulation of photon * distribution in multilayered turbid media. * ****/ #include "conv.h" #define EPS 0.1 /* default relative error in convolution. */ void ReadMcoFile(InputStruct *, OutStruct *); void OutputOrigData(InputStruct *, OutStruct *); void OutputConvData(InputStruct *, OutStruct *); void ContourOrigData(InputStruct *, OutStruct *); void ContourConvData(InputStruct *, OutStruct *); void ScanOrigData(InputStruct *, OutStruct *); void ScanConvData(InputStruct *, OutStruct *); void LaserBeam(BeamStruct *, OutStruct *); void ConvResolution(InputStruct *, OutStruct *); void ConvError(InputStruct *, OutStruct *); /**************************************************************** ****/ void ShowMainMenu() { puts("i = Input filename of mcml output"); puts("b = specify laser Beam"); puts("r = convolution Resolution. "); puts("e = convolution Error. "); puts("oo = Output Original data"); puts("oc = Output Convolved data"); puts("co = Contour output of Original data"); puts("cc = Contour output of Convolved data"); puts("so = Scanning output of Original data"); puts("sc = Scanning output of Convolved data"); puts("q = Quit"); puts("* Commands in conv are not case-sensitive"); } /**************************************************************** ****/ void QuitProgram(InputStruct * In_Ptr, OutStruct * Out_Ptr) { char ch, cmd_str[STRLEN]; printf("Do you really want to quit conv (y/n): "); gets(cmd_str); sscanf(cmd_str, "%c", &ch); if (toupper(ch) == 'Y') { /* really quit. */ if (Out_Ptr->allocated) { FreeOrigData(In_Ptr, Out_Ptr); FreeConvData(In_Ptr, Out_Ptr); } exit(0); } } /**************************************************************** ****/ void BranchMainCmd1(char *Cmd, /* Cmd is command char. */ InputStruct * In_Ptr, OutStruct * Out_Ptr) { switch (toupper(Cmd[0])) { case 'I':ReadMcoFile(In_Ptr, Out_Ptr); break; case 'B': LaserBeam(&In_Ptr->beam, Out_Ptr); break; case 'R': ConvResolution(In_Ptr, Out_Ptr); break; case 'E': ConvError(In_Ptr, Out_Ptr); break; case 'H': ShowMainMenu(); break; case 'Q': QuitProgram(In_Ptr, Out_Ptr); break; default: puts("...Wrong command"); } } /**************************************************************** ****/ void BranchMainCmd2(char *Cmd, /* Cmd is command char. */ InputStruct * In_Ptr, OutStruct * Out_Ptr) { switch (toupper(Cmd[0])) { case 'O': if (toupper(Cmd[1]) == 'O') OutputOrigData(In_Ptr, Out_Ptr); else if (toupper(Cmd[1]) == 'C') OutputConvData(In_Ptr, Out_Ptr); break; case 'C': if (toupper(Cmd[1]) == 'O') ContourOrigData(In_Ptr, Out_Ptr); else if (toupper(Cmd[1]) == 'C') ContourConvData(In_Ptr, Out_Ptr); break; case 'S': if (toupper(Cmd[1]) == 'O') ScanOrigData(In_Ptr, Out_Ptr); else if (toupper(Cmd[1]) == 'C') ScanConvData(In_Ptr, Out_Ptr); break; default: puts("...Wrong command"); } } /**************************************************************** ****/ void BranchMainCmd(char *Cmd) { /* Cmd is command char. */ static InputStruct in_parm; static OutStruct out_parm = NULLOUTSTRUCT; static char first_time = 1; /* used to initialize. */ if (first_time) { in_parm.eps = EPS; first_time = 0; } if (strlen(Cmd) == 1) BranchMainCmd1(Cmd, &in_parm, &out_parm); else if (strlen(Cmd) == 2) BranchMainCmd2(Cmd, &in_parm, &out_parm); else puts("...Wrong command"); } /**************************************************************** ****/ int main(void) { char cmd_str[STRLEN]; ShowVersion(); do { printf("\n> Main menu (h for help) => "); do /* get the command input. */ gets(cmd_str); while (!strlen(cmd_str)); BranchMainCmd(cmd_str); } while (1); }

__MACOSX/FTP-Uploads/ch04Convolution/._Convmain.c

FTP-Uploads/ch04Convolution/CONVNR.C

FTP-Uploads/ch04Convolution/CONVNR.C

/***********************************************************
 *  Some routines modified from Numerical Recipes in C,
 *  including error report, array or matrix declaration
 *  and releasing, integrations.
 *
 *  Some frequently used routines are also included here.
 ****/

/*
 * #include <stdlib.h> #include <stdio.h> #include <math.h>
 */
#include   "conv.h"

/***********************************************************
 *  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 );
}

/***********************************************************
 *  Allocate an 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()" );

   for   ( =  nl ;  i  <=  nh ;  i ++ )
    v [ i ]   =   0.0 ;           /* init. */
   return  v  -  nl ;
}

/***********************************************************
 *  Allocate a matrix 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   ( =  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   ( =  nrl ;  i  <=  nrh ;  i ++ )
     for   ( =  ncl ;  j  <=  nch ;  j ++ )
      m [ i ][ j ]   =   0.0 ;
   return  m ;
}

/***********************************************************
 *  Release the memory.
 ****/
void
FreeVector ( double   * v ,   short  nl ,   short  nh )
{
  free (( char   * )   ( +  nl ));
}

/***********************************************************
 *  Release the memory.
 ****/
void
FreeMatrix ( double   ** m ,   short  nrl ,   short  nrh ,
        short  ncl ,   short  nch )
{
   short        i ;

   for   ( =  nrh ;  i  >=  nrl ;  i -- )
    free (( char   * )   ( m [ i ]   +  ncl ));
  free (( char   * )   ( +  nrl ));
}

/***********************************************************
 *  Trapzoidal integration.
 ****/

#define  FUNC ( x )   (( * func )( x ))

float
trapzd (
        float   ( * func )   ( float ),
        float  a ,   float  b ,   int  n )
{
   float        x ,  tnm ,  sum ,  del ;
   static   float  s ;
   static   int   it ;
   int          j ;

   if   ( ==   1 )   {
    it  =   1 ;
    s  =   0.5   *   ( -  a )   *   ( FUNC ( a )   +  FUNC ( b ));
   }   else   {
    tnm  =  it ;
    del  =   ( -  a )   /  tnm ;
    x  =  a  +   0.5   *  del ;
     for   ( sum  =   0.0 ,  j  =   1 ;  j  <=  it ;  j ++ ,  x  +=  del )
      sum  +=  FUNC ( x );
    it  *=   2 ;
    s  =   0.5   *   ( +   ( -  a )   *  sum  /  tnm );
   }
   return   ( s );
}
#undef  FUNC

/***********************************************************
 *  Allow user to change EPS.
 *  Modifed to do at least three trapzd() in case data is
 *  noisy.  9/26/1994 Lihong Wang.
 ****/
#define  JMAX  30
float
qtrap (
       float   ( * func )   ( float ),
       float  a ,
       float  b ,
       float  EPS )
{
   int          j ;
   float        s ,  s_old  =   0 ;

   for   ( =   1 ;  j  <=  JMAX ;  j ++ )   {
    s  =  trapzd ( func ,  a ,  b ,  j );
     if   ( <=   3   ||  fabs ( -  s_old )   >  EPS  *  fabs ( s_old ))
      s_old  =  s ;
     else
       break ;
   }
   return   ( s );
}
#undef  JMAX

/***********************************************************
 *  Modified Bessel function exp(-x) I0(x), for x >=0.
 *  We modified from the original bessi0(). Instead of
 *  I0(x) itself, it returns I0(x) exp(-x).
 ****/
double
BessI0 ( double  x )
{
   double       ax ,  ans ;
   double       y ;

   if   (( ax  =  fabs ( x ))   <   3.75 )   {
    y  =  x  /   3.75 ;
    y  *=  y ;
    ans  =  exp ( - ax )   *   ( 1.0   +  y  *   ( 3.5156229   +  y  *   ( 3.0899424   +  y  *   ( 1.2067492
           +  y  *   ( 0.2659732   +  y  *   ( 0.360768e-1   +  y  *   0.45813e-2 ))))));
   }   else   {
    y  =   3.75   /  ax ;
    ans  =   ( 1   /  sqrt ( ax ))   *   ( 0.39894228   +  y  *   ( 0.1328592e-1
         +  y  *   ( 0.225319e-2   +  y  *   ( - 0.157565e-2   +  y  *   ( 0.916281e-2
         +  y  *   ( - 0.2057706e-1   +  y  *   ( 0.2635537e-1   +  y  *   ( - 0.1647633e-1
                            +  y  *   0.392377e-2 ))))))));
   }
   return  ans ;
}

/****************************************************************
 ****/
short
GetShort ( short   Lo ,   short   Hi )
{
   char         in_str [ STRLEN ];
   short        x ;

  gets ( in_str );
  sscanf ( in_str ,   "%hd" ,   & x );
   while   ( <   Lo   ||  x  >   Hi )   {
    printf ( "...Wrong paramter.  Input again: " );
    gets ( in_str );
    sscanf ( in_str ,   "%hd" ,   & x );
   }
   return   ( x );
}

/****************************************************************
 ****/
float
GetFloat ( float   Lo ,   float   Hi )
{
   char         in_str [ STRLEN ];
   float        x ;

  gets ( in_str );
  sscanf ( in_str ,   "%f" ,   & x );
   while   ( <   Lo   ||  x  >   Hi )   {
    printf ( "...Wrong paramter.  Input again: " );
    gets ( in_str );
    sscanf ( in_str ,   "%f" ,   & x );
   }
   return   ( x );
}

__MACOSX/FTP-Uploads/ch04Convolution/._CONVNR.C

FTP-Uploads/ch04Convolution/CONVO.C

FTP-Uploads/ch04Convolution/CONVO.C

/****************************************************************
 *  Functions for file output.
 ****/

#include   "conv.h"

/****************************************************************
 *  Center a string according to the column width.
 ****/
char         *
CenterStr ( short   int   Wid ,
       char   * InStr ,
       char   * OutStr )
{
  size_t      nspaces ;
   /* number of spaces to be filled before InStr. */

  nspaces  =   ( Wid   -  strlen ( InStr ))   /   2 ;
   if   ( nspaces  <   0 )
    nspaces  =   0 ;

  strcpy ( OutStr ,   "" );
   while   ( nspaces -- )
    strcat ( OutStr ,   " " );

  strcat ( OutStr ,   InStr );

   return   ( OutStr );
}

/****************************************************************
 *  Print some messages before starting simulation.  e.g. author,
 *  address, program version, year.
 ****/
#define  COLWIDTH  80
void  
ShowVersion ( void )
{
   char         str [ STRLEN ];

  puts ( "" );
   CenterStr ( COLWIDTH ,   "Convolution of MCML Simulation Data" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "Version 1.1, 1994" ,  str );  puts ( str );
  puts ( "" );

   CenterStr ( COLWIDTH ,   "Lihong Wang, Ph.D." ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "Biomedical Engineering Program" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "3120 TAMU, Texas A&M University" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "College Station, TX 77843-3120" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "Email: [email protected]" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "URL: http://oilab.tamu.edu" ,  str );  puts ( str );
  puts ( "" );

   CenterStr ( COLWIDTH ,   "Steven L. Jacques, Ph.D." ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "Oregon Medical Laser Center" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "Providence/St. Vincent Hospital" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "9205 SW Barnes Rd., Portland, OR 97225" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "Email: [email protected]" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,   "URL: http://omlc.ogi.edu/staff/jacques.html" ,  str );  
  puts ( str );
  puts ( "" );

   CenterStr ( COLWIDTH ,  
   "Please cite the following article in your publications:" ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,  
   "L.-H. Wang, S. L. Jacques, and L.-Q. Zheng, " ,  str );  puts ( str );
   CenterStr ( COLWIDTH ,  
   "CONV - Convolution for responses to a finite diameter photon beam" ,  str );  
  puts ( str );
   CenterStr ( COLWIDTH ,  
   "incident on multi-layered tissues, Computer Methods and Programs in" ,  str );  
  puts ( str );
   CenterStr ( COLWIDTH ,  
   "Biomedicine 54, 141-150 (1997)." ,  str );  puts ( str );
}
#undef  COLWIDTH

/****************************************************************
 *  Open a file for output with extension Ext.  If file exists,
 *  ask whether to overwrite or append or change filename.
 *
 *  Return file pointer, which could be NULL.
 *  Return the full filename as Ext.
 ****/
FILE        *
GetWriteFile ( char   * Ext )
{
  FILE        * file ;
   char         fname [ STRLEN ],  fmode [ STRLEN ];

   do   {
    printf ( "Enter output filename with extension .%s (or . to quit): " ,   Ext );
    gets ( fname );
     if   ( strlen ( fname )   ==   1   &&  fname [ 0 ]   ==   '.' )   {
      fmode [ 0 ]   =   'q' ;
       break ;
     }   else
      fmode [ 0 ]   =   'w' ;

     if   (( file  =  fopen ( fname ,   "r" ))   !=  NULL )   {     /* file exists. */
      printf ( "File %s exists, %s" ,
         fname ,   "w=overwrite, a=append, n=new filename, q=quit: " );
       do
    gets ( fmode );
       while   ( ! strlen ( fmode ));     /* avoid null line. */
      fclose ( file );
     }
   }   while   ( fmode [ 0 ]   !=   'w'   &&  fmode [ 0 ]   !=   'a'   &&  fmode [ 0 ]   !=   'q' );

   if   ( fmode [ 0 ]   !=   'q' )
    file  =  fopen ( fname ,  fmode );
   else
    file  =  NULL ;

  strcpy ( Ext ,  fname );
   return   ( file );          /* could be NULL. */
}

/****************************************************************
 *  Return the index to the layer, where iz is in.
 *
 *  Use the center of box.
 ****/
short  
IzToLayer ( short   Iz ,
       InputStruct   *   In_Ptr )
{
   short        i  =   1 ;          /* index to layer. */
   short        num_layers  =   In_Ptr -> num_layers ;
   double       dz  =   In_Ptr -> dz ;

   while   (( Iz   +   0.5 )   *  dz  >=   In_Ptr -> layerspecs [ i ]. z1
      &&  i  <  num_layers )
    i ++ ;

   return   ( i );
}

/****************************************************************
 *  Write the input parameter for Monte Carlo simulation program
 *  in such a format that it can be read directly back.
 ****/
void  
WriteInParm ( InputStruct   *   In_Ptr )
{
   short        i ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "InP" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "1.1\t# file version.\n" );
  fprintf ( file ,   "1\t# number of runs.\n\n" );
  fprintf ( file ,   "temp.out\tA\t\t#output filename.\n" );

  fprintf ( file ,   "%ld \t\t\t# No. of photons.\n" ,
       In_Ptr -> num_photons );

  fprintf ( file ,   "%G\t%G\t\t# dz, dr.\n" ,   In_Ptr -> dz ,   In_Ptr -> dr );
  fprintf ( file ,   "%hd\t%hd\t%hd\t# No. of dz, dr, da.\n\n" ,
       In_Ptr -> nz ,   In_Ptr -> nr ,   In_Ptr -> na );

  fprintf ( file ,   "%hd\t\t\t\t\t# Number of layers.\n" ,
       In_Ptr -> num_layers );
  fprintf ( file ,
       "#n\tmua\tmus\tg\td\t# One line for each layer.\n" );
  fprintf ( file ,   "%G\t\t\t\t\t# n for medium above.\n" ,
       In_Ptr -> layerspecs [ 0 ]. n );
   for   ( =   1 ;  i  <=   In_Ptr -> num_layers ;  i ++ )   {
     LayerStruct  s ;
    s  =   In_Ptr -> layerspecs [ i ];
    fprintf ( file ,   "%G\t%G\t%G\t%G\t%G\t# layer %hd\n" ,
        s . n ,  s . mua ,  s . mus ,  s . g ,  s . z1  -  s . z0 ,  i );
   }
  fprintf ( file ,   "%G\t\t\t\t\t# n for medium below.\n\n" ,
       In_Ptr -> layerspecs [ i ]. n );

  fclose ( file );
}

/****************************************************************
 *  Write reflectance, absorption, transmission.
 ****/
void  
WriteRAT ( OutStruct   *   Out_Ptr )
{
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "RAT" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,           /* flag. */
       "RAT #Reflectance, absorption, transmission.\n" );
  fprintf ( file ,   "%-12.4G \t#Specular reflectance.\n" ,
       Out_Ptr -> Rsp );
  fprintf ( file ,   "%-12.4G \t#Diffuse reflectance.\n" ,
       Out_Ptr -> Rd );
  fprintf ( file ,   "%-12.4G \t#Absorption.\n" ,
       Out_Ptr -> A );
  fprintf ( file ,   "%-12.4G \t#Transmission.\n" ,
       Out_Ptr -> Tt );

  fprintf ( file ,   "\n" );

  fclose ( file );
}

/****************************************************************
 *  Write absorption as a function of layer.
 *  2 numbers each line: layer, A[layer].
 ****/
void  
WriteA_layer ( short   Num_Layers ,
          double   * A_l )
{
   short        i ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Al" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "layer\t%-s[-]\n" ,  fname );
   for   ( =   1 ;  i  <=   Num_Layers ;  i ++ )
    fprintf ( file ,   "%-4hd\t%-12.4G\n" ,  i ,   A_l [ i ]);

  fclose ( file );
}

/****************************************************************
 *  2 numbers each line: z, A[z].
 ****/
void  
WriteA_z ( InputStruct   *   In_Ptr ,   double   * A_z )
{
   short        nz  =   In_Ptr -> nz ;
   double       dz  =   In_Ptr -> dz ;
   short        iz ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Az" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-s[1/cm]\n" ,   "z[cm]" ,  fname );
   for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( iz  +   0.5 )   *  dz ,   A_z [ iz ]);

  fclose ( file );
}

/****************************************************************
 *  3 numbers each line: r, z, A[r][z].
 ****/
void  
WriteA_rz ( InputStruct   *   In_Ptr ,
       double   ** A_rz )
{
   short        ir ,  iz ,  nz  =   In_Ptr -> nz ,  nr  =   In_Ptr -> nr ;
   double       r ,  z ,  dr  =   In_Ptr -> dr ,  dz  =   In_Ptr -> dz ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Arz" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-12s\t%-s[1/cm3]\n" ,   "r[cm]" ,   "z[cm]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
     for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
      z  =   ( iz  +   0.5 )   *  dz ;
      fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,
          r ,  z ,   A_rz [ ir ][ iz ]);
     }
   }

  fclose ( file );
}

/****************************************************************
 *  2 numbers each line: z, F[z].
 ****/
void  
WriteF_z ( InputStruct   *   In_Ptr ,
      double   * A_z )
{
  FILE        * file ;
   short        iz ,  nz  =   In_Ptr -> nz ;
   double       mua ,  dz  =   In_Ptr -> dz ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Fz" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-s[-]\n" ,   "z[cm]" ,  fname );
   for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
    mua  =   In_Ptr -> layerspecs [ IzToLayer ( iz ,   In_Ptr )]. mua ;
     if   ( mua  >   0.0 )
      fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( iz  +   0.5 )   *  dz ,
           A_z [ iz ]   /  mua );
     else
      fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( iz  +   0.5 )   *  dz ,   0.0 );
   }

  fclose ( file );
}

/****************************************************************
 *  3 numbers each line: r, z, F[r][z].
 ****/
void  
WriteF_rz ( InputStruct   *   In_Ptr ,
       double   ** A_rz )
{
  FILE        * file ;
   short        ir ,  iz ,  nz  =   In_Ptr -> nz ,  nr  =   In_Ptr -> nr ;
   double       mua ,  r ,  z ,  dr  =   In_Ptr -> dr ,  dz  =   In_Ptr -> dz ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Frz" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-12s\t%-s[1/cm2]\n" ,
       "r[cm]" ,   "z[cm]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
     for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
      z  =   ( iz  +   0.5 )   *  dz ;
      mua  =   In_Ptr -> layerspecs [ IzToLayer ( iz ,   In_Ptr )]. mua ;
       if   ( mua  >   0.0 )
    fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,
        r ,  z ,   A_rz [ ir ][ iz ]   /  mua );
       else
    fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,  r ,  z ,   0.0 );
     }
   }

  fclose ( file );
}

/****************************************************************
 *  3 numbers each line: r, a, Rd[r][a].
 ****/
void  
WriteRd_ra ( InputStruct   *   In_Ptr ,
        double   ** Rd_ra )
{
   short        ir ,  ia ,  nr  =   In_Ptr -> nr ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  dr  =   In_Ptr -> dr ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Rra" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-12s\t%-s[1/(cm2sr)]\n" ,
       "r[cm]" ,   "a[rad]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
     for   ( ia  =   0 ;  ia  <  na ;  ia ++ )   {
      a  =   ( ia  +   0.5 )   *  da ;
      fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,
          r ,  a ,   Rd_ra [ ir ][ ia ]);
     }
   }

  fclose ( file );
}

/****
 *  2 numbers each line: r, Rd[r]
 ****/
void  
WriteRd_r ( InputStruct   *   In_Ptr ,
       double   * Rd_r )
{
   short        ir ,  nr  =   In_Ptr -> nr ;
   double       dr  =   In_Ptr -> dr ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Rr" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-s[1/cm2]\n" ,   "r[cm]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( ir  +   0.5 )   *  dr ,
         Rd_r [ ir ]);

  fclose ( file );
}

/****************************************************************
 *  2 numbers each line: a, Rd[a].
 ****/
void  
WriteRd_a ( InputStruct   *   In_Ptr ,
       double   * Rd_a )
{
   short        ia ,  na  =   In_Ptr -> na ;
   double       da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Ra" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-s[1/sr]\n" ,   "a[rad]" ,  fname );
   for   ( ia  =   0 ;  ia  <  na ;  ia ++ )
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( ia  +   0.5 )   *  da ,
         Rd_a [ ia ]);

  fclose ( file );
}

/****************************************************************
 *  3 numbers each line:r, a, Tt[r][a]. a = theta.
 ****/
void  
WriteTt_ra ( InputStruct   *   In_Ptr ,
        double   ** Tt_ra )
{
   short        ir ,  ia ,  nr  =   In_Ptr -> nr ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  dr  =   In_Ptr -> dr ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Tra" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-12s\t%-s[1/(cm2sr)]\n" ,
       "r[cm]" ,   "a[rad]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
     for   ( ia  =   0 ;  ia  <  na ;  ia ++ )   {
      a  =   ( ia  +   0.5 )   *  da ;
      fprintf ( file ,   "%-12.4E\t%-12.4E\t%-12.4E\n" ,
          r ,  a ,   Tt_ra [ ir ][ ia ]);
     }
   }

  fclose ( file );
}

/****
 *  2 numbers each line: r, Tt[r].
 ****/
void  
WriteTt_r ( InputStruct   *   In_Ptr ,
       double   * Tt_r )
{
   short        ir ,  nr  =   In_Ptr -> nr ;
   double       dr  =   In_Ptr -> dr ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Tr" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-s[1/cm2]\n" ,   "r[cm]" ,  fname );
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( ir  +   0.5 )   *  dr ,
         Tt_r [ ir ]);

  fclose ( file );
}

/****************************************************************
 *  2 numbers each line: theta, Tt[theta].
 ****/
void  
WriteTt_a ( InputStruct   *   In_Ptr ,
       double   * Tt_a )
{
   short        ia ,  na  =   In_Ptr -> na ;
   double       da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Ta" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fprintf ( file ,   "%-12s\t%-s[1/sr]\n" ,   "a[rad]" ,  fname );
   for   ( ia  =   0 ;  ia  <  na ;  ia ++ )
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,   ( ia  +   0.5 )   *  da ,
         Tt_a [ ia ]);

  fclose ( file );
}

/****************************************************************
 *  Write output in M. Keijzer's format so that the file can be
 *  read by the convolution program written by Keijzer in Pascal.
 ****/
void  
WriteKFormat ( InputStruct   *   In_Ptr ,
          OutStruct   *   Out_Ptr )
{
   short        i ,  j ;
   double       dz ,  dr ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "K" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  fputs ( "output.filename\n" ,  file );
  fprintf ( file ,   "%12hd layers\n" ,   In_Ptr -> num_layers );
  fprintf ( file ,   "%12s %12s %12s %12s %12s %12s\n" ,
       "layer" ,   "mua" ,   "mus" ,   "g" ,   "nt" ,   "thickness" );

   for   ( =   1 ;  i  <=   In_Ptr -> num_layers ;  i ++ )
    fprintf ( file ,
         "%12hd %12.6lf %12.6lf %12.6lf %12.6lf %12.6lf\n" ,
        i ,   In_Ptr -> layerspecs [ i ]. mua ,
         In_Ptr -> layerspecs [ i ]. mus ,
         In_Ptr -> layerspecs [ i ]. g ,   In_Ptr -> layerspecs [ i ]. n ,
         In_Ptr -> layerspecs [ i ]. z1  -
         In_Ptr -> layerspecs [ i ]. z0 );
  fprintf ( file ,   "%12.6lf index of refraction above\n" ,
       In_Ptr -> layerspecs [ 0 ]. n );
  fprintf ( file ,   "%12.6lf index of refraction below\n" ,
       In_Ptr -> layerspecs [ i ]. n );
  fprintf ( file ,   "\n" );

  fprintf ( file ,   "%12ld photons\n" ,   In_Ptr -> num_photons );
  fprintf ( file ,   "%12.6lf critical weight\n" ,   In_Ptr -> Wth );
  fprintf ( file ,   "%12.6lf depth of boxes micron\n" ,
       In_Ptr -> dz  *   1e4 );
  fprintf ( file ,   "%12.6lf width of boxes micron\n" ,
       In_Ptr -> dr  *   1e4 );
  fprintf ( file ,   "%12hd number of boxes in z \n" ,   In_Ptr -> nz );
  fprintf ( file ,   "%12hd number of boxes in r \n" ,   In_Ptr -> nr );
  fprintf ( file ,   "\n" );

  fprintf ( file ,
       "%12.6lf Total reflection (including direct R)\n" ,
       Out_Ptr -> Rsp   +   Out_Ptr -> Rd );
   for   ( =   1 ;  i  <=   In_Ptr -> num_layers ;  i ++ )
    fprintf ( file ,   "%12.6lf Absorbed in layer %12hd\n" ,
         Out_Ptr -> A_l [ i ],  i );
  fprintf ( file ,   "%12.6lf Total transmission\n" ,   Out_Ptr -> Tt );
  fprintf ( file ,   "\n" );

  fprintf ( file ,   "Reflectance and Transmission in [cm-2]\n" );
  fprintf ( file ,   "Absorption in z-layers in [cm-1]\n" );
  fprintf ( file ,   "Absorption in z/r-boxes in [cm-3]\n" );

  fprintf ( file ,   "z/r [cm] Layer" );
  dr  =   In_Ptr -> dr ;
   for   ( =   0 ;  i  <   In_Ptr -> nr ;  i ++ )
    fprintf ( file ,   "%12.6lf " ,  i  *  dr );
  fprintf ( file ,   "\n" );

  fprintf ( file ,   "Refl. " );
   for   ( =   0 ;  i  <   In_Ptr -> nr ;  i ++ )
    fprintf ( file ,   "%12.6lf " ,   Out_Ptr -> Rd_r [ i ]);
  fprintf ( file ,   "\n" );

  dz  =   In_Ptr -> dz ;
   for   ( =   0 ;  i  <   In_Ptr -> nz ;  i ++ )   {
    fprintf ( file ,   "%12.6lf %12.6lf" ,  i  *  dz ,   Out_Ptr -> A_z [ i ]);
     for   ( =   0 ;  j  <   In_Ptr -> nr ;  j ++ )
      fprintf ( file ,   "%12.6lf " ,   Out_Ptr -> A_rz [ j ][ i ]);
    fprintf ( file ,   "\n" );
   }

  fprintf ( file ,   "Transm." );
   for   ( =   0 ;  i  <   In_Ptr -> nr ;  i ++ )
    fprintf ( file ,   "%12.6lf " ,   Out_Ptr -> Tt_r [ i ]);
  fprintf ( file ,   "\n" );

  fclose ( file );
}

/****************************************************************
 ****/
void  
ShowOutMenu ( char   * in_fname )
{
  printf ( "I   = Input parameters of mcml\n" );
  printf ( "3   = reflectance, absorption, and transmittance\n" );

  printf ( "AL  = absorption vs layer [-]\n" );
  printf ( "Az  = absorption vs z [1/cm]\n" );
  printf ( "Arz = absorption vs r & z [1/cm3]\n" );
  printf ( "Fz  = fluence vs z [-]\n" );
  printf ( "Frz = fluence vs r & z [1/cm2]\n" );

  printf ( "Rr  = diffuse reflectance vs radius r [1/cm2]\n" );
  printf ( "Ra  = diffuse reflectance vs angle alpha [1/sr]\n" );
  printf ( "Rra = diffuse reflectance vs radius and angle [1/(cm2 sr)]\n" );

  printf ( "Tr  = transmittance vs radius r [1/cm2]\n" );
  printf ( "Ta  = transmittance vs angle alpha [1/sr]\n" );
  printf ( "Tra = transmittance vs radius and angle [1/(cm2 sr)]\n" );

  printf ( "K   = Keijzer's format\n" );
  printf ( "Q   = Quit to main menu\n" );
  printf ( "* input filename: %s \n" ,  in_fname );
}

/****************************************************************
 ****/
void  
BranchOutA ( char   * Cmd_Str ,
        InputStruct   *   In_Ptr ,
        OutStruct   *   Out_Ptr )
{
   switch   ( toupper ( Cmd_Str [ 1 ]))   {
     case   'L' :             /* A_l. */
     WriteA_layer ( In_Ptr -> num_layers ,   Out_Ptr -> A_l );
     break ;
   case   'Z' :
     if   ( toupper ( Cmd_Str [ 2 ])   ==   '\0' )      /* A_z. */
       WriteA_z ( In_Ptr ,   Out_Ptr -> A_z );
     else
      puts ( "...Wrong command" );
     break ;
   case   'R' :
     if   ( toupper ( Cmd_Str [ 2 ])   ==   'Z' )   /* A_rz. */
       WriteA_rz ( In_Ptr ,   Out_Ptr -> A_rz );
     else
      puts ( "...Wrong command" );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
BranchOutF ( char   * Cmd_Str ,
        InputStruct   *   In_Ptr ,
        OutStruct   *   Out_Ptr )
{
   switch   ( toupper ( Cmd_Str [ 1 ]))   {
     case   'Z' :
     if   ( toupper ( Cmd_Str [ 2 ])   ==   '\0' )      /* F_z. */
       WriteF_z ( In_Ptr ,   Out_Ptr -> A_z );
     else
      puts ( "...Wrong command" );
     break ;
   case   'R' :
     if   ( toupper ( Cmd_Str [ 2 ])   ==   'Z' )   /* F_rz. */
       WriteF_rz ( In_Ptr ,   Out_Ptr -> A_rz );
     else
      puts ( "...Wrong command" );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
BranchOutR ( char   * Cmd_Str ,
        InputStruct   *   In_Ptr ,
        OutStruct   *   Out_Ptr )
{
   char         ch ;

   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'A' :           /* Rd_a. */
     WriteRd_a ( In_Ptr ,   Out_Ptr -> Rd_a );
     break ;
   case   'R' :
    ch  =  toupper ( Cmd_Str [ 2 ]);
     if   ( ch  ==   '\0' )       /* Rd_r. */
       WriteRd_r ( In_Ptr ,   Out_Ptr -> Rd_r );
     else   if   ( ch  ==   'A' )       /* Rd_ra. */
       WriteRd_ra ( In_Ptr ,   Out_Ptr -> Rd_ra );
     else
      puts ( "...Wrong command" );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
BranchOutT ( char   * Cmd_Str ,
        InputStruct   *   In_Ptr ,
        OutStruct   *   Out_Ptr )
{
   char         ch ;

   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'A' :           /* Tt_a. */
     WriteTt_a ( In_Ptr ,   Out_Ptr -> Tt_a );
     break ;
   case   'R' :
    ch  =  toupper ( Cmd_Str [ 2 ]);
     if   ( ch  ==   '\0' )       /* Tt_r. */
       WriteTt_r ( In_Ptr ,   Out_Ptr -> Tt_r );
     else   if   ( ch  ==   'A' )       /* Tt_ra. */
       WriteTt_ra ( In_Ptr ,   Out_Ptr -> Tt_ra );
     else
      puts ( "...Wrong command" );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
BranchOutCmd ( char   * Cmd_Str ,
          InputStruct   *   In_Ptr ,
          OutStruct   *   Out_Ptr )
{
   char         ch ;

   switch   ( toupper ( Cmd_Str [ 0 ]))   {
   case   'I' :
     WriteInParm ( In_Ptr );
     break ;
   case   '3' :
     WriteRAT ( Out_Ptr );
     break ;
   case   'K' :
     WriteKFormat ( In_Ptr ,   Out_Ptr );
     break ;
   case   'A' :
     BranchOutA ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'F' :
     BranchOutF ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'R' :
     BranchOutR ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'T' :
     BranchOutT ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'H' :
     ShowOutMenu ( In_Ptr -> in_fname );
     break ;
   case   'Q' :
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
OutputOrigData ( InputStruct   *   In_Ptr ,
            OutStruct   *   Out_Ptr )
{
   char         cmd_str [ STRLEN ];

   if   ( ! Out_Ptr -> allocated )
    puts ( "...No data to output" );
   else
     do   {
      printf ( "\n> Output mcml data (h for help) => " );
       do
    gets ( cmd_str );
       while   ( ! strlen ( cmd_str ));   /* avoid null string. */
       BranchOutCmd ( cmd_str ,   In_Ptr ,   Out_Ptr );
     }   while   ( toupper ( cmd_str [ 0 ])   !=   'Q' );
}

/****************************Contours***************************/
/****************************************************************
 ****/
void  
ShowContOrigMenu ( char   * in_fname )
{
  printf ( "A = absorption vs r & z [1/cm3]\n" );
  printf ( "F = fluence vs r & z [1/cm2]\n" );
  printf ( "R = diffuse reflectance vs radius and angle [1/(cm2 sr)]\n" );
  printf ( "T = transmittance vs radius and angle [1/(cm2 sr)]\n" );
  printf ( "Q   = Quit to main menu\n" );
  printf ( "* input filename: %s \n" ,  in_fname );
}

/****************************************************************
 *  Absorption density to fluence. A = F/mua;
 ****/
void  
A2F ( InputStruct   *   In_Ptr ,   double   ** A_rz )
{
   short        nz  =   In_Ptr -> nz ,  nr  =   In_Ptr -> nr ;
   double       dr  =   In_Ptr -> dr ,  dz  =   In_Ptr -> dz ;
   short        ir ,  iz ;
   double       mua ;

   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )
     for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
      mua  =   In_Ptr -> layerspecs [ IzToLayer ( iz ,   In_Ptr )]. mua ;
       if   ( mua  >   0.0 )
     A_rz [ ir ][ iz ]   /=  mua ;
     }
}

/****************************************************************
 *  Fluence to absorption density. F = A*mua;
 ****/
void  
F2A ( InputStruct   *   In_Ptr ,   double   ** A_rz )
{
   short        nz  =   In_Ptr -> nz ,  nr  =   In_Ptr -> nr ;
   double       dr  =   In_Ptr -> dr ,  dz  =   In_Ptr -> dz ;
   short        ir ,  iz ;
   double       mua ;

   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )
     for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
      mua  =   In_Ptr -> layerspecs [ IzToLayer ( iz ,   In_Ptr )]. mua ;
       if   ( mua  >   0.0 )
     A_rz [ ir ][ iz ]   *=  mua ;
     }
}

/****************************************************************
 ****/
void  
BranchContOrigCmd ( char   * Cmd_Str ,
           InputStruct   *   In_Ptr ,
           OutStruct   *   Out_Ptr )
{
   char         ch ;

   switch   ( toupper ( Cmd_Str [ 0 ]))   {
   case   'A' :
     IsoPlot ( Out_Ptr -> A_rz ,   In_Ptr -> nr  -   1 ,   In_Ptr -> nz  -   1 ,
         In_Ptr -> dr ,   In_Ptr -> dz );
     break ;
   case   'F' :
    A2F ( In_Ptr ,   Out_Ptr -> A_rz );
     IsoPlot ( Out_Ptr -> A_rz ,   In_Ptr -> nr  -   1 ,   In_Ptr -> nz  -   1 ,
         In_Ptr -> dr ,   In_Ptr -> dz );
    F2A ( In_Ptr ,   Out_Ptr -> A_rz );
     break ;
   case   'R' :
     IsoPlot ( Out_Ptr -> Rd_ra ,   In_Ptr -> nr  -   1 ,   In_Ptr -> na  -   1 ,
         In_Ptr -> dr ,   In_Ptr -> da );
     break ;
   case   'T' :
     IsoPlot ( Out_Ptr -> Tt_ra ,   In_Ptr -> nr  -   1 ,   In_Ptr -> na  -   1 ,
         In_Ptr -> dr ,   In_Ptr -> da );
     break ;
   case   'H' :
     ShowContOrigMenu ( In_Ptr -> in_fname );
     break ;
   case   'Q' :
     break ;
   default :
    puts ( "...Wrong command" );
   }
}
/****************************************************************
 ****/
void  
ContourOrigData ( InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   char         cmd_str [ STRLEN ];

   if   ( ! Out_Ptr -> allocated )
    puts ( "...No data to output" );
   else
     do   {
      printf ( "\n> Contour output of mcml data (h for help) => " );
       do
    gets ( cmd_str );
       while   ( ! strlen ( cmd_str ));   /* avoid null string. */
       BranchContOrigCmd ( cmd_str ,   In_Ptr ,   Out_Ptr );
     }   while   ( toupper ( cmd_str [ 0 ])   !=   'Q' );
}

/****************************Scanning***************************/
/****************************************************************
 ****/
void  
ShowScanOrigMenu ( char   * in_fname )
{
  printf ( "Ar = absorption vs r @ fixed z [1/cm3]\n" );
  printf ( "Az = absorption vs z @ fixed r [1/cm3]\n" );
  printf ( "Fr = fluence vs r @ fixed z [1/cm2]\n" );
  printf ( "Fz = fluence vs z @ fixed r [1/cm2]\n" );
  printf ( "Rr = diffuse reflectance vs r @ fixed angle [1/(cm2 sr)]\n" );
  printf ( "Ra = diffuse reflectance vs angle @ fixed r [1/(cm2 sr)]\n" );
  printf ( "Tr = transmittance vs r @ fixed angle [1/(cm2 sr)]\n" );
  printf ( "Ta = transmittance vs angle @ fixed r [1/(cm2 sr)]\n" );
  printf ( "Q  = quit\n" );
  printf ( "* input filename: %s \n" ,  in_fname );
}

/****************************************************************
 *  Ext is either "Ars" or "Frs".
 ****/
void  
ScanOrigA_r ( char   * Ext ,   InputStruct   *   In_Ptr ,   double   ** A_rz )
{
   short        ir ,  iz ,  nr  =   In_Ptr -> nr ,  nz  =   In_Ptr -> nz ;
   double       r ,  z ,  dr  =   In_Ptr -> dr ,  dz  =   In_Ptr -> dz ;
  FILE        * file ;

  file  =   GetWriteFile ( Ext );
   if   ( file  ==  NULL )
     return ;

  printf ( "z grid separation is %-10.4lg cm.\n" ,  dz );
  printf ( "Input fixed z index (0 - %2hd): " ,  nz  -   1 );
  iz  =   GetShort ( 0 ,  nz  -   1 );
  fprintf ( file ,   "%-12s\t%-s@z=%-9.3lg\n" ,   "r[cm]" ,   Ext ,  dz  *   ( iz  +   0.5 ));
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  r ,   A_rz [ ir ][ iz ]);
   }

  fclose ( file );
}

/****************************************************************
 *  Ext is either "Azs" or "Fzs".
 ****/
void  
ScanOrigA_z ( char   * Ext ,   InputStruct   *   In_Ptr ,   double   ** A_rz )
{
   short        ir ,  iz ,  nr  =   In_Ptr -> nr ,  nz  =   In_Ptr -> nz ;
   double       r ,  z ,  dr  =   In_Ptr -> dr ,  dz  =   In_Ptr -> dz ;
  FILE        * file ;

  file  =   GetWriteFile ( Ext );
   if   ( file  ==  NULL )
     return ;

  printf ( "r grid separation is %-10.4lg cm.\n" ,  dr );
  printf ( "Input fixed r index (0 - %2hd): " ,  nr  -   1 );
  ir  =   GetShort ( 0 ,  nr  -   1 );
  fprintf ( file ,   "%-12s\t%-s@r=%-9.3lg\n" ,   "z[cm]" ,   Ext ,  dr  *   ( ir  +   0.5 ));
   for   ( iz  =   0 ;  iz  <  nz ;  iz ++ )   {
    z  =   ( iz  +   0.5 )   *  dz ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  z ,   A_rz [ ir ][ iz ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void  
ScanOrigRd_r ( InputStruct   *   In_Ptr ,   double   ** Rd_ra )
{
   short        ir ,  ia ,  nr  =   In_Ptr -> nr ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  dr  =   In_Ptr -> dr ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Rrs" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  printf ( "Angle grid separation is %-10.4lg rad.\n" ,  da );
  printf ( "Input fixed angle index (0 - %2hd): " ,  na  -   1 );
  ia  =   GetShort ( 0 ,  na  -   1 );
  fprintf ( file ,   "%-12s\t%-s@a=%-9.3lg\n" ,   "r[cm]" ,  fname ,  da  *   ( ia  +   0.5 ));
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  r ,   Rd_ra [ ir ][ ia ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void  
ScanOrigRd_a ( InputStruct   *   In_Ptr ,   double   ** Rd_ra )
{
   short        ir ,  ia ,  nr  =   In_Ptr -> nr ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  dr  =   In_Ptr -> dr ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Ras" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  printf ( "r grid separation is %-10.4lg cm.\n" ,  dr );
  printf ( "Input fixed r index (0 - %2hd): " ,  nr  -   1 );
  ir  =   GetShort ( 0 ,  nr  -   1 );
  fprintf ( file ,   "%-12s\t%-s@r=%-9.3lg\n" ,   "a[rad]" ,  fname ,  dr  *   ( ir  +   0.5 ));
   for   ( ia  =   0 ;  ia  <  na ;  ia ++ )   {
    a  =   ( ia  +   0.5 )   *  da ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  a ,   Rd_ra [ ir ][ ia ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void  
ScanOrigTt_r ( InputStruct   *   In_Ptr ,   double   ** Tt_ra )
{
   short        ir ,  ia ,  nr  =   In_Ptr -> nr ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  dr  =   In_Ptr -> dr ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Trs" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  printf ( "Angle grid separation is %-10.4lg rad.\n" ,  da );
  printf ( "Input fixed angle index (0 - %2hd): " ,  na  -   1 );
  ia  =   GetShort ( 0 ,  na  -   1 );
  fprintf ( file ,   "%-12s\t%-s@a=%-9.3lg\n" ,   "r[cm]" ,  fname ,  da  *   ( ia  +   0.5 ));
   for   ( ir  =   0 ;  ir  <  nr ;  ir ++ )   {
    r  =   ( ir  +   0.5 )   *  dr ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  r ,   Tt_ra [ ir ][ ia ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void  
ScanOrigTt_a ( InputStruct   *   In_Ptr ,   double   ** Tt_ra )
{
   short        ir ,  ia ,  nr  =   In_Ptr -> nr ,  na  =   In_Ptr -> na ;
   double       r ,  a ,  dr  =   In_Ptr -> dr ,  da  =   In_Ptr -> da ;
  FILE        * file ;
   char         fname [ STRLEN ];

  strcpy ( fname ,   "Tas" );
  file  =   GetWriteFile ( fname );
   if   ( file  ==  NULL )
     return ;

  printf ( "r grid separation is %-10.4lg cm.\n" ,  dr );
  printf ( "Input fixed r index (0 - %2hd): " ,  nr  -   1 );
  ir  =   GetShort ( 0 ,  nr  -   1 );
  fprintf ( file ,   "%-12s\t%-s@r=%-9.3lg\n" ,   "a[rad]" ,  fname ,  dr  *   ( ir  +   0.5 ));
   for   ( ia  =   0 ;  ia  <  na ;  ia ++ )   {
    a  =   ( ia  +   0.5 )   *  da ;
    fprintf ( file ,   "%-12.4E\t%-12.4E\n" ,  a ,   Tt_ra [ ir ][ ia ]);
   }

  fclose ( file );
}

/****************************************************************
 ****/
void  
BranchScanOrigA ( char   * Cmd_Str ,
         InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   char         fname [ STRLEN ];

   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'R' :
    strcpy ( fname ,   "Ars" );
     ScanOrigA_r ( fname ,   In_Ptr ,   Out_Ptr -> A_rz );
     break ;
   case   'Z' :
    strcpy ( fname ,   "Azs" );
     ScanOrigA_z ( fname ,   In_Ptr ,   Out_Ptr -> A_rz );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
BranchScanOrigF ( char   * Cmd_Str ,
         InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   char         fname [ STRLEN ];

  A2F ( In_Ptr ,   Out_Ptr -> A_rz );

   switch   ( toupper ( Cmd_Str [ 1 ]))   {
   case   'R' :
    strcpy ( fname ,   "Frs" );
     ScanOrigA_r ( fname ,   In_Ptr ,   Out_Ptr -> A_rz );
     break ;
   case   'Z' :
    strcpy ( fname ,   "Fzs" );
     ScanOrigA_z ( fname ,   In_Ptr ,   Out_Ptr -> A_rz );
     break ;
   default :
    puts ( "...Wrong command" );
   }

  F2A ( In_Ptr ,   Out_Ptr -> A_rz );
}

/****************************************************************
 ****/
void  
BranchScanOrigR ( char   * Cmd_Str ,
         InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   switch   ( toupper ( Cmd_Str [ 1 ]))   {
     case   'R' :
     ScanOrigRd_r ( In_Ptr ,   Out_Ptr -> Rd_ra );
     break ;
   case   'A' :
     ScanOrigRd_a ( In_Ptr ,   Out_Ptr -> Rd_ra );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
BranchScanOrigT ( char   * Cmd_Str ,
         InputStruct   *   In_Ptr ,
         OutStruct   *   Out_Ptr )
{
   switch   ( toupper ( Cmd_Str [ 1 ]))   {
     case   'R' :
     ScanOrigTt_r ( In_Ptr ,   Out_Ptr -> Tt_ra );
     break ;
   case   'A' :
     ScanOrigTt_a ( In_Ptr ,   Out_Ptr -> Tt_ra );
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
BranchScanOrigCmd ( char   * Cmd_Str ,
           InputStruct   *   In_Ptr ,
           OutStruct   *   Out_Ptr )
{
   char         ch ;

   switch   ( toupper ( Cmd_Str [ 0 ]))   {
   case   'A' :
     BranchScanOrigA ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'F' :
     BranchScanOrigF ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'R' :
     BranchScanOrigR ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'T' :
     BranchScanOrigT ( Cmd_Str ,   In_Ptr ,   Out_Ptr );
     break ;
   case   'H' :
     ShowScanOrigMenu ( In_Ptr -> in_fname );
     break ;
   case   'Q' :
     break ;
   default :
    puts ( "...Wrong command" );
   }
}

/****************************************************************
 ****/
void  
ScanOrigData ( InputStruct   *   In_Ptr ,
          OutStruct   *   Out_Ptr )
{
   char         cmd_str [ STRLEN ];

   if   ( ! Out_Ptr -> allocated )
    puts ( "...No data to output" );
   else
     do   {
      printf ( "\n> Scans of mcml data (h for help) => " );
       do
    gets ( cmd_str );
       while   ( ! strlen ( cmd_str ));   /* avoid null string. */
       BranchScanOrigCmd ( cmd_str ,   In_Ptr ,   Out_Ptr );
     }   while   ( toupper ( cmd_str [ 0 ])   !=   'Q' );
}

__MACOSX/FTP-Uploads/ch04Convolution/._CONVO.C

FTP-Uploads/ch04Convolution/Mcman.pdf

Monte Carlo Modeling of Light Transport

in Multi-layered Tissues in Standard C

Lihong Wang, Ph. D.

Steven L. Jacques, Ph. D.

Laser Biology Research Laboratory

University of Texas M. D. Anderson Cancer Center

Supported by the Medical Free Electron Laser Program, the

Department of the Navy N00015-91-J-1354.

Copyright © University of Texas M. D. Anderson Cancer Center 1992

Monte Carlo Modeling of Light Transport

in Multi-layered Tissues in Standard C

Lihong Wang, Ph. D.

Steven L. Jacques, Ph. D.

Laser Biology Research Laboratory – 17

University of Texas M. D. Anderson Cancer Center

1515 Holcombe Blvd.

Houston, Texas 77030

Copyright © University of Texas

M. D. Anderson Cancer Center 1992

First printed August, 1992

Reprinted with corrections January, 1993 & November, 1995.

Abstract iii

Abstract

A Monte Carlo model of steady-state light transport in multi-layered tissue (mcml)

and the corresponding convolution program (conv) have been coded in ANSI Standard C.

The programs can therefore be executed on a variety of computers. Dynamic data

allocation is used for mcml, hence the number of tissue layers and the number of grid

elements of the grid system can be varied by users at run time as long as the total amount

of memory does not exceed what the system allows. The principle and the implementation

details of the model, and the instructions for using mcml and conv are presented here. We

have verified some of the mcml and conv computation results with those of other theories

or other investigators.

iv Acknowledgement

Acknowledgment

We would like to thank a group of people who have helped us with this package

directly or indirectly. Massoud Motamedi (University of Texas Medical Branch,

Galveston) has let us use his Sun SPARCstation 2. Scott A. Prahl (St. Vincent's Hospital,

Oregon) and Thomas J. Farrell (Hamilton Regional Cancer Center, Canada) have helped

us locate an insidious bug in the program. Craig M. Gardner (University of Texas, Austin)

provided us his Monte Carlo simulation and convolution results of a multi-layered

medium, which are compared with our results. We learned a lot from Marleen Keijzer and

Steven L. Jacques's Monte Carlo simulation program in PASCAL on Macintoshes.

Liqiong Zheng (University of Houston) has helped us greatly improve the speed of the

convolution program. They all deserve our thanks.

This work is supported by the Medical Free Electron Laser Program, the

Department of the Navy N00015-91-J-1354.

Table of Contents v

Table of Contents

Abstract ........................................................................................................... iii

Acknowledgment ............................................................................................. iv

0. Introduction ................................................................................................. 1

Part I. Description of Monte Carlo Simulation ..........................................4 1. The Problem and Coordinate Systems .......................................................... 4

2. Sampling Random Variables......................................................................... 7

3. Rules for Photon Propagation....................................................................... 11

3.1 Launching a photon packet ............................................................ 11

3.2 Photon's step size........................................................................... 12

3.3 Moving the photon packet ............................................................. 14

3.4 Photon absorption.......................................................................... 15

3.5 Photon scattering ........................................................................... 15

3.6 Reflection or transmission at boundary........................................... 17

3.7 Reflection or transmission at interface ............................................ 19

3.8 Photon termination ........................................................................ 20

4. Scored Physical Quantities ........................................................................... 22

4.1 Reflectance and transmittance ........................................................ 22

4.2 Internal photon distribution ............................................................ 24

4.3 Issues regarding grid system .......................................................... 27

5. Programming mcml ...................................................................................... 32

5.1 Programming rules and conventions ............................................... 32

5.2 Several constants ........................................................................... 33

5.3 Data structures and dynamic allocations ......................................... 34

5.4 Flowchart of photon tracing ........................................................... 38

5.5 Flow of the program mcml............................................................. 41

5.6 Multiple simulations....................................................................... 43

5.7 Timing profile of the program ........................................................ 43

6. Computation Results of mcml and Verification ............................................. 47

6.1 Total diffuse reflectance and total transmittance ............................. 47

6.2 Angularly resolved diffuse reflectance and transmittance ................ 48

6.3 Radially resolved diffuse reflectance............................................... 49

6.4 Depth resolved internal fluence ...................................................... 50

6.5 Computation times vs optical properties......................................... 52

6.6 Scored Physical Quantities of Multi-layered Tissues ....................... 62

vi Table of Contents

7. Convolution for Photon Beams of Finite Size................................................67

7.1 Principles of convolution ................................................................67

7.2 Convolution over Gaussian beams ..................................................71

7.3 Convolution over circularly flat beams............................................72

7.4 Numerical solution to the convolution ............................................73

7.5 Computation results of conv and verification ..................................80

Part II. User Manual..................................................................................87 8. Installing mcml and conv...............................................................................87

8.1 Installing on Sun workstations ........................................................87

8.2 Installing on IBM PC compatibles ..................................................88

8.3 Installing on Macintoshes ...............................................................88

8.4 Installing by Electronic Mail ...........................................................89

9. Instructions for mcml ....................................................................................91

9.1 File of input data ............................................................................91

9.2 Execution .......................................................................................93

9.3 File of output data ..........................................................................95

9.4 Subset of output data .....................................................................96

9.5 Bugs of mcml .................................................................................97

10. Instructions for conv ...................................................................................99

10.1 Start conv....................................................................................99

10.2 Main menu of conv......................................................................99

10.3 Command "i" of conv ..................................................................100

10.4 Command "b" of conv .................................................................100

10.5 Command "r" of conv..................................................................101

10.6 Command "e" of conv .................................................................101

10.7 Command "oo" of conv ...............................................................101

10.8 Command "oc" of conv ...............................................................103

10.9 Command "co" of conv ...............................................................104

10.10 Command "cc" of conv...............................................................105

10.11 Command "so" of conv...............................................................106

10.12 Command "sc" of conv ...............................................................107

10.13 Command "q" of conv ................................................................109

10.14 Bugs of conv ..............................................................................109

11. How to Modify mcml..................................................................................110

Appendices ................................................................................................113 Appendix A. Cflow Output of the Program mcml.............................................113

Table of Contents vii

Appendix B. Source Code of the Program mcml.............................................. 117

B.1 mcml.h .......................................................................................... 117

B.2 mcmlmain.c................................................................................... 121

B.3 mcmlio.c ....................................................................................... 125

B.4 mcmlgo.c ...................................................................................... 142

B.5 mcmlnr.c ....................................................................................... 154

Appendix C. Makefile for the Program mcml................................................... 156

Appendix D. A Template of mcml Input Data File ........................................... 157

Appendix E. A Sample Output Data File of mcml............................................ 158

Appendix F. Several C Shell Scripts ................................................................ 160

F.1 conv.bat for batch processing conv ................................................ 160

F.2 p1 for pasting files of 1D arrays ..................................................... 161

Appendix G. Where to Get the Programs mcml and conv ................................ 163

Appendix H. Future Developments of the Package .......................................... 164

References .................................................................................................166 Index ..........................................................................................................169

Chapter 0 Introduction 1

0. Introduction

Monte Carlo simulation has been used to solve a variety of physical problems.

However, there is no succinct well-established definition. We would like to adopt the

definition by Lux et al. (1991). In all applications of the Monte Carlo method, a

stochastic model is constructed in which the expected value of a certain random variable

(or of a combination of several variables) is equivalent to the value of a physical quantity

to be determined. This expected value is then estimated by the average of multiple

independent samples representing the random variable introduced above. For the

construction of the series of independent samples, random numbers following the

distribution of the variable to be estimated are used.

Monte Carlo simulations of photon propagation offer a flexible yet rigorous

approach toward photon transport in turbid tissues. The method describes local rules of

photon propagation that are expressed, in the simplest case, as probability distributions

that describe the step size of photon movement between sites of photon-tissue interaction,

and the angles of deflection in a photon's trajectory when a scattering event occurs. The

simulation can score multiple physical quantities simultaneously. However, the method is

statistical in nature and relies on calculating the propagation of a large number of photons

by the computer. As a result, this method requires a large amount of computation time.

The number of photons required depends largely on the question being asked, the

precision needed, and the spatial resolution desired. For example, to simply learn the total

diffuse reflectance from a tissue of specified optical properties, typically about 3,000

photons can yield a useful result. To map the spatial distribution of photons, φ(r, z), in a cylindrically symmetric problem, at least 10,000 photons are usually required to yield an

acceptable answer. To map spatial distributions in a more complex three-dimensional

problem such as a finite diameter beam irradiating a tissue with a buried blood vessel, the

required photons may exceed 100,000. The point to be remembered in these introductory

remarks is that Monte Carlo simulations are rigorous, but necessarily statistical and

therefore require significant computation time to achieve precision and resolution.

Nevertheless, the flexibility of the method makes Monte Carlo modeling a powerful tool.

Another aspect of the Monte Carlo simulations presented in this paper deserves

emphasis. The simulations described here do not treat the photon as a wave phenomenon,

and ignore such features as phase and polarization. The motivation for these simulations

2 Chapter 0 Introduction

is to predict radiant energy transport in turbid tissues. The photons are multiply scattered

by most tissues, therefore phase and polarization are quickly randomized, and play little

role in energy transport. Although the Monte Carlo simulations may be capable of

bookkeeping phase and polarization and treating wave phenomena statistically, this

manual will not consider these issues.

The Monte Carlo simulations are based on macroscopic optical properties that are

assumed to extend uniformly over small units of tissue volume. Mean free paths between

photon-tissue interaction sites typically range from 10-1000 µm, and 100 µm is a very

typical value in the visible spectrum (Cheong et al., 1990). The simulations do not treat

the details of radiant energy distribution within cells, for example.

As a simple example of the Monte Carlo simulation. We would like to present a

typical trajectory of a single photon packet in Fig. 0.1. Each step between photon positions (dots) is variable and equals –ln(ξ)/(µ a + µ s) where ξ is a random number and µa and µ s are the absorption and scattering coefficients, respectively (in this example, µa =

0.5 cm−1, µ s = 15 cm−1, g = 0.90). The value g is the anisotropy of scattering. The

weight of the photon is decreased from an initial value of 1 as it moves through the tissue, and equals an after n steps, where a is the albedo (a = µ s/(µ a + µ s)). When the photon

strikes the surface, a fraction of the photon weight escapes as reflectance and the

remaining weight is internally reflected and continues to propagate. Eventually, the

photon weight drops below a threshold level and the simulation for that photon is

terminated. In this example, termination occurred when the last significant fraction of

remaining photon weight escaped at the surface at the position indicated by the asterisk

(*). Many photon trajectories (104 to 106) are typically calculated to yield a statistical

description of photon distribution in the medium.

This manual is roughly divided into two major parts. Part I describes the principles

of Monte Carlo simulations of photon transport in tissues, how to realize the simulation in

ANSI Standard C, and some computation results and verifications. Part II provides users

detailed instructions of using mcml and modifying mcml to suit special need, where mcml

stands for Monte Carlo simulations for multi-layered tissues. The appendices furnish the

flow graph and the whole source code of mcml, and some other useful information.

Chapter 0 Introduction 3

2000

1500

1000

500

0

-1500 -1000

de pt

h z

(µ m

)

-500 0 500 1000 1500

*

position x (µm)

Figure 0.1. The movement of one photon through a homogenous medium,

as calculated by Monte Carlo simulation.

4 Chapter 1 The Problem and Coordinate Systems

Part I. Description of Monte Carlo Simulation

1. The Problem and Coordinate Systems

The Monte Carlo simulation described in this paper deals with the transport of an

infinitely narrow photon beam perpendicularly incident on a multi-layered tissue. Each

layer is infinitely wide, and is described by the following parameters: the thickness, the refractive index, the absorption coefficient µa, the scattering coefficient µs, and the

anisotropy factor g. The refractive indices of the top ambient medium (e.g., air) and

bottom ambient medium (if exists) have to be given as well. Although the real tissue can

never be infinitely wide, it can be so treated if it is much larger than the spatial extent of the photon distribution. The absorption coefficient µ a is defined as the probability of

photon absorption per unit infinitesimal pathlength, and the scattering coefficient µ s is

defined as the probability of photon scattering per unit infinitesimal pathlength. For the simplicity of notation, the total interaction coefficient µ t, which is the sum of the

absorption coefficient µa and the scattering coefficient µ s, is sometimes used.

Correspondingly, the interaction coefficient means the probability of photon interaction

per unit infinitesimal pathlength. The anisotropy g is the average of the cosine value of the

deflection angle (see Section 3.5).

Photon absorption, fluence, reflectance and transmittance are the physical

quantities to be simulated. The simulation propagates photons in three dimensions,

records photon deposition, A(x , y, z), (J/cm3 per J of delivered energy or cm−3) due to

absorption in each grid element of a spatial array, and finally calculates fluence, φ(x , y, z), (J/cm2 per J of delivered energy or cm−2) by dividing deposition by the local absorption coefficient, µa in cm−1: φ(x , y, z) = A(x , y, z)/µ a. Since the photon absorption and the

photon fluence can be converted back and forth through the local absorption coefficient of

the tissue, we only report the photon absorption in mcml (see Section 4.2 and Section

9.3). The photon fluence can be obtained by converting the photon absorption in another

program conv. The simulation also records the escape of photons at the top (and bottom)

surface as local reflectance (and transmittance) (cm–2sr–1) (see Section 4.1).

In this first version of mcml, we consider cylindrically symmetric tissue models.

Therefore, we chose to record photon deposition in a two-dimensional array, A(r, z)

although the photon propagation of this simulation is conducted in three-dimensions.

Chapter 1 The Problem and Coordinate Systems 5

Three coordinate systems are used in the Monte Carlo simulation at the same time.

A Cartesian coordinate system is used to trace photon packets. The origin of the

coordinate system is the photon incident point on the tissue surface, the z-axis is always

the normal of the surface pointing toward the inside of the tissue, and the xy-plane is

therefore on the tissue surface (Fig. 1.1). A cylindrical coordinate system is used to score

internal photon absorption A(r, z), where r and z are the radial and z axis coordinates of

the cylindrical coordinate system respectively. The Cartesian coordinate system and the

cylindrical coordinate system share the origin and the z axis. The r coordinate of the

cylindrical coordinate system is also used for the diffuse reflectance and total transmittance. They are recorded on tissue surface in Rd(r, α) and Tt(r, α) respectively,

where α is the angle between the photon exiting direction and the normal (–z axis for reflectance and z axis for transmittance) to the tissue surfaces. A moving spherical

coordinate system, whose z axis is aligned with the photon propagation direction

dynamically, is used for sampling of the propagation direction change of a photon packet.

In this spherical coordinate system, the deflection angle θ and the azimuthal angle ψ due to scattering are first sampled. Then, the photon direction is updated in terms of the

directional cosines in the Cartesian coordinate system (see Section 3.5).

For photon absorption, a two-dimensional homogeneous grid system is setup in z

and r directions. The grid line separations are ∆z and ∆r in z and r directions respectively. The total numbers of grid elements in z and r directions are Nz and Nr respectively. For

diffuse reflectance and transmittance, a two-dimensional homogeneous grid system is

setup in r and α directions. This grid system can share the r direction with the grid system for photon absorption. Therefore, we only need to set up an extra one dimensional grid

system for the diffuse reflectance and transmittance in the α direction. In our simulation, we always choose the range of α to be [0, π/2], i.e., 0 ≤ α ≤ π/2. The total number of grid elements is Nα. Therefore the grid line separation is ∆α = π/(2 Nα).

This is an appropriate time to mention that we always use cm as the basic unit of

length throughout the simulation for consistency. For example, the thickness of each layer

and the grid line separations in r and z directions are in cm. The absorption coefficient and

scattering coefficient are in cm−1.

6 Chapter 1 The Problem and Coordinate Systems

y

z

x

Photon Beam

Layer 1

Layer N

Layer 2

Fig. 1.1. A schematic of the Cartesian coordinate system set up on multi-

layered tissues. The y-axis points outward.

In some of the discussions, the arrays will simply be referenced by the location of

the grid element, e.g., (r, z) or (r, α), rather than by the indices of the grid element, although the indices are used in the program to reference array elements.

Chapter 2 Sampling Random Variables 7

2. Sampling Random Variables

The Monte Carlo method, as its name implies ("throwing the dice"), relies on the

random sampling of variables from well-defined probability distributions. Several books

(Cashew et al., 1959; Lux et al., 1991; and Kalos et al., 1986) provide good references

for the principles of Monte Carlo modeling. Let us briefly review the method for sampling

random variables in a Monte Carlo simulation.

Consider a random variable χ, which is needed by the Monte Carlo simulation of photon propagation in tissue. This variable may be the variable step size a photon will

take between photon-tissue interaction sites, or the angle of deflection a scattered photon

may experience due to a scattering event. There is a probability density function that

defines the distribution of χ over the interval (a, b). The probability density function is normalized such that:

⌡⌠ a

b p(χ) dχ = 1 (2.1)

To simulate propagation, we wish to be able to choose a value for χ repeatedly and randomly based on a pseudo-random number generator. The computer provides a

random variable, ξ, which is uniformly distributed over the interval (0, 1). The cumulative distribution function of this uniformly distributed random variable is:

Fξ(ξ) =    0 if ξ ≤ 0

ξ if 0 < ξ ≤ 1 1 if ξ > 1

(2.2)

To sample a generally non-uniformly distributed function p(χ), we assume there exists a nondecreasing function χ = f(ξ) (Kalos et al., 1986), which maps ξ ∈ (0, 1) to χ ∈ (a, b) (Fig. 2.1). The variable χ and variable ξ then have a one-to-one mapping. This subsequently leads to the following equality of probabilities:

P{f(0) < χ ≤ f(ξ1)} = P{0 < ξ ≤ ξ1} (2.3a)

or

P{a < χ ≤ χ1} = P{0 < ξ ≤ ξ1} (2.3b)

8 Chapter 2 Sampling Random Variables

According to the definition of cumulative distribution functions, Eq. 2.3b can be changed

to an equation of cumulative distribution functions:

Fχ(χ1) = Fξ(ξ1) (2.4)

Expanding the cumulative distribution function Fχ(χ1) in terms of the corresponding

probability density function for the left-hand side of Eq. 2.4 and employing Eq. 2.2 for the

right-hand side, we convert Eq. 2.4 into:

⌡⌠ a

χ1 p(χ) dχ = ξ1 for ξ1 ∈ (0, 1) (2.5)

Eq. 2.5 is then used to solve for χ1 to get the function f(ξ1). If the function χ =

f(ξ) is assumed nonincreasing, a similar derivation will lead to the counterpart of Eq. 2.5 as:

⌡⌠ a

χ1 p(χ) dχ = 1 – ξ1 for ξ1 ∈ (0, 1) (2.6)

However, since (1 − ξ1) and ξ1 have the same distribution, they can be interchanged.

Therefore, Eq. 2.5 and Eq. 2.6 are equivalent. In the following chapter, Eq. 2.5 will be

repeatedly invoked for sampling propagation variables.

The whole sampling process can be understood from Fig. 2.1. The key to the

Monte Carlo selection of χ using ξ is to equate the probability that ξ is in the interval [0, ξ1] with the probability that χ is in the interval [a, χ1]. In Fig. 2.1, we are equating the shaded area depicting the integral of p(χ) over [0, χ1] with the shaded area depicting the integral p(ξ) over [0, ξ1]. Keep in mind that the total areas under the curves p(χ) and p(ξ)

each equal unity, as is appropriate for probability density functions. The result is a one-to- one mapping between the upper boundaries ξ1 and χ1 based on the equality of the shaded areas in Fig. 2. In other words, we have equated Fχ(χ1) with Fξ(ξ1) (Eq. 2.4) which is equivalent to Eq. 2.5. The transformation process χ1 = f(ξ1) is shown by the arrows. For each ξ1, a χ1 is chosen such that the cumulative distribution functions for ξ1 and χ1 have

the same value. Correspondingly, the hatched areas are equal. It can also be seen in Fig.

2.1 that the monotonic function f(ξ) always exists because both cumulative distribution functions of ξ and χ are monotonic.

Chapter 2 Sampling Random Variables 9

p(ξ)

F (ξ)

0 a b

p(χ)

0

1

a bχ

0

1

0 1

ξ

0

1

0 1ξ

ξ F (χ)χ

χ1ξ1

χ f

Fig. 2.1. Sampling a random variable χ based on a uniformly distributed random variable ξ.

For example, consider the sampling of the step size for photon movement, s, which

is to be discussed fully in Section 3.2. The probability density function is given:

p(s) = µ t exp(–µ ts) (2.7)

where interaction coefficient µt equals µa + µ s. Using this function in Eq. 2.5 yields an

expression for a sampled value, s1, based on the random number ξ:

ξ = ⌡⌠ 0

s1 p(s) ds = ⌡⌠

0

s1 µ texp(–µ ts) ds = 1 – exp(µ ts1) (2.8)

Solving for the value s1:

s1 = –ln(1 – ξ)

µ t (2.9a)

As was explained in Eq. 2.6, the above expression is equivalent to:

10 Chapter 2 Sampling Random Variables

s1 = –ln(ξ)

µ t (2.9b)

Chapter 3 Rules for Photon Propagation 11

3. Rules for Photon Propagation

This chapter presents the rules that define photon propagation in the Monte Carlo

model as applied to tissues. The treatment is based upon Prahl et al. (1989) except that

we deal with a multi-layered tissue instead of a semi-infinite tissue.

3.1 Launching a photon packet

A simple variance reduction technique, implicit photon capture, is used to improve

the efficiency of the Monte Carlo simulation. This technique allows one to equivalently

propagate many photons as a packet along a particular pathway simultaneously. Each

photon packet is initially assigned a weight, W, equal to unity. The photon is injected

orthogonally into the tissue at the origin, which corresponds to a collimated arbitrarily

narrow beam of photons.

The current position of the photon is specified by the Cartesian coordinates (x, y,

z). The current photon direction is specified by a unit vector, r, which can be equivalently described by the directional cosines (µ x, µ y, µ z):

µ x = r • x

µ y = r • y

µ z = r • z

(3.1)

where x, y, and z are unit vectors along each axis. The photon position is initialized to (0,

0, 0,) and the directional cosines are set to (0, 0, 1). This description of photon position

and direction in a Cartesian coordinate system (Witt, 1977) turned out to be simpler than

the counterpart in a cylindrical coordinate system (Keijzer et al., 1989).

When the photon is launched, if there is a mismatched boundary at the tissue

surface, then some specular reflectance will occur. If the refractive indices of the outside medium and tissue are n1 and n2, respectively, then the specular reflectance, Rsp, is

specified (Born et al., 1986; Hecht, 1987):

Rsp = (n1 – n2)2

(n1 + n2)2 (3.2a)

12 Chapter 3 Rules for Photon Propagation

If the first layer is glass, which is on top of a layer of medium whose refractive index is n3,

multiple reflections and transmissions on the two boundaries of the glass layer are

considered. The specular reflectance is then computed by:

Rsp = r1 + (1–r1)2 r2 1–r1 r2

(3.2b)

where r1 and r2 are the Fresnel reflectances on the two boundaries of the glass layer:

r1 = (n1 – n2)2

(n1 + n2)2 (3.3)

r2 = (n3 – n2)2

(n3 + n2)2 (3.4)

Note that if the specular reflectance is defined as the probability of photons being

reflected without interactions with the tissue, then Eqs. 3.2a and 3.2b are not strictly

correct although they may be very good estimates of the real specular reflectance for thick

tissues. If we want to strictly distinguish the specular reflectance and the diffuse

reflectance, we can keep track of the number of interactions experienced by a photon

packet. When we score the reflectance, if the number of interactions is not zero, the

reflectance is diffuse reflectance. Otherwise, it is specular reflectance. The transmittances

can be distinguished similarly.

The photon weight is decremented by Rsp, and the specular reflectance Rsp will be

reported to the file of output data.

W = 1 – Rsp (3.5)

3.2 Photon's step size

The step size of the photon packet is calculated based on a sampling of the

probability distribution for photon's free path s ∈ [0, ∞), which means 0 ≤ s < ∞. According to the definition of interaction coefficient µ t, the probability of interaction per

unit pathlength in the interval (s', s' + ds') is:

µ t = – dP{s ≥ s'} P{s ≥ s'} ds'

(3.6a)

Chapter 3 Rules for Photon Propagation 13

or

d(ln(P{s ≥ s'})) = – µ t ds' (3.6b)

The above Eq. 3.6b can be integrated over s' in the range (0, s1), and lead to an

exponential distribution, where P{s ≥ 0} = 1 is used:

P{s ≥ s1} = exp(– µ t s1) (3.7)

Eq. 3.7 can be rearranged to yield the cumulative distribution function of free path s:

P{s < s1} = 1 – exp(– µ t s1) (3.8)

This cumulative distribution function can be assigned to the uniformly distributed random

number ξ as discussed in Chapter 2. The equation can be rearranged to provide a means of choosing step size:

s1 = –ln(1 –ξ)

µ t (3.9a)

or substituting ξ for (1–ξ):

s1 = –ln(ξ)

µ t (3.9b)

Eq. 3.9b gives a mean free path between photon-tissue interaction sites of 1/µ t because the

statistical average of –ln(ξ) is 1, i.e., <–ln (ξ)> = 1. There is another approach to obtain Eq. 3.9b. Employing Eq. 3.8, the probability density function of free path s is:

p(s1) = dP{s < s1}/ds1 = µ t exp(– µ t s1) (3.10)

p(s1) can be substituted into Eq. 2.5 to yield Eq. 3.9b, where the integration in Eq. 2.5 will

be recovered to Eq. 3.8.

In multi-layered turbid media, the photon packet may experience free flights over

multiple layers of media before an interaction occurs. In this case, the counterpart of Eq.

3.7 becomes:

P{s ≥ ssum} = exp(– ∑ i

µ ti si ) (3.11)

14 Chapter 3 Rules for Photon Propagation

where i is an index to a layer, the symbols µti is the interaction coefficient for the ith layer,

and si is the step size in the ith layer. The total step size ssum is:

ssum = ∑ i

si (3.12)

The summation is over all the layers in which the photon packet has traveled. Eq. 3.11

does not take photon reflection and transmission at boundaries into account because they

are processed separately. The sampling equation is obtained by equating Eq. 3.11 to ξ:

∑ i

µ ti si = – ln(ξ) (3.13)

As you may have seen, Eq. 3.9b is just a special case of Eq. 3.13. The sampling can be

interpreted as that the total dimensionless step size is –ln(ξ), where dimensionless step size is defined as the product of the dimensional step size si and the interaction coefficient µti.

Since the absorption coefficient and the scattering coefficient of a glass layer are zeros, it

does not contribute to the left hand side of Eq. 3.13. The detailed process of Eq. 3.13 will

be discussed in Section 3.6 and 3.7. Although Eq. 3.13 looks complicated, it is the

theoretical ground for the process in Section 3.6 and 3.7 which looks simpler.

From now on, we will use step size s instead of s1 or ssum for simplicity. Note that

this sampling method involves computation of a logarithm function, which is time-

consuming. This is reflected in Section 5.7. Faster methods can be used to avoid the

logarithmic computation (Ahrens et al., 1972; Marsaglia, 1961; MacLaren et al., 1964).

3.3 Moving the photon packet

Once the step size s is specified, the photon is ready to be moved in the tissue.

The position of the photon packet is updated by:

x ← x + µ x s

y ← y + µy s

z ← z + µ z s (3.14)

The arrows indicate quantity substitutions. The variables on the left hand side have the

new values, and the variables on the right hand side have the old values. In an actual

Chapter 3 Rules for Photon Propagation 15

program in C, an equal sign is used for this purpose. The simplicity of Eqs. 3.14 is a

major reason for using Cartesian coordinates.

3.4 Photon absorption

Once the photon has taken a step, some attenuation of the photon weight due to

absorption by the interaction site must be calculated. A fraction of the photon's current

weight, W, will be deposited in the local grid element. The amount of deposited photon

weight, ∆W, is calculated:

∆W = W µ a µ t

(3.15)

The total accumulated photon weight A(r, z) deposited in that local grid element is

updated by adding ∆W:

A(r, z) ← A(r, z) + ∆W (3.16)

The photon weight has to be updated as well by:

W ← W – ∆W (3.17)

The photon packet with the new weight W will suffer scattering at the interaction

site (discussed later). Note that the whole photon packet experiences interaction at the

end of the step, either absorption or scattering.

3.5 Photon scattering

Once the photon packet has been moved, and its weight decremented, the photon

packet is ready to be scattered. There will be a deflection angle, θ ∈ [0, π), and an azimuthal angle, ψ ∈ [0, 2 π) to be sampled statistically. The probability distribution for the cosine of the deflection angle, cosθ, is described by the scattering function 1) that Henyey and Greenstein (1941) originally proposed for galactic scattering:

p(cosθ) = 1 – g2

2 (1 + g2 – 2gcosθ)3/2 (3.18)

1) Note that the scattering function we defined here is a probability density function of cosθ. It has a

difference of a constant 1/2 with the phase function defined by van de Hulst (1980).

16 Chapter 3 Rules for Photon Propagation

where the anisotropy, g, equals <cosθ> and has a value between –1 and 1. A value of 0 indicates isotropic scattering and a value near 1 indicates very forward directed scattering.

Jacques et al. (1987) determined experimentally that the Henyey-Greenstein function

described single scattering in tissue very well. Values of g range between 0.3 and 0.98 for

tissues, but quite often g is ~0.9 in the visible spectrum. Applying Eq. 2.5, the choice for

cosθ can be expressed as a function of the random number, ξ:

cosθ =

   1

2g   

  

1 + g2 –   

  1 – g2

1 – g + 2g ξ

2

if g > 0

2 ξ – 1 if g = 0

(3.19)

Next, the azimuthal angle, ψ, which is uniformly distributed over the interval 0 to 2π, is sampled:

ψ = 2π ξ (3.20)

Once the deflection angle and azimuthal angle are chosen, the new direction of the

photon packet can be calculated:

µ'x = sinθ

1 – µ

2 z

(µ x µ z cosψ – µ y sinψ) + µ x cosθ

µ'y = sinθ

1 – µ

2 z

(µ y µ z cosψ + µ x sinψ) + µ y cosθ

µ'z = –sinθ cosψ

1 – µ 2 z + µ z cosθ

(3.21)

If the angle of the photon packet is too close to normal of the tissue surfaces(e.g., |µ z| >

0.99999), then the following formulas should be used:

µ'x = sinθ cosψ

µ'y = sinθ sinψ

µ'z = SIGN(µ z) cosθ

(3.22)

Chapter 3 Rules for Photon Propagation 17

where SIGN(µ z) returns 1 when µ z is positive, and it returns –1 when µ z is negative.

Finally, the current photon direction is updated: µ x = µ' x, µ y = µ' y, µ z = µ' z.

In the sampling of the two angles θ and ψ and the updating of the directional cosines, trigonometric operations are involved. Because trigonometric operations are

computation-intensive, we should try to avoid them whenever possible. The detailed

process of the sampling can be found in the function Spin() written in the file "mcmlgo.c"

(See Appendix A and Appendix B.4).

3.6 Reflection or transmission at boundary

During a step, the photon packet may hit a boundary of the tissue, which is

between the tissue and the ambient medium, where the step size s is computed by Eq.

3.9b. For example, the photon packet may attempt to escape the tissue at the air/tissue

interface. If this is the case, then the photon packet may either escape as observed

reflectance (or transmittance if a rear boundary is also included) or be internally reflected

by the boundary. There are different methods of dealing with this problem when the step

size is large enough to hit the boundary. Let us present one of the two approaches used in

the program mcml first.

First, a foreshortened step size s1 is computed:

s1 =    (z – z0)/µ z if µ z < 0 (z – z1)/µ z if µ z > 0

(3.23)

where z0 and z1 are the z coordinates of the upper and lower boundaries of the current

layer (See Fig. 1.1 for the Cartesian coordinate system). The foreshortened step size s1 is

the distance between the current photon location and the boundary in the direction of the photon propagation. Since the photon direction is parallel with the boundary when µ z is

zero, the photon will not hit the boundary. Therefore, Eq. 3.23 does not include the case when µz is zero. We move the photon packet s1 to the boundary with a flight free of

interactions with the tissue (see Section 3.3 for moving photon packet). The remaining step size to be taken in the next step is updated to s ← s – s1. The photon packet will

travel the remaining step size if being internally reflected.

Second, we compute the probability of a photon packet being internally reflected, which depends on the angle of incidence, αi, onto the boundary, where αi = 0 means orthogonal incidence. The value of αi is calculated:

18 Chapter 3 Rules for Photon Propagation

αi = cos–1(|µ z|) (3.24)

Snell's law indicates the relationship between the angle of incidence, αi, the angle of transmission, αt, and the refractive indices of the media that the photon is incident from, ni, and transmitted to, nt:

ni sinαi = nt sinαt (3.25)

The internal reflectance, R(αi), is calculated by Fresnel's formulas (Born et al., 1986;

Hecht, 1987):

R(αi) = 1 2 

 

  sin2(ai –at)

sin2(ai +at) +

tan2(ai –at)

tan2(ai +at) (3.26)

which is an average of the reflectances for the two orthogonal polarization directions.

Third, we determine whether the photon is internally reflected by generating a

random number, ξ, and comparing the random number with the internal reflectance, i.e.:

If ξ ≤ R(αi), then photon is internally reflected;

If ξ > R(αi), then photon escapes the tissue (3.27)

If the photon is internally reflected, then the photon packet stays on the surface and its directional cosines (µx, µ y, µ z) must be updated by reversing the z component:

(µ x, µ y, µ z) ← (µ x, µ y, –µ z) (3.28)

At this point, the remaining step size has to been checked again. If it is large

enough to hit the other boundary, we should repeat the above process. If it hits a

tissue/tissue interface, we will have to process it according to the following section.

Otherwise, if the step size is small enough to fit in this layer of tissue, the photon packet

will move with the small step size. At the end of this small step, the absorption and

scattering are processed correspondingly.

On the other hand, if the photon packet escapes the tissue, the reflectance or transmittance at the particular grid element (r, αt) must be incremented. The reflectance, Rd(r, αt), or transmittance, Tt(r, αt), is updated by the amount of escaped photon weight,

W:

Chapter 3 Rules for Photon Propagation 19

Rd(r, αt) ← Rd(r, αt) + W if z = 0

Tt(r, αt) ← Tt(r, αt) + W if z = the bottom of the tissue. (3.29)

Since the photon has completely escaped, the tracing of this photon packet ends here. A

new photon may be launched into the tissue and traced thereafter. Note that in our

simulation, both unscattered transmittance, if any, and diffuse transmittance are scored into Tt(r, αt) without distinction although they could be distinguished as we discussed in

Section 3.1.

An alternative approach toward modeling internal reflectance deserves mention.

Rather than making the internal reflection of the photon packet an all-or-none event, a

partial reflection approach can be used each time a photon packet strikes the surface boundary. A fraction 1 – R(αi) of the current photon weight successfully escapes the tissue, and increments the local reflectance or transmittance array, e.g., Rd(r, αt) ← Rd(r, αt) + W (1–R(αi)). All the rest of the photon weight will be reflected, and the photon weight is updated as W ← W R(αi).

Both approaches are available in the program mcml, and the users have the option

to use either approach depending on the physical quantities that they want to score. A

flag in the program can be changed to switch between these two approaches (see Section

5.2). The all-or-none approach is faster, but the partial reflection approach should be able

to reduce the variance of the reflectance or transmittance. It is uninvestigated how much

variance can be reduced by using the partial reflection approach.

Similar to Section 3.5, the number of trigonometric operations in Eqs. 3.24-3.26

should be minimized for the sake of computation speed. The detailed process of these

computations can be found in the function RFresnel() written in the file "mcmlgo.c" (See

Appendix A and Appendix B.4).

3.7 Reflection or transmission at interface

If a photon step size is large enough to hit a tissue/tissue interface, this step may

cross several layers of tissue. Consider a photon packet that attempts to make a step of size s within tissue 1 with µa1, µ s1, n1, but hits an interface with tissue 2 with µa2, µ s2, n2 after a foreshortened step s1. Similar to the discussion in the last section, the photon

packet is first moved to the interface without interactions, and the remaining photon step size to be taken in the next step is updated to s ← s – s1. Then, we have to determine

20 Chapter 3 Rules for Photon Propagation

statistically whether the photon packet should be reflected or transmitted according to the

Fresnel's formulas. If the photon packet is reflected, it is processed the same way as in the

last section. However, if the photon packet is transmitted to the next layer of tissue, it has

to continue propagation instead of being terminated. Based on Eq. 3.13, the remaining

step size has to be converted for the new tissue according to its optical properties:

s ← s µ t1 µ t2

(3.30)

where µ t1 and µ t2 are the interaction coefficients for tissue 1 and tissue 2 correspondingly.

The current step size s is again checked for another boundary or interface crossing. The

above process is repeated until the step size is small enough to fit in one layer of tissue.

At the end of this small step, the absorption and scattering are processed correspondingly.

If the photon packet is in a layer of glass, the photons are moved to the boundary

of the glass layer without updating the remaining photon step size because the path length

in the glass layer does not contribute to the left hand side of Eq. 3.13. It is important to

understand that if a photon packet traverses several layers of tissues, the use of Eq. 3.9b

for the step size and the repetitive uses of Eq. 3.30 are based on Eq. 3.13.

3.8 Photon termination

After a photon packet is launched, it can be terminated naturally by reflection or

transmission out of the tissue. For a photon packet that is still propagating inside the

tissue, if the photon weight, W, has been sufficiently decremented after many steps of

interaction such that it falls below a threshold value (e.g., Wth = 0.0001), then further

propagation of the photon yields little information unless you are interested in the very late

stage of the photon propagation. However, proper termination must be executed to

ensure conservation of energy (or number of photons) without skewing the distribution of

photon deposition. A technique called roulette is used to terminate the photon packet

when W ≤ Wth. The roulette technique gives the photon packet one chance in m (e.g., m = 10) of surviving with a weight of mW. If the photon packet does not survive the

roulette, the photon weight is reduced to zero and the photon is terminated.

W =   mW if ξ ≤ 1/m 0 if ξ > 1/m

(3.31)

Chapter 3 Rules for Photon Propagation 21

where ξ is the uniformly distributed pseudo-random number (see Chapter 2). This method conserves energy yet terminates photons in an unbiased manner. The combination of

photon roulette and splitting that is contrary to roulette, may be properly used to reduce

variance (Hendricks et al., 1985).

22 Chapter 4 Scored Physical Quantities

4. Scored Physical Quantities

As we mentioned earlier, we record the photon reflectance, transmittance, and

absorption during the Monte Carlo simulation. In this chapter, we will discuss the

process of these physical quantities in detail. The dimensions of some of the quantities are

shown in square brackets at the end of their formulas.

The last cells in z and r directions require special attention. Because photons can

propagate beyond the grid system, when the photon weight is recorded into the diffuse

reflectance or transmittance array, or absorption array, the physical location may not fit

into the grid system. In this case, the last cell in the direction of the overflow is used to

collect the photon weight. Therefore, the last cell in the z and r directions do not give the

real value at the corresponding locations. However, the angle α is always within the bound we choose for it, i.e., 0 ≤ α ≤ π/2, hence does not cause a problem in the scoring of diffuse reflectance and transmittance.

4.1 Reflectance and transmittance

When a photon packet is launched, the specular reflectance is computed

immediately. The photon weight after the specular reflection is transmitted into the tissue.

During the simulation, some photon packets may exit the media and their weights are

accordingly scored into the diffuse reflectance array or the transmittance array depending

on where the photon packet exits. After tracing multiple photon packets (N), we have two scored arrays Rd(r, α) and Tt(r, α) for diffuse reflectance and transmittance respectively. They are internally represented by Rd-rα[ir, iα] and Tt-rα[ir, iα] respectively

in the program. The coordinates of the center of a grid element are computed by:

r = (ir + 0.5) ∆r [cm] (4.1)

α = (iα + 0.5) ∆α [rad] (4.2)

where ir and iα are the indices for r and α. The raw data give the total photon weight in

each grid element in the two-dimensional grid system. To get the total photon weight in

the grid elements in each direction of the two-dimensional grid system, we sum the 2D

arrays in the other dimension:

Chapter 4 Scored Physical Quantities 23

Rd-r[ir] = ∑ iα=0

Nα–1 Rd-rα [ir, iα] (4.3)

Rd-α[iα] = ∑ ir=0

Nr–1 Rd-rα [ir, iα] (4.4)

Tt-r[ir] = ∑ iα=0

Nα–1 Tt-rα [ir, iα] (4.5)

Tt-α[iα] = ∑ ir=0

Nr–1 Tt-rα [ir, iα] (4.6)

To get the total diffuse reflectance and transmittance, we sum the 1D arrays again:

Rd = ∑ ir=0

Nr–1 Rd-r [ir] (4.7)

Tt = ∑ ir=0

Nr–1 Tt-r [ir] (4.8)

All these arrays give the total photon weight per grid element, based on N initial photon packets with weight unity. To convert Rd-rα[ir, iα] and Tt-rα[ir, iα] into photon

probability per unit area perpendicular to the photon direction per solid angle, they are

divided by the projection of the annular area onto a plane perpendicular to the photon

exiting direction (∆a cosα), the solid angle (∆Ω) spanned by a grid line separation in the α direction around an annular ring, and the total number of photon packets (N):

Rd-rα[ir, iα] ← Rd-rα[ir, iα] / (∆α cosα ∆Ω N) [cm–2 sr–1] (4.9)

Tt-rα[ir, iα] ← Tt-rα[ir, iα] / (∆α cosα ∆Ω N) [cm–2 sr–1] (4.10)

24 Chapter 4 Scored Physical Quantities

where

∆α = 2 π r ∆r = 2 π (ir + 0.5) (∆r)2 [cm2] (4.11)

∆Ω = 4 π sinα sin(∆α/2) = 4 π sin[(iα + 0.5) ∆α] sin(∆α/2) [sr] (4.12)

where r and α are computed from Eq. 4.1 and Eq. 4.2 respectively. The radially resolved diffuse reflectance Rd-r[ir] and total transmittance Tt-r[ir] are divided by the area of the

annular ring (∆a) and the total number of photon packets (N) to convert them into photon probability per unit area:

Rd-r[ir] ← Rd-r[ir] / (∆α N) [cm–2] (4.13)

Tt-r[ir] ← Tt-r[ir] / (∆α N) [cm–2] (4.14)

The angularly resolved diffuse reflectance Rd-α[iα] and total transmittance Tt-α[iα] are

divided by the solid angle (∆Ω) and the total number of photon packets (N) to convert them into photon probability per unit solid angle:

Rd-α[iα] ← Rd-α[iα] / (∆Ω N) [sr–1] (4.15)

Tt-α[iα] ← Tt-α[iα] / (∆Ω N) [sr–1] (4.16)

The total diffuse reflectance and transmittance are divided by the total number of photon

packets (N) to get the probabilities:

Rd ← Rd / N [–] (4.17)

Tt ← Tt / N [–] (4.18)

where [–] means dimensionless units.

4.2 Internal photon distribution

During the simulation, the absorbed photon weight is scored into the absorption array A(r, z). A(r, z) is internally represented by a 2D array Arz[ir, iz], where ir and iz are

the indices for grid elements in r and z directions. The coordinates of the center of a grid

element can be computed by Eq. 4.1 and the following:

Chapter 4 Scored Physical Quantities 25

z = (iz + 0.5) ∆z (4.19)

The raw data Arz[ir, iz] give the total photon weight in each grid element in the two-

dimensional grid system. To get the total photon weight in each grid element in the z

direction, we sum the 2D array in the r direction:

Az[iz] = ∑ ir=0

Nr–1 Arz [ir, iz] (4.20)

The total photon weight absorbed in each layer Al[layer] and the total photon weight

absorbed in the tissue A can be computed from Az[iz]:

Al[layer] = ∑ iz in layer

Az [iz] (4.21)

A = ∑ iz=0

Nz–1 Az [iz] (4.22)

where the summation range "iz in layer" includes all iz's that lead to a z coordinate in the

layer. Then, these quantities are scaled properly to get the densities:

Arz[ir, iz] ← Arz[ir, iz] / (∆α ∆z N) [cm–3] (4.23)

Az[iz] ← Az[iz] / (∆z N) [cm–1] (4.24)

Al[layer] ← Al[layer] / N [–] (4.25)

A ← A / N [–] (4.26)

The quantity A gives the photon probability of absorption by the tissue. The 1D array Al[layer] gives the photon probability of absorption in each layer. At this point, Arz[ir, iz]

gives the absorbed photon probability density (cm−3), and can be converted into photon fluence, φrz, (cm−2) by dividing it by the absorption coefficient µa (cm−1) of the layer

where the current location resides:

26 Chapter 4 Scored Physical Quantities

φrz[ir, iz] = Arz[ir, iz] / µa [cm–2] (4.27)

The 1D array Az[iz] gives the photon probability per unit length in the z direction (cm−1).

It can also be divided by the absorption coefficient µa (cm−1) to yield a dimensionless quantity φz[iz]:

φz[iz] = Az[iz] / µa [–] (4.28)

This quantity may seem hard to understand or redundant at first glance. However, the

summation of the raw data in Eq. 4.20 is equivalent to the convolution for an infinitely

wide flat beam in Eq. 7.15 to be discussed in Chapter 7. The equivalence can be shown as follows. According to Eqs. 4.20, 4.23 and 4.24, the final converted Az[iz] and Arz[ir, iz]

have the following relation:

Az[iz] = ∑ ir=0

Nr–1 Arz [ir, iz] ∆α(ir) (4.29)

where ∆a(ir) is computed in Eq. 4.11, but we stress that it is a function of ir in Eq. 4.29.

Employing Eqs. 4.27 and 4.28, Eq. 4.29 can be converted to:

φz[iz] = ∑ ir=0

Nr–1 frz [ir, iz] ∆α(ir) (4.30)

This is a numerical solution of the following integral:

φz(z) = ⌡⌠ 0

∞ frz(r, z) 2 π r dr (4.31)

Eq. 4.31 is essentially Eq. 7.15 for an infinitely wide flat beam with a difference of

constant S, where S is the power density of the infinitely wide flat beam. The equivalence between Eq. 4.31 and Eq. 7.15 can be seen after substituting φz(z) for F(r, z), φrz(r, z) for G(r'', z), and r for r''. Therefore, φz[iz] gives the fluence for an infinitely wide flat beam

with a difference of a constant which is the power density S.

The program mcml will only report Arz[ir, iz] and Az[iz] instead of φrz[ir, iz] and φz[iz]. The program conv will be set up to convert Arz[ir, iz] and Az[iz] into φrz[ir, iz] and φz[iz].

Chapter 4 Scored Physical Quantities 27

4.3 Issues regarding grid system

In our Monte Carlo simulation, we always set up a grid system. The computation

results will be limited by the finite grid size. This section will discuss what is the best that

one can do.

Position of average value for each grid element

The simulation provides the average value of the scored physical quantities in each

grid element. Now, the question is at what position should that averaged value be

assigned? One can argue that there is no best point because the exact answer to the

physical quantities is unknown. However, under linear approximations of the physical

quantities in each grid element, we can find the best point for each grid point. The linear

approximations can be justified for small grid size in most cases because the higher order

terms are considerably less than the linear term. Some special occasions will be discussed

subsequently.

Let us discuss the grid system in the r direction first because r is the variable over

which the convolution for photon beams of finite size will be implemented (see Chapter 7).

The grid system in the r direction uses ∆r as the grid separation with a total of N grid elements. The index to each grid element is denoted by n. The center of each grid element is denoted by rn:

rn = (n + 0.5) ∆r (4.32)

As we have mentioned, the Monte Carlo simulation actually approximates the

average of the physical quantity Y(r) in each grid element, where Y(r) can be the diffuse reflectance, diffuse transmittance, and internal fluence Arz(r, z) for a particualr z value.

<Y(r)> = 1

2 π rn ∆r

⌡   ⌠

rn – ∆r/2

rn + ∆r/2

Y(r) 2 π r dr (4.33)

where 2 π rn ∆r is the area of the ring or the circle when n = 0.

If Y(r) in each grid element is approximated linearly, we can prove that there exists a best point rb to satisfy:

28 Chapter 4 Scored Physical Quantities

<Y(r)> = Y(rb) (4.34)

where

rb = rn + ∆r

12 rn ∆r (4.35)

Proof: Y(r) is approximated by a Taylor series about rb expanded to the first order:

Y(r) = Y(rb) + (r – rb) Y'(rb) (4.36)

Substituting Eq. 4.36 into Eq. 4.33 yields:

<Y(r)> = 1

2 π rn ∆r

⌡   ⌠

rn – ∆r/2

rn + ∆r/2

[Y(rb) + (r – rb) Y'(rb)] 2 π r dr

= Y(rb)

2 π rn ∆r

⌡   ⌠

rn – ∆r/2

rn + ∆r/2

2 π r dr + Y'(rb) 2 π rn ∆r

⌡   ⌠

rn – ∆r/2

rn + ∆r/2

(r – rb) 2 π r dr

= Y(rb)

2 π rn ∆r [π r2]

rn + ∆r/2

rn – ∆r/2 +

Y'(rb) 2 π rn ∆r

(π/3) [2 r3 – 3 rb r2] rn + ∆r/2

rn – ∆r/2

= Y(rb)

2 π rn ∆r [2 π rn ∆r] +

Y'(rb) 2 π rn ∆r

(π/3) [2 ∆r (3 rn2 + (∆r)2

4 – 3 rb rn)]

= Y(rb) + Y'(rb) [rn + (∆r)2 12 rn

– rb]

or

<Y(r)> = Y(rb) + Y'(rb) [rn + (∆r)2 12 rn

– rb] (4.37)

If we set the term in the square bracket in Eq. 4.37 to zero, and solve for rb, we obtain:

Chapter 4 Scored Physical Quantities 29

rb = rn + (∆r)2 12 rn

(4.38a)

and Eq. 4.37 becomes:

<Y(r)> = Y(rb) (4.39)

Eq. 4.38a can be reformulated:

rb = rn + ∆r

12 rn ∆r (4.38b)

Q.E.D.

We can substitute Eq. 4.32 into the second term of Eq. 4.38b:

rb = [(n + 0.5) + 1

12 (n + 0.5) ] ∆r (4.38c)

When n = 0, rb = [0.5 + 1 6 ] ∆r =

2 3 ∆r

When n = 1, rb = [1.5 + 1 18 ] ∆r ≈ 1.556 ∆r

When n = 2, rb = [2.5 + 1 30 ] ∆r ≈ 2.533 ∆r

When n = 3, rb = [3.5 + 1 42 ] ∆r ≈ 3.524 ∆r

When n = 4, rb = [4.5 + 1 54 ] ∆r ≈ 4.519 ∆r

...

It is observed that the best point deviates from the center of each grid element, and

the smaller the index to the grid box, the larger the deviation. As the index n becomes

large, the best point approaches the center of the grid element. This behavior is due to the

2 π r factor in Eq. 4.33. A similar factor does not exist for the z direction, and the best points for the z direction should be the centers of each grid element.

30 Chapter 4 Scored Physical Quantities

The computation results of a Monte Carlo simulation with a selected grid system

always have finite precision which is fundamentally limited by the finite grid size. The

above theorem only gives the points where the function values are best represented by the

simulated results.

Effects of the first photon-tissue interaction

The above theorem assumed differentiability of Y(r), where Y(r) can represent the diffuse reflectance Rd(r), the diffuse transmittance Td(r), and the internal fluence φrz(r, z)

at a particular z value. This assumption should hold for the diffuse reflectance and the

diffuse transmittance. However, for the internal fluence, the on-z-axis fluence is a delta

function for an impulse response (responses to an infinitely narrow photon beam). Therefore, you cannot assume the differentiability of the function φrz(r, z) at r equal zero, i.e., φrz(r=0, z). The best solution to this problem has been provided by Gardner et al.

(1992b). They keep track of the first photon interactions with the medium, which are

always on the z-axis, separately from the rest of the interactions. Therefore, the function φrz(r, z) will not include the first photon-tissue interaction which yield a delta function. This approach ensures a differentiable φrz(r, z) at r equal zero besides its better accuracy.

In the current version (version 1.0) of mcml, Gardner et al.'s approach is not yet

implemented although we intend to add this in the later version. However, the result from

mcml will still be correct within the spatial resolution of the grid size in the r direction.

Although nobody will use Monte Carlo to simulate the responses in an absorption-only

semi-infinite medium, let us use this simple example to illustrate what we mean. In this

case, Gardner et al.'s approach will yield an exponentially decaying response on the z axis

which is a delta function of r. The version 1.0 of mcml will yield the same exponentially

decaying response in the first grid elements which is not a delta function because of the

finite volume of the grid elements. However, as the grid separation ∆r is made sufficiently small, the result approaches Gardner et al.'s.

So far the discussion has nothing to do with convolution for photon beams of finite

size which will be discussed in Chapter 7. According to Gardner et al. (1992b), the error

in the convolution caused by not scoring the first interactions separately will be small for

Gaussian beams whose radius is at least three times larger than the grid separation ∆r. As we will discuss in Section 7.4, our extended trapezoidal integration, instead of integrating

over the original grid points, in the convolution program conv should further decrease the

error. Of course, when the on-z-axis interactions are considered separately, our

Chapter 4 Scored Physical Quantities 31

convolution program conv is not subject to this limitation on the radius of the Gaussian

beam. In contrast, if the integration is approximated by computing the integrand over the

original grid points, the radius of the photon beam should still be large enough to yield

reasonable integration accuracy.

Since this theorem is a late development, it has not been implemented in the

programs (mcml and conv) yet. We plan to implement it in the next release (see Appendix

H).

32 Chapter 5 Programming mcml

5. Programming mcml

The simulation is written in ANSI Standard C (Plauger et al., 1989, Kelley et al.,

1990), which makes it possible to execute the program on any computers that support

ANSI Standard C. So far, the program has been successfully tested on Macintosh, IBM

PC compatibles, Sun SPARCstation 2, IBM RISC/6000 POWERstation 320, and Silicon

Graphics IRIS workstation. This chapter mainly describes several rules, the important

constants and the data structures used in the program, the algorithm to trace photon

packets, flow of the program, and the timing profile of the program. Prior knowledge of

C is assumed to fully understand this chapter. The flow of the program is listed in detail in

Appendix A. The complete source code is listed in Appendix B file by file. In Appendix

C, we have provided a make file used by make utilities on UNIX machines. Appendix D

and E are a template of input data file and a sample output data file respectively.

Appendix F gives several C Shell scripts for UNIX users. The information needed to

obtain the program is detailed in Appendix G.

5.1 Programming rules and conventions

When we write this program, we have followed the following rules and

conventions:

1. Conform to ANSI Standard C so that the program can be executed on a variety of

computers.

2. Avoid global variables whenever possible. This program has not defined any

global variables so far.

3. Avoid hard limits on the program. For example, we removed the limits on the

number of array elements by dynamic allocation at run time. This means that the

program can accept any number of layers or gridlines as long as the memory

permits.

4. Preprocessor names are all capital letters, e.g.,

#define PREPROCESSORS

5. For global variables, function names, or data types, first letter of each word is

capital, and words are connected without underscores, e.g.,

Chapter 5 Programming mcml 33

short GlobalVar;

6. For dummy variables, first letter of each word is capital, and words are connected

by underscores, e.g.,

void NiceFunction(char Dummy_Var);

7. Local variables are all lower cases, and words are connected by underscores, e.g.,

short local_var;

5.2 Several constants

There are several important constants defined in the header file mcml.h or the

source files mcmlmain.c and mcmlgo.c. They are listed in Table 5.1, and may be altered

according to your special need.

The constant STRLEN is used in the program to define string length. WEIGHT is the

threshold photon weight, below which the photon packet will go through a roulette. This

photon packet with small weight W has a chance of CHANCE to survive with a new weight

of W/CHANCE. COSZERO and COS90D are used for the computation of reflection with

Fresnel's formulas, and for the computation of new photon directional cosines. When

|cosα| > COSZERO, α is considered very close to 0 or 180o. When |cosα| < COS90D, α is considered very close to 90o.

When THINKCPROFILER is 1, the profiler of THINK C compiler (Symantec, 1991)

on Macintosh can be used to monitor the timing profile of the program regarding each

function (see Section 5.7). When GNUCC is 1, the code can be compiled with GNU C

compiler (gcc) from the Free Software Foundation, which does not support several

functions such as difftime() although being claimed to conform to ANSI C standards.

Therefore, the timing modules in the program will not work when GNUCC is 1, although the

program will otherwise operate normally. When STANDARDTEST is 1, the random number

generator will generate a fixed sequence of random numbers after being fed a fixed seed.

This feature is used to debug the program. STANDARDTEST should be set to 0 normally,

which makes the random number generator use the current time as the seed. When

PARTIALREFLECTION is set to 0, the all-or-none simulation mechanism of photon internal

reflection at a boundary (e.g., air/tissue boundary) described in Section 3.6 is used, where

the photon packet is either totally reflected or totally transmitted determined by the

comparison of the Fresnel reflectance and a random number. Otherwise when

PARTIALREFLECTION is set to 1, the photon packet will be partially reflected and

34 Chapter 5 Programming mcml

transmitted. Normally we set PARTIALREFLECTION to 0 because the all-or-none simulation

is faster.

Table 5.1. Important constants in the program mcml.

5.3 Data structures and dynamic allocations

Data structures are an important part of the program. Related parameters are

logically organized by structures such that the program is easier to write, read, maintain,

and modify. The parameters for a photon packet are grouped into a single structure

defined by:

typedef struct { double x, y, z; /* Cartesian coordinates.[cm] */ double ux, uy, uz;/* directional cosines of a photon. */ double w; /* weight. */ Boolean dead; /* 1 if photon is terminated. */ short layer; /* index to layer where the photon packet resides.*/ double s; /* current step size. [cm]. */ double sleft; /* step size left. dimensionless [-]. */ } PhotonStruct;

The location and the traveling direction of a photon packet are described by the Cartesian

coordinates x, y, z, and the directional cosines (ux, uy, uz) respectively. The current

weight of the photon packet is denoted by the structure member w. The member dead,

initialized to be 0 when the photon packet is launched, represents the status of a photon

packet. If the photon packet has exited the tissue or it has not survived a roulette when its

weight is below the threshold weight, then the member dead is set to 1. It is used to signal

Constants File Value Meaning

WEIGHT mcml.h 1×10 –4 threshold weight

CHANCE mcml.h 0.1 chance of surviving a roulette

STRLEN mcml.h 256 string length

COSZERO mcmlgo.c 1–1×10–12 cosine of ~ 0

COS90D mcmlgo.c 1×10–6 cosine of ~ 90o

THINKCPROFILER mcmlmain.c 1/0 switch for THINK C profiler on

Macintosh

GNUCC mcmlmain.c 1/0 switch for GNU C compiler

STANDARDTEST mcmlgo.c 1/0 switch for fixed sequence of

random numbers

PARTIAL-

REFLECTION

mcmlgo.c 1/0 switch for partial internal reflection

at boundary

Chapter 5 Programming mcml 35

the program to stop tracing the current photon packet. Note that the type Boolean is not

an internal data type in ANSI Standard C. It is defined to be type char in our header file

mcml.h. The member layer is the index to the layer where the photon packet resides.

Type short, whose value ranges between –32768 (215) and +32768 (Plauger et al., 1989),

is used for the member layer. It is defined for computation efficiency although the layer

can always be identified according to the Cartesian coordinates of the photon packet and

the geometric structure of the media. The member layer is updated only when the photon

packet crosses tissue/tissue interfaces. The member s is the step size in cm for the current

step. The member sleft is used to store the unfinished step size in dimensionless units

when a step size is large enough to hit a boundary or interface. For example, if a selected

step size s is long enough to hit a boundary of the current layer with interaction coefficient µ t as explained in Section 3.6, a foreshortened step size s1 is chosen as the current step

size, and the unfinished step size has to be stored. In the program, the following assignments are implemented: the member s = s1 and the member sleft = (s – s1) µ t.

Note that we store the unfinished step size in dimensionless unit, therefore, we only need

to know the interaction coefficient of the current layer to convert the step size back in cm

when the photon packet crosses layers.

The parameters that are needed to describe a layer of tissue are grouped into one

structure:

typedef struct { double z0, z1; /* z coordinates of a layer. [cm] */ double n; /* refractive index of a layer. */ double mua; /* absorption coefficient. [1/cm] */ double mus; /* scattering coefficient. [1/cm] */ double g; /* anisotropy. */

double cos_crit0, cos_crit1; } LayerStruct;

The Cartesian coordinates of the top and bottom boundaries are denoted by z0 and z1

respectively. The refractive index, absorption coefficient, scattering coefficient, and

anisotropy factor of a layer of medium are represented by the members n, mua, mus, and g

respectively. The cosines of the critical angles are denoted by the members cos_crit0 and

cos_crit1 respectively. They are computed with the relative refractive index of this layer

with respect to the two neighbor layers.

All the input parameters are defined in the following structure. Most of the

members of the structure are provided by the user before the simulation.

typedef struct { char out_fname[STRLEN]; /* output filename. */

36 Chapter 5 Programming mcml

char out_fformat; /* output file format. */ /* 'A' for ASCII, */ /* 'B' for binary. */ long num_photons; /* to be traced. */ double Wth; /* play roulette if photon */ /* weight < Wth.*/

double dz; /* z grid separation.[cm] */ double dr; /* r grid separation.[cm] */ double da; /* alpha grid separation. */ /* [radian] */ short nz; /* array range 0..nz-1. */ short nr; /* array range 0..nr-1. */ short na; /* array range 0..na-1. */

short num_layers; /* number of layers. */ LayerStruct * layerspecs; /* layer parameters. */ } InputStruct;

The filename for data output is out_fname, and its format (out_fformat) can be A for

ASCII or B for binary. Currently, only ASCII format is supported. The number of

photon packets to be simulated is denoted by the member num_photons. Since larger

number of photon packets may be simulated, type long int, whose value ranges between

–2,147,483,648 (231) and +2,147,483,648 (Plauger et al, 1989), is used for the member

num_photons. The threshold weight is denoted by the member Wth. The photon packet

with weight less than Wth will experience a roulette. The grid line separations ∆z, ∆r, and ∆α are represented by members dz, dr, and da respectively. The numbers of grid elements Nz, Nr, and Nα are represented by nz, nr, and na respectively. The total

number of layers is represented by the member num_layers. The member layerspecs is a

pointer to the structure LayerStruct. This pointer can be dynamically allocated with an

array of structures, in which each structure represents a layer, or the top ambient medium,

or the bottom ambient medium (e.g., air). Therefore, there are (num_photons + 2)

elements in the array, where the element 0 and element (num_photons + 1) store the

refractive indices for the top ambient medium and bottom ambient medium respectively.

The dynamic allocation will be discussed subsequently.

All the output data are organized into one structure too:

typedef struct { double Rsp; /* specular reflectance. [-] */ double ** Rd_ra; /* 2D distribution of diffuse */ /* reflectance. [1/(cm2 sr)] */ double * Rd_r; /* 1D radial distribution of diffuse */ /* reflectance. [1/cm2] */ double * Rd_a; /* 1D angular distribution of diffuse */ /* reflectance. [1/sr] */ double Rd; /* total diffuse reflectance. [-] */

double ** A_rz; /* 2D probability density in turbid */ /* media over r & z. [1/cm3] */ double * A_z; /* 1D probability density over z. */ /* [1/cm] */

Chapter 5 Programming mcml 37

double * A_l; /* each layer's absorption */ /* probability. [-] */ double A; /* total absorption probability. [-] */

double ** Tt_ra; /* 2D distribution of total */ /* transmittance. [1/(cm2 sr)] */ double * Tt_r; /* 1D radial distribution of */ /* transmittance. [1/cm2] */ double * Tt_a; /* 1D angular distribution of */ /* transmittance. [1/sr] */ double Tt; /* total transmittance. [-] */ } OutStruct;

The member Rsp is the specular reflectance. The pointer Rd_ra will be allocated

dynamically, and used equivalently as if it is a 2D array over r and α. Rd_ra is the internal representation of diffuse reflectance Rd(r, α) discussed in Section 4.1. The members Rd_r

and Rd_a are the 1D diffuse reflectance distributions over r and α respectively. The member Rd is the total diffuse reflectance Rd. The member A_rz is the representation of the

2D internal photon distribution A(r, z) (see Section 4.2). The members A_z and A_l are

the corresponding 1D internal photon distributions with respect to z and layers

respectively. The member A is the probability of photon absorption by the whole tissue.

The members for transmittance are analogous to these for reflectance except that there is

no distinction between unscattered transmittance and diffuse transmittance. All the

transmitted photon weight is scored into the arrays.

In the above defined structures, pointers are used to denote the 2D or 1D arrays.

These pointers are dynamically allocated at run time according to user's specifications.

Therefore, the user can use different numbers of layers or numbers of grid elements

without changing the source code of the program. This provides the flexibility of the

program and the efficiency of memory utilization. The dynamic allocation procedures are

modified from Press (1988). Only 1D array allocation will be presented here, and the

matrix allocation can be found in Appendix B.5 -- "mcmlnr.c".

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; }

This function returns a pointer, which points to an array of elements. Each element is a

double precision floating point number. The index range of the array is from nl to nh

38 Chapter 5 Programming mcml

inclusive. In our simulation, nl is always 0, which is the default value in C. This function

also initializes all the elements equal to zero.

5.4 Flowchart of photon tracing

Fig. 5.1 indicates the basic flowchart for the photon tracing part of the Monte

Carlo calculation as described in Chapter 3. Many boxes in the flowchart are direct

implementations of the discussions in Chapter 3. This chart also includes the situation

where the photon packet is in a glass, in which absorption and scattering do not exist. In

this case, the photon packet will be moved to the boundary of the glass layer in the current

photon direction. Then, we have to determine statistically whether the photon packet will

cross the boundary or be reflected according to the Fresnel's formulas.

The box "Launch photon" initializes the photon packet position, direction, weight,

and several other structure members including ds and dsleft in PhotonStruct. The flow

control box "Photon in glass?" determines whether the current layer is a glass layer or

tissue layer.

Chapter 5 Programming mcml 39

Fig. 5.1. Flowchart for Monte Carlo simulation of multi-layered tissue.

When the photon packet is in glass layers, which have no absorption or scattering,

the box "Set step size s" chooses the distance between the current photon position and the

boundary in the direction of the photon movement as the step size. The box "Move to

Y

Photon in glass?

Hit boundary?

Move s

Absorb

Scatter

Y

N

N

Launch photon

N

Photon weight small?

Survive roulette?

Last photon?

Y

N

Y

End

N

Y

Set step size sSet step size s

Move to boundary

Transmit or reflect

Store unfinished sMove to boundary

Transmit or reflect

40 Chapter 5 Programming mcml

boundary" updates the position of the photon packet. Now, as the photon packet is on the

boundary, two approaches of processing photon transmission and reflection are supported

in the box "Transmit or reflect" as discussed in Section 3.6. If the constant

PARTIALREFLECTION is zero, the box determines whether the photon packet should cross

the boundary or be reflected according to the Fresnel reflectance in an all-or-none fashion.

If the photon packet is reflected, the propagation direction should be updated. Otherwise,

if the photon packet crosses the interface into another layer of tissue, the propagation

direction and the index to the layer are updated. If the photon packet crosses the

boundary and moves out of the medium, the photon packet is terminated and the photon

weight is scored into the array for reflectance or transmittance depending on where the

photon packet exits. If the constant PARTIALREFLECTION is one, the box "Transmit or

reflect" deals with ambient medium/tissue interface differently. Part of the photon weight

is transmitted to the ambient medium as reflectance or transmittance, and the rest of the

weight will be reflected and continue propagation. Normally we set PARTIALREFLECTION

to 0 because the all-or-none simulation is faster (see Section 3.6).

In tissue layers, the box "Set step size s" sets the step size to the unfinished step

size according to the structure member sleft if sleft is not zero. Otherwise, it sets the

step size according to the interaction coefficient of the medium. With the chosen step size

s, the box "Hit boundary?" identifies whether the step size is long enough to hit the

boundary of the current layer.

If the step does not hit the boundary, then the box "Move s" will update the

position of the photon packet. Then, the box "Absorb" will deposit a portion of the

photon packet weight in the local grid element, and the box "Scatter" will determine the

new traveling direction for the rest of the photon packet after absorption.

If the step hits the boundary, then the step size s is foreshortened. The

foreshortened step size is the distance between the photon position and the boundary in

the direction of the photon movement, and the unfinished step size is stored by the box

"Store unfinished s". The stored unfinished step size in dimensionless units will be used by

the box "Set step size s" for tissue layers to generate the next step size. The next two

boxes "Move to boundary" and "Transmit or reflect" function the same as for the glass

layer.

At this point, the photon weight and the structure member dead are checked in the

box "Photon weight small?". If the photon packet is dead, it will jump to the box "Last

Chapter 5 Programming mcml 41

photon?" (transition not shown in the flowchart). If the photon packet is still alive and its

weight exceeds Wth, it will start the next step of propagation. If the photon packet is still

alive and its weight is lower than Wth, it has to experience a roulette in the box "Survive

roulette?". If the photon packet survives the roulette, it will start the next step of

propagation. Otherwise, the photon packet is terminated and the box "Last photon?"

determines whether to end the simulation or to start tracing a new photon packet.

5.5 Flow of the program mcml

A flow graph of the mcml source code has been generated using the UNIX

command cflow under SunOS. Each line of output begins with a reference number, i.e., a

line number, followed by a suitable number of tabs indicating the level, then the name of

the global (normally only a function not defined as an external or beginning with an

underscore), a colon, and its definition. The definition consists of an abstract type

declaration (for example, char *), and delimited by angle brackets, the name of the source

file and the line number where the definition was found.

Once a definition of a name has been printed, subsequent references to that

name contain only the reference number of the line where the definition may be found.

For undefined references, only <> is printed.

The list in Fig. 5.2 is generated by command: cflow -d2 mcml*.c. The option "-

d2" limits the nesting depth to 2. The command output with all the nesting levels is shown

in Appendix A.

42 Chapter 5 Programming mcml

1 main: char(), <mcmlmain.c 198> 2 ShowVersion: void*(), <mcmlio.c 49> 3 CenterStr: char*(), <mcmlio.c 28> 4 puts: <> 5 GetFnameFromArgv: void*(), <mcmlmain.c 150> 6 strcpy: <> 7 GetFile: struct*(), <mcmlio.c 94> 8 printf: <> 9 scanf: <> 10 strlen: <> 11 exit: <> 12 fopen: <> 13 CheckParm: void*(), <mcmlio.c 514> 14 ReadNumRuns: short(), <mcmlio.c 205> 15 printf: 8 16 ReadParm: void*(), <mcmlio.c 425> 17 FnameTaken: char(), <mcmlio.c 487> 18 sprintf: <> 19 free: <> 20 nrerror: void*(), <mcmlnr.c 19> 21 FreeFnameList: void*(), <mcmlio.c 500> 22 rewind: <> 23 ReadNumRuns: 14 24 ReadParm: 16 25 DoOneRun: void*(), <mcmlmain.c 163> 26 InitOutputData: void*(), <mcmlio.c 546> 27 Rspecular: double(), <mcmlgo.c 117> 28 PunchTime: long(), <mcmlmain.c 60> 29 ReportStatus: void*(), <mcmlmain.c 122> 30 LaunchPhoton: void*(), <mcmlgo.c 143> 31 HopDropSpin: void*(), <mcmlgo.c 726> 32 ReportResult: void*(), <mcmlmain.c 133> 33 FreeData: void*(), <mcmlio.c 581> 34 fclose: <>

Fig. 5.2. Flow of mcml.

The names of most functions well describe what they do. The function

ShowVersion() prints the name of the program, the names and address of the authors of

the program, and the version of the program. The function GetFnameFromArgv() get an

input data filename from the command line input for UNIX users or DOS users.

Macintosh OS does not allow command line input. The function GetFile() is used to get

the pointer to the file stream for the input data file. The function CheckParm() checks the

input parameters. If the CheckParm() detects an error, it will exit the program.

Otherwise, if all the input parameters pass the error check by the function CheckParm(),

independent simulation runs will be implemented one by one. For each independent run,

the function ReadParm() obtains the input parameters, and the function DoOneRun() does

the simulation and reports the results to the output data file.

After all the input parameters are checked by CheckParm(), the file pointer is

rewound to the beginning of the file stream so that the input parameters can be read again

by the functions ReadNumRuns() and ReadParm(). The function ReadNumRuns() gets how

Chapter 5 Programming mcml 43

many independent simulations are to be specified in this input data file. The function

ReadParm() only reads the input parameters for one independent run.

Under the function DoOneRun(), the function PunchTime() provides the user time

and the real time used so far by the current simulation run. The function ReportStatus()

fetches the real time and predicts when the current simulation run will finish. However,

these timing functions will not work if the source code is compiled by a GNU C compiler.

The function LaunchPhoton() initializes a photon packet. The function HopDropSpin()

moves the photon packet, deposits some photon packet weight, scatters the packet, and

deals with the boundary. After the given number of photon packets are traced, the results

are properly processed (see Chapter 4) and then written to an output file by the function

ReportResult().

5.6 Multiple simulations

The program can do any number of independent simulations sequentially without

being subject to memory limit. It checks the parameters of one simulation after another

before starting the simulation. If it detects an error in the input data file, the program

stops the execution. Otherwise, it reads in the parameters of one independent simulation

at a time and starts the simulation. At the end of the simulation, it writes the results to the

output data file whose name is specified by the input data file. The next independent run

will be processed thereafter. The users should try to make sure not to use the same output

filenames for different simulations, although the program checks against duplicated output

filenames.

To check against duplicated filenames specified in an input data file, we set up a

linear linked list to store the filenames specified in the input data file. Each new filename

is compared with every node of the filename list. If the name is already taken, the

program notifies the user of the name, and exits to the system. Otherwise, if no filename

is duplicated, the program deletes the filename list to release the memory, and continues

execution.

5.7 Timing profile of the program

The timing profile of the program mcml indicates how to improve the efficiency of

the program by logging how much time the program spends on each function. Profilers

are available in a few compilers including C compilers of UNIX operating systems, and

44 Chapter 5 Programming mcml

THINK C compiler on Macintosh. We used the THINK C 5.0.1 profiler here (Symantec,

1991). To log the timings, we turn on the flag THINKCPROFILER to 1 in mcmlmain.c, and

check the "Generate profiler calls" check box in the Debugging page of the "Options..."

dialog box in the THINK C compiler.

The following input file is used (see Section 9.1 for how to name the input file) to

test the profile, where only 100 photon packets are traced. The optical parameters of the tissue are: refractive index n = 1.37, absorption coefficient µ a = 1 cm−1, scattering

coefficient µs = 100 cm−1, anisotropy factor g = 0.9, and thickness d = 0.1 cm.

1.0 # file version 1 # number of runs

prof.mco A # output filename, ASCII/Binary 100 # No. of photons 1E-2 1E-2 # dz, dr 100 100 1 # No. of dz, dr & da.

1 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.37 1 100 0.90 0.1 # layer 1 1.0 # n for medium below.

The profiler output is listed in Table 5.2. The heading "Function" gives the name

of a function. The headings "Minimum", "Maximum" and "Average" give the minimum,

maximum and average time spent in a routine respectively. The unit of time here is a unit

of the VIA 1 timer. Each unit is 1.2766 µsec (approximately 780,000 in a second). The

heading "%" is the percentage of profiling period spent in the routine, where the profiling

period is the accumulated time spent in routines that were compiled with the "Generate

profiler calls" options on. The heading "Entries" is the number of times the routine was

called.

Chapter 5 Programming mcml 45

Table 5.2. Timing profile of the program mcml.

Function Minimum Maximum Average % Entries

AllocMatrix 2507 134153 46662 0 3

AllocVector 65 319 169 0 6

CrossDnOrNot 53 670 145 0 95

CrossOrNot 40 631 66 0 175

CrossUpOrNot 53 635 144 0 80

Drop 166 887 245 5 3554

HitBoundary 38 622 62 1 3729

Hop 40 595 59 1 3729

HopDropSpin 52 711 74 1 3729

HopDropSpinInTissue 111 815 242 5 3729

InitOutputData 314 314 314 0 1

LaunchPhoton 35 123 59 0 100

PredictDoneTime 56472 61201 58638 3 9

PunchTime 76 116 104 0 10

RFresnel 300 905 411 0 105

RandomNum 37 735 67 4 10937

RecordR 1099 2293 1690 0 46

RecordT 1363 2396 1761 0 54

ReportStatus 19 790 90 0 100

Rspecular 58 58 58 0 1

Spin 914 2157 1412 33 3554

SpinTheta 72 665 102 2 3554

StepSizeInTissue 36 2094 1378 33 3729

ran3 27 1187 44 3 10938

From the percentage column, we observe that the functions Spin() and

StepSizeInTissue() take most of the computation time. They are the primary places to

modify if we need to improve the efficiency. The function StepSizeinTissue() takes a

long time because of the logarithmic operation. The function PredictDoneTime()

apparently takes much computation time too. This is because we simulated only 100

photon packets. This function will be called for a fixed number of times (10 times) for a

46 Chapter 5 Programming mcml

simulation no matter how many photon packets are simulated. Therefore, its percentage

will decrease linearly with the number of photon packets to be simulated. Note that we

made the profiler include only the main part of the simulation (See file mcmlmain.c in

Appendix B). Therefore, the functions for input or output of data are not included since

they do not scale up as the number of photon packets to be simulated increases.

Chapter 6 Computation Results of mcml and Verification 47

6. Computation Results of mcml and Verification

Some computation results are described in this chapter as examples, and some of

them are compared with the results from other theory or with the Monte Carlo simulation

results from other investigators to verify the program.

6.1 Total diffuse reflectance and total transmittance

We computed the total diffuse reflectance and total transmittance of a slab of

turbid medium with the following optical properties: relative refractive index n = 1, absorption coefficient µa = 10 cm−1, scattering coefficient µ s = 90 cm−1, anisotropy factor

g = 0.75, and thickness d = 0.02 cm. Ten Monte Carlo simulations of 50,000 photon

packets each are completed. Then, the averages and the standard errors of the total

diffuse reflectance and total transmittance are computed (Table 6.1). The table also lists

the results from van de Hulst's table (van de Hulst, 1980) and from Monte Carlo

simulations by Prahl et al. (1989). All results agree with each other.

Table 6.1. Verification of the total diffuse reflectance and the total

transmittance in a slab with a matched boundary.

The columns "Rd Average" and "Rd Error" are the average and the standard error of the

total diffuse reflectance respectively, while the columns "Tt Average" and "Tt Error" are

the average and the standard error of the total transmittance.

For a semi-infinite turbid medium that has mismatched refractive index with the

ambient medium, the average and the standard error of the total diffuse reflectance are

computed similarly, and compared in Table 6.2 with Giovanelli's (1955) results and Monte

Carlo simulation results by Prahl et al. (1989). The medium has the following optical properties: relative refractive index n = 1.5, µ a = 10 cm−1, µ s = 90 cm−1, g = 0 (isotropic

scattering). Ten Monte Carlo simulations of 5,000 photon packets each are completed to

compute the average and the standard error of the total diffuse reflectance.

Source Rd Average Rd Error Tt Average Tt Error

van de Hulst, 1980 0.09739 0.66096

mcml 0.09734 0.00035 0.66096 0.00020

Prahl et al., 1989 0.09711 0.00033 0.66159 0.00049

48 Chapter 6 Computation Results of mcml and Verification

Table 6.2. Verification of the total diffuse reflectance in a semi-infinite

medium with a mismatched boundary.

Source Rd Average Rd Error

Giovanelli, 1955 0.2600

mcml 0.25907 0.00170

Prahl et al., 1989 0.26079 0.00079

6.2 Angularly resolved diffuse reflectance and transmittance

We used mcml to compute the angularly resolved diffuse reflectance and

transmittance of a slab of turbid medium with the following optical properties: relative refractive index n = 1, absorption coefficient µa = 10 cm−1, scattering coefficient µ s = 90

cm−1, anisotropy factor g = 0.75, and thickness d = 0.02 cm. In the simulation, 500,000

photon packets are used, and the number of angular grid elements is 30. The results are

compared with the data from van de Hulst's table (van de Hulst, 1980) as shown in Figs.

6.2a and b.

Since mcml also scores the unscattered transmittance into the transmittance array,

we have to subtract it from the first element of the array to obtain the diffuse transmittance. In this case, the unscattered transmittance is exp(–(µ a+µ s) d) = exp(–2) ≈

0.13534, which is only scored into the first grid element in the α direction. The solid angle spanned by the first grid element is ∆Ω ≈ 2 π sin(∆α/2) ∆α, where ∆α = π/(2×30). Therefore, the contribution of the unscattered transmittance to the first element of the array Tt(α) is exp(–2)/∆Ω ≈ 15.68. After the subtraction, we get the adjusted first

element of transmittance array being 0.765 sr−1, which is the diffuse transmittance. Note

that the variance in Fig. 6.2a for diffuse reflectance is larger than that in Fig. 6.2b for

diffuse transmittance. This is because the total diffuse reflectance is much less than the

total diffuse transmittance (0.09739 vs 0.66096–0.13534 = 0.52562, as shown in Table

6.1).

Since van de Hulst used a different definition of reflectance and transmittance, and

a normalization to incident flux π, we multiplied van de Hulst's data by the cosine of the exiting angle from the normal to the surface, then divided by π.

Chapter 6 Computation Results of mcml and Verification 49

0

0.005

0.01

0.015

0.02

0.025

R d ( α ) [s

r -1 ]

0š 0.1š 0.2š 0.3š 0.4š 0.5š

R d (α) van de Hulst

R d (α) mcml

(a)

0

0.2

0.4

0.6

0.8

T d ( α ) [s

r -1 ]

0.1š 0.2š 0.3š 0.4š 0.5š

(b)

T d (α) van de Hulst

T d (α) mcml

α [rad]

Fig. 6.2. Angularly resolved (a) diffuse reflectance Rd(α) and (b) diffuse transmittance Td(α) vs the angle between the photon exiting direction and

the normal to the medium surface α. Solid circles are from van de Hulst's table, and the open square boxes are from mcml simulation. The optical parameters are: relative refractive index n = 1.0, µ a = 10 cm−1, µ s = 90

cm−1, g = 0, thickness d = 0.02 cm.

6.3 Radially resolved diffuse reflectance

As an example of the simulation of radially resolved diffuse reflectance, we

compare the diffuse reflectances of two semi-infinite media whose optical properties are

50 Chapter 6 Computation Results of mcml and Verification

governed by similarity relations (Wyman et al., 1989a and 1989b). The two sets of optical parameters share the same absorption coefficient µ a and transport scattering coefficient

µ s(1–g). These two media should give approximately the same diffuse reflectances if the

similarity relations are valid. The results (Fig. 6.3) confirmed that the similarity relations

do not apply for photon sources near the media boundary as Wyman et al. expected. The

relative difference between two diffuse reflectances is very large when the radius r is small,

and becomes smaller when r becomes larger. We have also confirmed that the similarity

relations work very well when the photon source is deep inside the media using a modified

mcml (see Chapter 11).

In Fig. 6.3., The

optical properties for curve A are: µ a = 0.1 cm−1, µ s =

100 cm−1, g = 0.9, nrel = 1,

and the optical properties for curve B are: µ a = 0.1

cm−1, µ s = 10 cm−1, g = 0,

nrel = 1. The two curves A

and B are results from pure

Monte Carlo simulations of

1 million photon packets.

The grid line separation in

the r direction is 0.005 cm,

and number of grid

elements is 200.

6.4 Depth resolved

internal fluence

As an example of

simulation of the depth

resolved internal fluence,

we show the results for two

semi-infinite media with

matched and mismatched

boundaries respectively

0.01

1

100

0 0.2

R d ( cm -2 )

0.4 0.6 0.8 1

A: g=0.9

B: g=0

(a)

-0.5

0

0.5

1

1.5

2

R e la

tiv e E

rr o r

0 0.2 0.4 0.6 0.8 1 r (cm)

(b)

(B-A) / A

Fig. 6.3. (a) Comparison and (b) the relative

difference of diffuse reflectances as a function

of radius r for two semi-infinite media whose

optical properties are "equivalent" according

to similarity relations.

Chapter 6 Computation Results of mcml and Verification 51

(Fig. 6.4). The dimensionless internal fluence as a function of depth z, φz[iz] in Section

4.2, is computed from the response to an infinitely narrow photon beam normally incident

on a semi-infinite medium. However, it can be equivalently considered as the response of

an infinitely wide photon beam perpendicularly incident on a semi-infinite medium with a

difference of a constant S (see Section 4.2), where S is the power density. Since the direction output of the program mcml gives Az[iz] instead of φz[iz], we divided Az[iz] by the absorption coefficient of the semi-infinite medium to get φz[iz]. Although the response

of an infinitely narrow photon beam is dimensionless (Fig. 6.4), if the input photon beam is

measured in W/cm2 or J/cm2 as the power density of energy density, the unit of fluence is

also in the unit of W/cm2 or J/cm2 correspondingly. Since we only consider steady-state

responses, we can discuss either energy density or power density because they can be

converted back and forth.

Note that the fluence near the surface is larger than 1 because the back scattered

light augments the fluence. Furthermore, the internal fluence for the medium with a

mismatched boundary is higher than that for the medium with a matched boundary. This

is due to the internal reflection by the mismatched boundary, therefore the photons that

would escape from the boundary of the boundary-matched medium may be reflected back

into the medium by the mismatched boundary and hence have a greater chance to be

absorbed. Also note that when z is sufficiently deep, the two curves are parallel. This

confirms the valid range of diffusion theory. For z larger than the penetration depth δ, diffusion theory predicts that the internal fluence distribution should be (Wilson et al.,

1990):

φ(z) = φ0 k exp(–z/δ) (6.1)

where k is a scalar that depends on the amount of back scattered reflectance, and φ0 is the

incident irradiance, which is 1 in our mcml simulation. The scalar k is obviously a function

of the relative index of refraction. Therefore, the matched boundary and the mismatched

boundary will have different k. The penetration depth δ is computed:

δ = 1/ 3 µ a (µ s + µ s (1–g)) = 1/ 3(0.1)(0.1+100(1–0.9)) ≈ 0.57 cm (6.2)

which is independent of relative index of refraction. Therefore, the two curves in Fig. 6.4

should be off just by a factor due to different k values when z > δ, which means the curves are parallel in log-linear scale when z > δ. The two curves shown here are parallel even when z > 1 mfp' = 1/(µs + µ s (1–g)) ≈ 0.1 cm, where mfp' is the transport mean free path.

52 Chapter 6 Computation Results of mcml and Verification

One mfp' may be a better criterion for valid application of diffusion theory than the

penetration depth. Further supporting evidence can be found in Chapter 11 and Wang et

al. (1992).

1

10

0 0.2

F lu

e n

ce [

-]

0.4 0.6 0.8 1

nrel = 1 nrel = 1.37

z (cm)

µa = 0.1 cm-1

µs = 100 cm -1

g = 0.9 N = 1,000,000

1 mfp'

Fig. 6.4. Comparison of internal fluences as a function of depth z for two

semi-infinite media with a matched boundary and a mismatched boundary

respectively. The results are from Monte Carlo simulations with 1 million

photon packets each using mcml. The grid line separation in the z direction

is 0.005 cm, and number of grid elements is 200.

We fit the parallel part of the two curves with exponential functions. The damping

constants for the curves are approximately 1.73 cm−1 for the matched boundary and 1.74

cm−1 for the mismatched boundary respectively. The reciprocals of the damping constants

are 0.578 cm for the matched boundary and 0.575 cm for the mismatched boundary

respectively. They are very close to the penetration depth (0.57 cm, Eq. 6.2) predicted

from diffusion theory.

6.5 Computation times vs optical properties

We completed multiple Monte Carlo simulations with mcml for semi-infinite media

with various optical properties, and fitted the user times as a function of the ratio between scattering coefficient and absorption coefficient µs/µ a and anisotropy g. The situations for

media with matched boundaries (relative refractive index is 1) and mismatched boundaries

Chapter 6 Computation Results of mcml and Verification 53

(relative refractive index is not 1) will be presented separately. Note that the user time is

whatever the system allocates to the running of the program, as opposed to the real time

which is wall-clock time. In a time-shared system, they need not be the same, and the real

time of the same run may not be reproduced depending on the status of the system. In

mcml, the user time is reported to the output data file, and the real time is used to predict

when the simulation finishes during the simulation.

Before starting multiple mcml simulations for various optical properties, we know

that the time required to finish tracing a photon packet is proportional to the number of

steps that a photon packet takes until being terminated. According to the rules for photon

propagation described in Chapter 3, this number of steps should not be dependent on the absolute values of scattering coefficient µs and absorption coefficient µa, but their ratio.

Therefore, we keep one of the two parameters constant (e.g., µ s = 100 cm−1), and vary

the other one (e.g., µ a).

Matched boundary

For media with matched boundaries (relative refractive index nrel is 1), we finished

multiple mcml simulations of 10,000 photon packets each for various absorption coefficients µa and anisotropy factors while keeping the scattering coefficient µ s fixed to

100 cm−1. The results are listed in Table 6.3, where the ratios between the scattering

coefficient and the absorption coefficient are tabulated, instead of the two coefficients

themselves separately, and the user

54 Chapter 6 Computation Results of mcml and Verification

Table. 6.3. Computation times for various media with matched

boundaries. The column "Predicted User Time" is the computed values

using Eqs. 6.1-6.3 presented later. The column "Error" is equal to

(Predicted User Time – User Time)/(User Time)*100.

µs/µa g User Time (sec./1000 photons)

Predicted User Time (sec./1000 photons)

Error (%)

0.2 0 0.43 0.36 –16.15 1 0 0.78 0.81 3.58 2 0 1.10 1.15 4.42 10 0 2.59 2.56 –0.99 20 0 3.66 3.62 –0.97 100 0 8.46 8.10 –4.23 200 0 12.27 11.46 –6.64 1000 0 29.61 25.61 –13.49

0.2 0.1 0.40 0.38 –5.04 1 0.1 0.80 0.86 7.19 2 0.1 1.10 1.22 10.70 10 0.1 2.80 2.75 –1.82 20 0.1 3.90 3.90 0.10 100 0.1 9.80 8.81 –10.07 200 0.1 13.20 12.52 –5.19 1000 0.1 28.60 28.25 –1.21

0.2 0.5 0.50 0.45 –9.54 1 0.5 1.00 1.08 7.71 2 0.5 1.40 1.57 11.80 10 0.5 3.50 3.73 6.49 20 0.5 5.30 5.42 2.19 100 0.5 12.10 12.90 6.60 200 0.5 17.90 18.74 4.71 1000 0.5 40.60 44.63 9.93

0.2 0.9 0.50 0.49 –1.83 1 0.9 1.20 1.35 12.74 2 0.9 1.90 2.09 10.20 10 0.9 6.30 5.77 –8.39 20 0.9 9.80 8.93 –8.86 100 0.9 25.80 24.62 –4.58 200 0.9 38.10 38.10 0.00 1000 0.9 95.30 105.02 10.20

0.2 0.99 0.50 0.42 –16.18 1 0.99 1.20 1.42 18.68 2 0.99 2.10 2.41 14.85 10 0.99 8.80 8.20 –6.87 20 0.99 16.50 13.88 –15.89 100 0.99 60.60 47.16 –22.18 200 0.99 97.20 79.86 –17.84 1000 0.99 248.80 271.37 9.07

Chapter 6 Computation Results of mcml and Verification 55

times are converted to seconds per 1000, instead of 10,000, photon packets. The columns

"Predicted User Time" and "Error" will be discussed subsequently

We plotted the user times as a function of the ratio between the scattering

coefficient and the absorption coefficient for each anisotropy factor g in a log-log scale

(Fig. 6.5). For each anisotropy factor g, the higher the ratio between the scattering

coefficient and the absorption coefficient, the longer the user time. This is because the photon packets in media of higher ratio µ s/µ a, compared with media of lower ratio, can

jump more steps before reaching the threshold weight and hence having a chance to be

terminated. Therefore, the Monte Carlo simulation of low absorbing medium is very slow.

If the diffuse reflectance as a function of r in a low absorption semi-infinite turbid medium

is the only physical quantity to be computed, a hybrid model of pure Monte Carlo

simulation and diffusion theory (Wang et al., 1992) is a much faster model than pure

Monte Carlo simulations. The speed of the hybrid model is not so sensitive to the ratio

between the scattering coefficient and the absorption coefficient.

For the same ratio between the scattering coefficient and the absorption

coefficient, the larger the anisotropy factor g, the longer the user time. This is because

that the photon packets in media of larger anisotropy factors g have less chance to be

reflected out of the media because the scatterings are more forward directed, and hence to

be terminated.

For each anisotropy factor g, the data points are well aligned in the log-log plot.

This means that we can fit the data points for each anisotropy factor g with a power

function. The fitted lines are presented in Fig. 6.5.

The two fitting coefficients C1(g) and C2(g) are dependent on the anisotropy

factor g. The fitting coefficients C1(g) are plotted against the anisotropy factor g in a log-

linear scale (Fig. 6.6a), and can be fitted with an exponential function. Similarly, the fitting coefficients C2(g) are plotted against (1 – g) in a linear-log scale (Fig. 6.6b), and

can be fitted with a logarithmic function. These three fittings are summarized as the

following empirical formulas:

56 Chapter 6 Computation Results of mcml and Verification

0.1

1

10

100

U se

r T

im e [ se

c. /1

0 0 0 p

h o to

n s]

1000

0.1 1 10 100 µs/µa

1000

g=0 g=0.5 g=0.9 g=0.99

t = C1(g) (µs/µa) C2(g)

nrel = 1

Fig. 6.5. The user times vs the ratio between the scattering coefficient µ s and the absorption coefficient µ a for different anisotropy factors g of media

with matched boundaries.

C1(g) = 0.81 exp(0.57 g) (6.3)

C2(g) = 0.50 – 0.13 log(1–g) (6.4)

t = C1(g) (µ s/µ a) C2(g) [sec./1000 photons] (6.5)

where t is the user time in seconds per 1000 photon packets for semi-infinite media with

matched boundaries.

Chapter 6 Computation Results of mcml and Verification 57

1

0

C 1

0.2 0.4 0.6 0.8 1

C1(g) = 0.81 exp(0.57 g)

g

(a)

0.4

0.5

0.6C 2

0.7

0.8

0.01 0.1

C2(g) = 0.50 - 0.13 log(1-g)

1-g 1

(b)

Fig. 6.6. The fitting coefficients (a) C1(g) and (b) C2(g) vs the anisotropy

factor g of media with matched boundaries.

To test how good Eqs. 6.3-6.5 are, we use them to compute the user times for the

optical properties given in Table 6.3, and presented the computed user times in Table 6.3

as the column "Predicted User Time". The relative errors are within 20% as shown in the

column "Error" in Table 6.3 for all rows in the table except one of them (in bold face).

The Eqs. 6.3-6.5 can be used generally to predict user time of a medium with a

matched boundary. However, several limitations and notes have to be mentioned. First,

58 Chapter 6 Computation Results of mcml and Verification

the anisotropy factor g is limited to less than 0.99, and the accuracy is unknown for g > 0.99 because the fitting coefficient C2(g) approaches infinity when g approaches 1.

Second, these equations are based on mcml simulations on a Sun SPARCstation 2.

Therefore, we expect a scale factor for Eq. 6.5 or the values in Table 6.3 on a different

computer system. This scale factor can be determined by simulating one or several media

with optical properties in Table 6.3 using mcml running on your computer system and

taking the ratio between the user time on your machine and the user time in Table 6.3.

Third, the speed of mcml is related to the threshold weight in the program mcml (WEIGHT

in Table 5.1), which is normally 1×10 –4 (See Table 5.1), and the chance of surviving a roulette (CHANCE in Table 5.1), which is normally 0.1 (See Table 5.1). If these two

parameters are changed, Eqs. 6.3-6.5 are no longer valid. It is unexplored yet how these

two parameters will affect the user times.

Mismatched boundary

We repeat the above process to media with mismatched boundaries (relative refractive index nrel � 1). We finished multiple mcml simulations of 1,000 (instead of

10,000 for matched boundaries) photon packets each for various absorption coefficients

and anisotropy factors while keeping the scattering coefficient fixed to 100 cm−1 and the

relative refractive index to 1.37, which is typical for human tissues in visible or infrared

wavelength. The results are listed in Table 6.4.

We plotted the user times as a function of the ratio between the scattering

coefficient and the absorption coefficient for each anisotropy factor g in a log-log scale (Fig. 6.7). Then, the fitting coefficients C1(g) and C2(g) are plotted with respect to g and

(1–g) respectively (Fig. 6.8a and b), and fitted with an exponential function and a

logarithmic function correspondingly. The fittings give the following empirical formulas:

Chapter 6 Computation Results of mcml and Verification 59

Table 6.4. Computation times for various media with mismatched

boundaries. The column "Predicted User Time" is the computed values

using Eqs. 6.4-6.6 presented later. The column "Error" is equal to

(Predicted User Time – User Time)/(User Time)*100.

µs/µa g User Time (sec./1000 photons)

Predicted User Time (sec./1000 photons)

Error (%)

0.2 0 0.48 0.43 –9.74 1 0 0.98 1.05 7.14 2 0 1.42 1.54 8.26 10 0 3.67 3.73 1.51 20 0 5.47 5.45 –0.28 100 0 13.43 13.22 –1.57 200 0 18.67 19.35 3.66 1000 0 50.50 46.90 –7.13

0.2 0.1 0.50 0.44 –11.03 1 0.1 1.00 1.09 8.85 2 0.1 1.50 1.60 6.68 10 0.1 3.83 3.92 2.23 20 0.1 5.90 5.76 –2.44 100 0.1 15.95 14.08 –11.70 200 0.1 22.55 20.71 –8.18 1000 0.1 42.38 50.66 19.54

0.2 0.5 0.53 0.49 –8.10 1 0.5 1.13 1.26 11.25 2 0.5 1.73 1.89 9.31 10 0.5 4.87 4.88 0.22 20 0.5 7.63 7.34 –3.77 100 0.5 19.53 18.95 –2.97 200 0.5 27.08 28.51 5.28 1000 0.5 77.96 73.58 –5.62

0.2 0.9 0.55 0.49 –11.64 1 0.9 1.27 1.45 14.31 2 0.9 2.03 2.33 14.58 10 0.9 7.28 6.95 –4.55 20 0.9 12.10 11.13 –7.99 100 0.9 35.98 33.26 –7.56 200 0.9 55.05 53.28 –3.21 1000 0.9 133.06 159.18 19.63

0.2 0.99 0.55 0.41 –25.96 1 0.99 1.28 1.50 17.16 2 0.99 2.10 2.63 25.19 10 0.99 8.82 9.68 9.77 20 0.99 16.82 16.97 0.92 100 0.99 69.55 62.51 –10.12 200 0.99 120.56 109.60 –9.09 1000 0.99 366.45 403.62 10.14

60 Chapter 6 Computation Results of mcml and Verification

C1(g) = 1.05 exp(0.36 g) (6.6)

C2(g) = 0.55 – 0.13 log(1–g) (6.7)

t = C1(g) (µ s/µ a) C2(g) [sec./1000 photons] (6.8)

where t is the user time in seconds per 1000 photon packets for semi-infinite media with mismatched boundaries (nrel = 1.37).

0.1

1

10

100

1000

0.1 1

U se

r T

im e [ se

c. /1

0 0 0 p

h o to

n s]

10 100 1000

g=0 g=0.5 g=0.9 g=0.99

µs/µa

C2(g)

nrel = 1.37

t = C1(g) (µs/µa)

Fig. 6.7. The user times vs the ratio between the scattering coefficient µ s and the absorption coefficient µ a for different anisotropy factors g in media

with relative refractive index 1.37.

Chapter 6 Computation Results of mcml and Verification 61

1

1.1

C 1

1.2

1.3

1.4

1.5

1.6

0 g

C1 = 1.05 exp(0.36 g)

0.2 0.4 0.6 0.8 1

(a)

0.5

0.6

0.7

0.8

0.9

0.01

C2 = 0.55 - 0.13 log(1-g)

C 2

0.1 1

(b)

1-g

Fig. 6.8. The fitting coefficients (a) C1 and (b) C2 vs the anisotropy factor

g of media with relative refractive index 1.37.

To test how good Eqs. 6.6-6.8 are, we use them to compute the user times for the

optical properties in Table 6.4, and presented the computed user times in Table 6.4 in the

column "Predicted User Time". For all rows in the table except two of them (in bold

face), the relative errors are within 20%. The cautions made for media with matched

boundaries apply here as well.

62 Chapter 6 Computation Results of mcml and Verification

To summarize the empirical formulas for both matched and mismatched

boundaries, we list the formulas in Table 6.5.

Table 6.5. Empirical formulas of user times for matched and mismatched

boundaries.

Items Matched (nrel = 1) Mismatched (nrel = 1.37)

C1(g) 0.81 exp(0.57 g) 1.05 exp(0.36 g)

C2(g) 0.50 – 0.13 log(1–g) 0.55 – 0.13 log(1–g)

t [sec./1000 photons] C1(g) (µ s/µ a) C2(g) C1(g) (µ s/µ a) C2(g)

6.6 Scored Physical Quantities of Multi-layered Tissues

For multi-layered tissues, we have not found computation results based on other

theories to compare with our computation. However, thanks to Gardner's cooperation

(Gardner et al., 1992), we compared our Monte Carlo simulation results of multi-layered

tissues with their results of an independently written Monte Carlo simulation. The comparison includes the diffuse reflectance versus radius, Rd(r), where the radius r is the

distance between the photon incident point and the observation point, the transmittance versus radius, Tt(r), and the internal fluence versus z, φz(z), and the internal fluence versus r and z, φrz(r, z). This comparison can at least greatly reduce the chance of programming

errors.

We chose a three-layer tissue for the simulation. The optical properties of each

layer are shown in Table 6.6.

Table 6.6. The optical properties of the three-layer tissue.

Layer Refractive Index n

Absorption Coeff. (cm–1)

Scattering Coeff. (cm–1)

Anisotropy Factor g

Thickness (cm)

1 1.37 1 100 0.9 0.1

2 1.37 1 10 0 0.1

3 1.37 2 10 0.7 0.2

The refractive indices of the top and bottom ambient media are both set to 1.0.

The grid separations in z and r directions are both 0.01 cm. The number of grid elements

in z and r directions are 40 and 50 respectively. We do not want to resolve the exiting

angles of reflected or transmitted photons, therefore we set the number of grid elements in

Chapter 6 Computation Results of mcml and Verification 63

the angle α direction to 1. The number of photon packets traced is 1,000,000. The actual input file for the program mcml is as follows.

1.0 # file version 1 # number of runs

### Specify data comp.mco A # output filename, ASCII/Binary 1000000 # No. of photons .01 .01 # dz, dr 40 50 1 # No. of dz, dr & da.

3 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.37 1 100 0.90 0.1 # layer 1 1.37 1 10 0 0.1 # layer 2 1.37 2 10 0.70 0.2 # layer 3 1.0 # n for medium below.

Craig Gardner set up the same run for his simulation code except he used only

100,000 photons (Gardner et al., 1992). The total diffuse reflectance and transmittance

from the two simulations are compared in Table 6.7.

Table 6.7. Comparison of total diffuse reflectances and transmittances.

Source Diffuse Reflectance Rd Transmittance Tt

Gardner et al., 1992 0.2381 0.0974

mcml 0.2375 0.0965

The comparison between the diffuse reflectances and transmittances are shown in Figs. 6.9 and 6.10 respectively. The comparison between fluences φrz(r, z), as an impulse

response, as a function of radius r for several given z coordinates is shown in Fig. 6.11. The comparison between fluences φz(z) as a function of z as described in Section 4.2 is

subsequently given in Fig. 6.12. All of the comparisons have shown agreement between

the two independent simulations.

64 Chapter 6 Computation Results of mcml and Verification

10-2

10-1

10 0

10 1

10 2

R d [ cm -2 ]

0 0.1 0.2 0.3 0.4 0.5 r [cm]

Gardner et al.

mcml

Impulse responses

Fig. 6.9. Comparison between diffuse reflectances as a function of radius

based on Gardner's computation (Gardner et al., 1992) and mcml

simulation.

10-2

10-1

10 0

0 0.1

T t [ cm -2 ]

0.2 0.3 0.4 0.5

Gardner et al.

mcml

r [cm]

Impulse responses

Fig. 6.10. Comparison between transmittances as a function of radius

based on Gardner's computation (Gardner et al., 1992) and mcml

simulation.

Chapter 6 Computation Results of mcml and Verification 65

10-1

10 0

10 1

10 2

10 3

10 4

F lu

e n ce

[c m

-2 ]

0 0.1 0.2 0.3 0.4 0.5 r [cm]

mcml z=0.005 cm mcml z=0.205 mcml z=0.395 Gardner et al. z=0.005 Gardner et al. z=0.205 Gardner et al. z=0.395

Impulse Responses

Fig. 6.11. Comparison between fluences as a function of radius r for

several z coordinates based on Gardner's computation (Gardner et al.,

1992) and mcml simulation.

0

0.5

1

1.5

2

F lu

e n ce

[ -]

2.5

3

0 0.1 0.2 0.3 z [cm]

0.4

Gardner et al.

Responses to infinitely wide beam

mcml

Fig. 6.12. Comparison between fluences as a function of z based on

Gardner's computation (Gardner et al., 1992) and mcml simulation.

66 Chapter 6 Computation Results of mcml and Verification

For 2D arrays such as the fluence as a function of r and z, conv has the ability to

output the data in contour format (see Sections 10.9 and 10.10). The fluence as a

function of r and z of the impulse response is shown in contour lines in Fig. 6.13.

0.25

0.2

0.15

0.1

0.05

0

z [c

m ]

0 0.05 0.1 0.15 0.2 0.25 r [cm]

1000 100 50

10

Fig. 6.13. Contour plot of the fluence as a function of r and z of impulse

response based on mcml simulation.

Chapter 7 Convolution for Photon Beams of Finite Size 67

7. Convolution for Photon Beams of Finite Size

This chapter will discuss the principles and implementation of convolving Monte

Carlo simulation results for an infinitely narrow photon beam to yield the responses to

photon beams of finite size. Gaussian beams and circularly flat beams are considered as

special cases.

7.1 Principles of convolution

So far we have only dealt with the response to an infinitely narrow photon beam

normally incident on the surface of a multi-layered tissue. This response is also called the

impulse response. However, all photon beams have finite size in reality. Theoretically, we

can use the Monte Carlo simulation to compute the response to a finite size photon beam

directly by distributing the initial positions of the launched photon packets. The only

problem is that it requires a larger number of photon packets to be traced to get

acceptable variance than simulating the responses of an infinitely narrow photon beam.

Therefore, this method is not efficient although sometimes it might be the only approach

for some types of tissue configurations which can not be convolved, such as a tissue with

an irregular buried object.

Fortunately, the system we are dealing with is linear and invariant. The linearity

means that if the input intensity of the infinitely narrow photon beam is multiplied by a

factor, the responses will be multiplied by the same factor. It also means that the response

to two photon beams is the sum of the responses to each photon beam. The invariance

means that when the infinitely narrow photon beam is shifted horizontally by a distance in

a certain direction, the responses will be shifted also horizontally by the same distance in

the same direction. Therefore, if we assume the photon beam of finite size is collimated,

the response of an infinitely narrow photon beam will be a Green's function of the tissue

system, and the response of the finite size photon beam can be computed from the

convolution of the Green's function according to the profile of the finite size photon beam.

Note that the responses mentioned above can be the internal absorption

distribution, or the reflectance or transmittance distributions. We denote the responses

generally as C(x, y, z) although it may not be a three-variable function of all three

coordinates. Such degeneracy due to the symmetry will be discussed subsequently. We

denote the Green's function corresponding to the type of response under consideration as

68 Chapter 7 Convolution for Photon Beams of Finite Size

G(x, y, z). Since the photon beam is normally incident on the tissue surface, the function

G(x, y, z) possesses cylindrical symmetry. If the collimated photon beam as the source has

the intensity profile S(x, y), the responses can be obtained through convolution (Prahl,

1988; Prahl et al., 1989):

C(x, y, z) = ⌡⌠ –∞

∞ ⌡⌠

–∞

∞ G(x–x', y–y', z) S(x', y') dx' dy' (7.1a)

or through variable transformation with x'' = x – x' and y'' = y – y':

C(x, y, z) = ⌡⌠ –∞

∞ ⌡⌠

–∞

∞ G(x'', y'', z) S(x–x'', y–y'') dx'' dy'' (7.1b)

In Eq. 7.1a the Green's function is a function of the distance between the source

point (x', y') and the observation point (x, y), where the distance is:

d' = (x–x')2 + (y–y')2 (7.2)

If the intensity profile S(x', y') of the source also has cylindrical symmetry, S(x', y') is only

a function of the radius of the source point (x', y') with respect to the origin point of the

coordinate system, where the radius is:

r' = x'2 + y'2 (7.3)

Therefore, Eq. 7.1a can be reformulated considering these symmetries:

C(x, y, z) = ⌡⌠ –∞

∞ ⌡⌠

–∞

∞ G( (x–x')2 + (y–y')2 , z) S( x'2 + y'2) dx' dy' (7.4a)

Similarly, Eq. 7.1b can also be reformulated with these symmetries:

C(x, y, z) = ⌡⌠ –∞

∞ ⌡⌠ –∞

∞ G( x''2 + y''2 , z) S( (x–x'')2 + (y–y'')2) dx'' dy'' (7.4b)

Since the response C(x, y, z) will have the same cylindrical symmetry, the problem

can be more easily handled in a cylindrical coordinate system, where Eqs. 7.4a and 7.4b

can be written:

Chapter 7 Convolution for Photon Beams of Finite Size 69

C(r, z) = ⌡  ⌠

0

S(r') r'   

  

⌡⌠

0

2 π

G( )r2 + r'2 – 2rr'cosθ' , z dθ' dr' (7.5a)

C(r, z) = ⌡  ⌠

0

G(r'', z) r''   

  

⌡⌠

0

2 π

S( )r2 + r''2 – 2rr''cosθ'' dθ'' dr'' (7.5b)

Eq. 7.5b is more advantageous than Eq. 7.5a in computation because the integration over

θ'' is independent of z. This integration hence need only be computed once for all depths z. In some cases as presented subsequently, the integral over θ'' can be expressed analytically, therefore the two-dimensional integral is converted into a one dimensional

integral.

The transformation of variables is illustrated in Fig. 7.1. The first coordinate

system (r', θ', z) has its origin at the center of the source, as in Fig. 7.1A and Eq. 7.5a. The point of observation is at (r, 0). An incremental region of source is located at (r', θ') and has a value S(r'). The distance between the source point and the observation point is

d' equal

r2 + r'2 – 2rr'cosθ' .

The second coordinate system (r", θ", z) has its origin at the center of the point of observation, as in Fig. 7.1B and Eq. 7.5b. The source is centered at (r, 0). The

incremental region of source is located at (r", θ") and has a value S(d"), where d" equals

r2 + r"2 – 2rr"cosθ" . The distance between the source point and the observation point

is r".

In the following sections, we will consider a Gaussian beam and a circularly flat

beam as examples of cylindrical symmetry to further simplify Eq. 7.5b.

70 Chapter 7 Convolution for Photon Beams of Finite Size

Fig. 7.1. Illustration of the transform between two coordinate systems.

The stippled circle is the laser source and P is the point of observation.

The circular lines schematically represent the integration of Eq. 7.5a (A)

and Eq. 7.5b (B).

(A)

r

(r, 0)

θ"(r", θ") r"

d"

(Β)

d'

r'

r

(r, 0)

θ'(r', θ')

d'' =

r2 + r'' 2 - 2rr''cos θ''

d' =

r2 + r' 2 - 2rr'cos θ'

P

P

Chapter 7 Convolution for Photon Beams of Finite Size 71

7.2 Convolution over Gaussian beams

In the case of a Gaussian beam, if the divergence is ignored, the above convolution

can be applied. If the 1/e2 radius of the Gaussian beam is denoted by R, the beam intensity

profile is:

S(r') = S0 exp(–2 (r'/R)2) (7.6)

where the intensity in the center (r=0), S0, is related to the total power P by:

S0 = 2 P / (π R2) (7.7)

Substituting Eq. 7.6 into Eq. 7.5b, the convolution becomes:

C(r, z) = S(r)

⌡   ⌠

0

G(r'', z)exp(–2 (r''/R)2)    

   

⌡⌠ 0

2 π exp(4rr''cosθ''/R2) dθ'' r''dr'' (7.8)

The integration in the square brackets resembles the integral representation of the

modified Bessel function (Spiegel, 1968):

I0(x) = 1

2 π ⌡ ⌠

0

2π exp(x sinθ) dθ (7.9a)

which can be reformatted to be:

I0(x) = 1

2 π ⌡ ⌠

0

2π exp(x cosθ) dθ (7.9b)

Eq. 7.8 can be written by substituting Eq. 7.9b into it:

C(r, z) = S(r) ⌡⌠ 0

∞ G(r'', z) exp(–2 (r''/R)2) I0(4rr''/R2) 2 π r'' dr'' (7.10)

where I0 is the zero order modified Bessel function.

72 Chapter 7 Convolution for Photon Beams of Finite Size

7.3 Convolution over circularly flat beams

If the photon beam is homogeneous within a radius R and collimated, the source

function becomes:

S(r') =   P/(π R2) if r' ≤ R 0 if r' > R

(7.11)

where P is the total power of the beam. Substituting Eq. 7.11 into Eq. 7.5b, the

convolution becomes:

C(r, z) = P/(π R2) ⌡⌠ 0

∞ G(r'', z) Iθ(r, r'') 2 π r'' dr'' (7.12)

where the function Iθ(r, r'') is:

Iθ(r, r'') =

  

1 if R ≥ r + r''

1 π

cos–1((r2 + r''2 – R2)/(2rr'')) if |r – r''| ≤ R < r + r''

0 if R < |r – r''|

(7.13)

From Eq. 7.13 and Fig. 7.1B, the limits of integration in Eq. 7.12 can be changed to a

finite region:

C(r, z) = P/(π R2) ⌡⌠ a

r + R G(r'', z) Iθ(r, r'') 2 π r'' dr'' (7.14a)

where

a = Max(0, r – R) (7.14b)

where the function Max takes the larger of the two arguments.

The circular lines in Fig. 7.1B illustrated the second case in Eq. 7.13 when the

observation point P is outside of the source. When the point P is outside the source, the

first case of Eq. 7.13 is never satisfied. Readers can similarly draw the pictures for the

case where the point P is inside the source. Note that much of the region of integration

lies outside the source and therefore the value of S in the integrand is zero.

Chapter 7 Convolution for Photon Beams of Finite Size 73

As a special case of a circularly flat beam, we let the radius R approach infinity,

which represents an infinitely wide flat beam. In this case the total power P also

approaches infinity, but we can use the power density to describe the intensity of the

beam. The convolution for this case can be accomplished by simply letting R → ∞ and P/(π R2) → S, where S is the power density or irradiance (W/cm2), in Eq. 7.14. Iθ(r, r'')

will be 1 constantly. Eq. 7.14 becomes:

C(r, z) = S ⌡⌠ 0

∞ G(r'', z) 2 π r'' dr'' (7.15)

7.4 Numerical solution to the convolution

In these two special cases of photon beams, the two-dimensional integrations are

converted into one-dimensional integrations, which are significantly faster in numerical

computation. Since the Monte Carlo simulation scores physical quantities to discrete grid

points, the best choice of integration algorithm is the extended trapezoidal rule, which is

written in C called qtrap() by Press et al. (1988). "Increased sophistication will usually

translate into a higher order method whose efficiency will greater only for sufficiently

smooth integrands. qtrap is the method of choice, e.g., for an integrand which is a

function of a variable that is linearly interpolated between measured data points." Press et

al. (1988) state.

The simplest choice of the integration is the summation of the integrand values at

the original grid points multiplied by the grid separation. However, this approach does not

have any control over the integration accuracy. For a given accuracy, sometimes this

approach gives more accuracy than required, which is a waste of computation time, and

sometimes it gives less accuracy, which does not meet the expectation. For example, the

number of original grid elements in the r direction is 50, and we want to convolve the

responses over a circularly flat beam with a radius of R which is about 5 ∆r, where ∆r is the grid separation in the r direction. To compute C(0, z) in Eq. 7.14a, the integral range,

from 0 and R, only covers 5 ∆r. This means only 5 function evaluations will be completed, which may yield unacceptable answer. On the contrary, the extended

trapezoidal rule does the right amount of computation until it reaches the user specified

accuracy.

74 Chapter 7 Convolution for Photon Beams of Finite Size

We have slightly modified the original function qtrap() so that it takes the

required degree of accuracy as an argument. Therefore, the users of the program conv can

change the allowed error at run time (the program conv is to be discussed in the next

section).

The sequence of integrand evaluations used in the extended trapezoidal integration

is shown in Fig. 7.2. (Press et al., 1988) If we are integrating f(x) over [a, b], we evaluate

f(a) and f(b) in the first step as noted by 1 and 2 in Fig. 7.2. This step will not give

sufficient accuracy unless the function if linear. To refine the grid, we evaluate f((a+b)/2)

in the second step as noted by 3. We continue this process until the integration evaluation

reaches the specified accuracy.

Note that the sequence of integrand evaluation after the third evaluation in Fig. 7.2

resembles a perfectly balanced binary tree. If we want to store the evaluated function

values, it is natural to store them in a binary tree for speed retrieval (to be discussed

subsequently).

1 2

3

4 5

6 7 8 9

N = 1

2

3

4

(total after N = 4)

Fig. 7.2. Illustration of integrand evaluation sequence in trapzd() called by

qtrap(). "Sequential calls to the routine trapzd() incorporate the

information from previous calls and evaluate the integrand only at those

new points necessary to refine the grid. The bottom line shows the totality

of function evaluations after the fourth call." (Press et al., 1988) The

sequence of integrand evaluation after the third evaluation resembles a

perfectly balanced binary tree.

Chapter 7 Convolution for Photon Beams of Finite Size 75

Interpolation and extrapolation of physical quantities

As shown in Fig. 7.2, the function qtrap() will need to evaluate the integrand,

Original data points Interpolation Extrapolation

Nr = 8

G (r

, z)

0 1 2 3 4 5 6

r/ ∆∆r 7 8 9

Nr - 0.5

1 2 1

2

3

4

(total after N = 4)

N

3

4 5

6 7 8 9

a b

Fig. 7.3. Illustration of the interpolation and

extrapolation of the physical quantities. As an

example, the number of grid elements in the r direction Nr is set to 8. ∆r is the grid separation in

the r direction. The integral limits are a and b (see

Eqs. 7.21 & 7.22). The arrows point to the places

where the integrand is evaluated.

76 Chapter 7 Convolution for Photon Beams of Finite Size

hence the physical quantities, at points which may not be the original grid points. Linear

interpolations are used for those points that fall between two original grid points. Linear

extrapolations are used for those points that fall beyond the original grid system. The

interpolation and extrapolation are illustrated in Fig. 7.3. The solid circles represent the

original score values at the grid points. The solid lines and the dashed lines represent the

interpolation and extrapolation respectively. For a given number of grid elements in the r direction (e.g., Nr = 8 in this picture), the extrapolation is only computed up to (Nr - 0.5)

because the linear extrapolation can be unreliable for points beyond (Nr - 0.5). Therefore,

the physical quantity is set to zero beyond (Nr - 0.5). Sometimes, the data may be so

noisy that the function at the last point is even higher than the function at the second to the

last point. In this case, the extrapolation is not used. Instead, we simply set the function

values to zero. Note that as we mentioned in the beginning of Chapter 4, the last cells in

the r direction are used to collect the photons that do not fit into the grid system.

Therefore, the values in the last cell are usually much higher than the values in the second

to the last cells, and hence are not used in the convolution process. In a word, the physical quantities are non-zero in the interval [0, rmax], where rmax is:

rmax = (Nr – 0.5) ∆r (7.16)

where ∆r is the grid separation in the r direction.

Integrand evaluation for Gaussian beams

As shown in Eq. 7.10, the evaluation of the physical quantities is only part of the

integrand evaluation for convolution over the Gaussian beam. Although the integration

has to converge due to physical reasons, the form of Eq. 7.10 may not be directly

computed numerically because the modified Bessel function increases rapidly as the

argument increases, and it can exceed the limit which the computer can hold (e.g., 10+38

for some computers). Therefore, a proper reformulation is required to compute Eq. 7.10.

We note that the modified Bessel function in the region where the argument is large has

the following approximation:

I0(x) ≈ exp(x) / 2 π x for large x (7.17)

Therefore, if we extract the exponential term from I0(), we can make sure the modified

Bessel function decreases as the argument increases. We define the following new function based on I0():

Chapter 7 Convolution for Photon Beams of Finite Size 77

I0e(x) = I0(x) exp(–x) (7.18a)

or

I0(x) = I0e(x) exp(x) (7.18b)

I0e() should always be bounded. Note that Eq. 7.17 is presented just to show the

asymptotic behavior of the function I0(). Eq. 7.18a by no means carries any

approximations. Substituting Eqs. 7.6, 7.7 and 7.18b into Eq. 7.10, it becomes:

C(r, z) = 4 P R2 ⌡

 ⌠

0

G(r'', z) exp[–2 ( r''– r R )

2] I0e( 4rr'' R2

) r'' dr'' (7.19)

Since both the exponential term and the I0e() term decrease, the integrand can be

computed without being out of bound.

Up to now, we have just solved the problem of how to compute the convolution

without overflow. However, the computation speed is another issue. We found that the evaluation of the exp() I0e() in Eq. 7.19 is a major part of the computation for each

integration, which can be up to 90% depending on the specific problem being solved. For

multi-variant physical quantities (e.g., A(r, z)), the convolution may repeatedly evaluate the exp() I0e() in Eq. 7.19 at the same point as the integration is computed for different z

coordinates.

Therefore, if we can save the function evaluations, i.e. the computations of exp() I0e() in Eq. 7.19, for one z coordinate, then we can save a lot computation time.

However, the integration is executed iteratively until a given precision is reached. Hence,

the number of function evaluations is unknown in advance. We can only save the function

evaluations with dynamic data allocation. Furthermore, since the evaluation sequence of

the trapezoidal integration qtrap() resembles a binary tree as in Fig. 7.2., a well-balanced

binary tree can be used to store the function evaluations for searching speed.

Integral Limits for Gaussian beams

Since the integral limits in Eq. 7.14a for circularly flat beams are finite, the

integration can be computed directly using the function qtrap(). In contrast, the upper

integral limit in Eq. 7.19 for Gaussian beams is infinity. This problem can be solved using

78 Chapter 7 Convolution for Photon Beams of Finite Size

variable transformation and the integration can be computed by the routine midexp()

(Press et al., 1988). However, we found this approach is not computationally efficient.

We can reduce the upper limit to a finite value by properly truncating the exponential term

in Eq. 7.19. When

|r" – r| ≤ K R (7.20a)

or

r – K R ≤ r" ≤ r + K R (7.20b)

where K is a constant which can be set in the convolution program conv, we compute the

integrand, otherwise we think the integrand negligible. For example, if we choose K equal

to 4 (which is actually used in the program), the exponential term in Eq. 7.19 is about

1x10–14 whose order of magnitude is considerable larger than the dynamic range of the

order of magnitude of the scored physical quantities.

As we discussed in the beginning of this section, we only compute the physical quantities in the interval [0, rmax], where rmax is given by Eq. 7.16. Combining this limit

and Eq. 7.20b, Eq. 7.19 becomes:

C(r, z) = 4 P R2 ⌡

 ⌠

a

b

G(r'', z) exp[–2 ( r''– r R )

2] I0e( 4rr'' R2

) r'' dr'' (7.21a)

a = Max(0, r – K R) (7.21b)

b = Min(rmax, r + K R) (7.21c)

where the functions Max() and Min() take the greater and the lesser of the two arguments

respectively.

Integrand evaluation for circularly flat beams

The integrand evaluation for circularly flat beams is much simpler than that for

Gaussian beams. However, the evaluation of Iθ() in Eq. 7.14a is time-consuming.

Similar to the integrand evaluation for Gaussian beams, a binary tree is used to store the

evaluated Iθ() to speed up the integration (see the discussion for Gaussian beams).

Chapter 7 Convolution for Photon Beams of Finite Size 79

Integral Limits for circularly flat beams

Since the integral limits in Eq. 7.14a for circularly flat beams are finite, the

integration can be computed directly using the function qtrap(). As we discussed in the beginning of this section, we only compute the physical quantities in the interval [0, rmax],

where rmax is given by Eq. 7.16. Considering this limit, Eq. 7.14a becomes:

C(r, z) = P/(π R2) ⌡⌠ a

b G(r'', z) Iθ(r, r'') 2 π r'' dr'' (7.22a)

a = Max(0, r – R) (7.22b)

b = Min(rmax, r + R) (7.22c)

where the functions Max() and Min() take the greater and the lesser of the two arguments

respectively.

Source of error in convolution

In Eqs. 7.21c and 7.22c, the upper limit of the integration may be limited by rmax

which is the grid limit in the r direction during the Monte Carlo simulation. The physical

quantities beyond the original grid limit in the r direction do not contribute to the

convolution, which leads an error.

Let us discuss the case for circularly flat beams first because it is easier. From Eq.

7.22c, we know that when

rmax ≥ r + R (7.23a)

or

r ≤ rmax – R (7.23b)

the limited grid in the r direction does not affect the convolution. Otherwise, the

convolution is truncated by the limited grid in the r direction. This effect can be see in the next section. Therefore, we should not trust the convolution data for r ≥ rmax – R. In

other words, if you want to observe the physical quantity at r in response to a circularly

80 Chapter 7 Convolution for Photon Beams of Finite Size

flat beam of radius R, the grid limit in the r direction should be large enough so that Eq.

7.23a holds when you perform the Monte Carlo simulation with mcml.

For Gaussian beams, there are no clean formulas like Eqs. 7.23 to describe the

valid range because the Gaussian beams theoretically extend to infinity in the r direction.

However, the convolution results of a Gaussian beam with a 1/e2 radius of R is so close

to those of a circularly flat beam with a radius of R for r >> R (shown in the next section).

Therefore, we can use the same criteria for circularly flat beams (Eqs. 7.23) for Gaussian

beams to certain precision.

The other source of error is due to the Monte Carlo simulation by mcml 1.0. This

version of mcml does not score the first interactions separately (see Section 4.3) as

Gardner et al. (1992b) did. This may make considerable error if the radius of the

Gaussian beam is less than three times the grid separation ∆r. In other words, the following equation should be satisfied to get reliable convolution:

R ≥ 3 ∆r (7.24)

In summary, when Eqs. 7.23 and 7.24 hold, the convolution should be reliable.

7.5 Computation results of conv and verification

The convolution process of the Monte Carlo simulation results from mcml is

implemented in another program called "conv". Like the program mcml, it is written in

ANSI Standard C, hence it can be executed on a variety of computers.

Convolution results of Gaussian beams

We do not have any standard data to verify the convolution program. However,

Craig Gardner kindly provided some convolution results using his convolution program

(Gardner et al., 1992). The impulse responses are based on the simulation discussed in

Section 6.6 where we have used the same turbid media and grid system. He computed his

convolution on his Monte Carlo simulation results, and we did it on ours after we

compared the Monte Carlo simulation results in Section 6.6.

The incident photon beam is a Gaussian beam with total energy of 1 J and radius of

0.1 cm. The convolved diffuse reflectances and transmittance are compared in Figs. 7.4

and 7.5 respectively. The convolved fluences are compared in Fig. 7.6. Note that the

Chapter 7 Convolution for Photon Beams of Finite Size 81

curves in Figs. 7.4-6 bend down faster near r equal 0.5 cm. This can be explained by the

integrations in Eqs. 7.21. Due to the spatially limited range of the grid system (50 radial grids of 0.01 cm spacing, or rmax = 0.5 cm), the upper limit of the integration is cut by

rmax more and more as the observation point r approaches rmax. Therefore, the

integration underestimates the true value.

10 -2

10 -1

10 0

10 1

0

R d [J /c

m

2 ]

0.1 0.2 0.3 0.4 0.5

Gardner et al.

r [cm]

conv

Gaussian beam responses Total energy: 1 J 1/e 2 radius: 0.1 cm

Fig. 7.4. Comparison between diffuse reflectances as a function of r based

on conv and Gardner's computation (Gardner et al., 1992).

10 -2

10 -1

10 0

0 0.1

T t [ J/

cm

2 ]

0.2 0.3 0.4 0.5

Gardner et al. conv

r [cm]

Gaussian beam responses Total energy: 1 J 1/e 2 radius: 0.1 cm

82 Chapter 7 Convolution for Photon Beams of Finite Size

Fig. 7.5. Comparison between transmittances as a function of r based on

conv and Gardner's computation (Gardner et al., 1992).

10 -1

10 0

10 1

10 2

0

F lu

e n ce

[J /c

m

2 ]

0.1 0.2 0.3 0.4 0.5

Gardner et al. z=0.005

r [cm]

Gardner et al. z=0.205

conv z=0.005 cm

conv z=0.205 cm

Gaussian beam Total energy: 1 J

1/e 2 radius: 0.1 cm

Fig. 7.6. Comparison between fluences as a function of r for given z

coordinates based on conv and Gardner's computation (Gardner et al.,

1992).

For 2D arrays such as the fluence as a function of r and z, conv has the ability to

output the data in contour format (see Sections 10.9 and 10.10). The fluence as a

function of r and z of the Gaussian beam is shown in contour lines in Fig. 7.7.

Chapter 7 Convolution for Photon Beams of Finite Size 83

0.4

0.3

0.2

0.1

0

0 0.1

z [c

m ]

0.2 0.3 0.4

air: n = 1

layer 1: n = 1.37 µa = 1, µs = 100

g = 0.9

layer 2: n = 1.37 µa = 1, µs = 10

g = 0

r [cm]

layer 3: n = 1.37 µa = 2, µs = 10 g = 0.7

air: n = 1

50 25 10 5 2.5 1 0.5

Fig. 7.7. Contour plot of the fluence [J cm−2] as a function of r and z

based on conv for a Gaussian beam. The Gaussian beam has total energy

of 1 J and 1/e2 radius of 0.1 cm. The Monte Carlo simulation is for a three-layer tissue of Table 6.6. The absorption coefficient µ a and

scattering coefficient µs are in cm–1.

Convolution results of circularly flat beams

For circularly flat photon beams, we do not have other results for comparison.

However, we would like to compare the results of circularly flat photon beams with that

of Gaussian beams. The results of Gaussian beams are taken from the above

computations. The circularly flat beam has 1 J of total energy and 0.1 cm of radius. The

diffuse reflectances, transmittances and fluences are compared respectively in Figs. 7.8,

7.9, and 7.10.

84 Chapter 7 Convolution for Photon Beams of Finite Size

10 -2

10 -1

10 0

10 1

0

R d [J /c

m

2 ]

0.1 0.2 0.3 0.4 0.5

Flat

r [cm]

Gaussian

Total energy: 1 J 1/e 2 radius: 0.1 cm Computed by conv

Fig. 7.8. Comparison between diffuse reflectances as a function of r

convolved over a Gaussian beam and a flat beam using conv. Both beams

have total energy of 1 J and radii of 0.1 cm.

10 -2

10 -1

10 0

0 0.1

T t [ J/

cm

2 ]

0.2 0.3 0.4 0.5

Flat Gaussian

r [cm]

Total energy: 1 J 1/e 2 radius: 0.1 cm Computed by conv

Fig. 7.9. Comparison between transmittances as a function of r convolved

over a Gaussian beam and a flat beam using conv. Both beams have total

energy of 1 J and radii of 0.1 cm.

Chapter 7 Convolution for Photon Beams of Finite Size 85

10 -1

10 0

10 1

10 2

0

F lu

e n ce

[J /c

m

2 ] 0.1 0.2 0.3 0.4 0.5

Flat

r [cm]

Gaussian

Computed by conv z = 0.005 cm Total energy: 1 J Radius: 0.1 cm

Fig. 7.10. Comparison between fluences as a function of r at z equal 0.005

cm convolved over a Gaussian beam and a flat beam using conv. Both

beams have total energy of 1 J and radii of 0.1 cm.

It is observed that the Gaussian beam and the flat beam give nearly the same

results when r is larger than about 2 R, where R is the radius of the beams. Furthermore,

both kinds of responses bend down when r approaches 0.5 cm which is the grid limit in the

r direction as discussed in last section.

Convolution error

The convolution integration is computed iteratively. The iteration stops when the

difference between the new estimate and the old estimate of the integration is a small part

of the new estimate. This small ratio can be controlled by users using command "e". It

ranges between 0 to 1. Small values would give better precision but longer computation

time and vice versa. Normally, 0.001 to 0.1 is recommended. Sometimes, a high allowed

error can cause some discontinuity in the convolved results. If this happens, choose a

lower allowed convolution error and redo the convolution. For example, the convolution

over a Gaussian beam in Fig. 7.10 has been done with an allowed convolution error of

0.001. If we choose the allowed convolution error to be 0.01, we can see the

discontinuity in the fluence distribution (Fig. 7.11).

86 Chapter 7 Convolution for Photon Beams of Finite Size

10 -1

10 0

10 1

10 2

0

F lu

e n ce

[J /c

m

2 ]

0.1 0.2 0.3 0.4 0.5

Error = 0.1%

r [cm]

Error = 1%

discontinuity

Computed by conv z = 0.005 cm Gaussian beam Total energy: 1 J Radius: 0.1 cm

Fig. 7.11. Comparison between fluences as a function of r at z equal 0.005

cm convolved over a Gaussian beam with different allowed convolution

errors using conv. The result with allowed error of 0.01 has discontinuity

around r = 0.15 cm. Using an allowed error of 0.001 eliminates the

discontinuity. The Gaussian beams have total energy of 1 J and 1/e2 radii

of 0.1 cm.

Chapter 8 Installing mcml and conv 87

Part II. User Manual

8. Installing mcml and conv

This chapter provides the instructions on how to install the software. Since both

mcml and conv are written in ANSI Standard C, they in principle should be able to be

compiled on any computer systems that support ANSI C. Subject to the computer

systems available to this laboratory, we will only provide the executables for Sun

workstations, IBM PC compatibles, and Macintoshes. On Sun SPARCstations 2, we have

compiled the mcml and conv using the ANSI C (acc). On IBM PC compatibles, we used

Microsoft QuickC. And on Macintoshes, we used Symantec THINK C. We will provide

the source code, users can feel free to compile them on their computer systems. Consult

corresponding manuals for information on how to compile the code.

As Monte Carlo simulations are computationally intensive, we suggest that you

use workstations such as Sun SPARCstations on which you can submit background jobs

and which provide high speed computation. The convolution program is also more

pleasant to use if you have a fast computer, although it is not as computation-intensive as

Monte Carlo simulations.

8.1 Installing on Sun workstations

The distribution disk is an IBM format double density 3 1/2" disk, which Sun

SPARCstation 2 should be able to read. All the files are packed into one file. Copy the

file mcR1_1.tar to a working directory using the command: mcopy a:mcR1_1.tar ., where

the period means the current directory, and then untar the file using the command: tar -

xvfo mcR1_1.tar.

The package includes three directories: mcmlcode, convcode, and Sun. The

directory mcmlcode includes all the source code of mcml and the makefile used for acc.

The directory convcode (not provided this time) includes all the source code of conv and

the corresponding makefile. You need to modify the makefiles for other compilers (See

Appendix C). The directory Sun includes all the executables, a template file of mcml input

(template.mci), a sample mcml output file (sample.mco), and a short manual

(mcmlconv.man) which is Chapter 8-10 of this manual.

88 Chapter 8 Installing mcml and conv

To install the executables, copy the executables to the sub directory ~/bin under

your home directory. Then, put the directory ~/bin under the search path in .cshrc or

.login if you are using C Shell. Consult manual if you are using other shells.

Having finished copying, you can eject the disk using the command eject. If your

Sun workstation does not have a floppy drive, you can transfer the files through a

networked IBM PC or a compatible. If you have an electronic mail address, we can also

send the package to you through mail.

8.2 Installing on IBM PC compatibles

For IBM PC's or compatibles, the distribution disk is a double density 3 1/2" disk.

An alternative 5 1/4" disk can be sent upon request. All the files are packed into one file.

Copy the self-extracting file mcR1_1.exe to your working directory on your hard drive

and run the file to extract all packed files using the command: mcR1_1.exe -d, where the

option "-d" keeps the directory structure.

The package includes three directories: mcmlcode, convcode, and IBMPC. The

directory mcmlcode includes all the source code of mcml. The directory convcode (not

provided for now) includes all the source code of conv. The directory IBMPC includes all

the executables, a template file of mcml input (template.mci), a sample mcml output file

(sample.mco), and a short manual (mcmlconv.man) which is Chapter 8-10 of this manual.

The executables include mcml.exe and conv.exe. The code was compiled and linked using

Microsoft QuickC 2.5. The executables will be able to detect whether your computer has

math coprocessor, and take advantage of the math coprocessors if they are present.

If you want to be able to execute the programs under any directory, you should

put the directory IBMPC in the search path. The search path can be changed in the file

autoexec.bat.

8.3 Installing on Macintoshes

For Macintoshes, the distribution disk is a double density 3 1/2" disk. All the files

are packed into one file. Copy the self-extracting file mcR1.1.sea to a working folder on

you hard drive, and double click on the icon to extract the files.

The package includes three folders: mcmlcode, convcode, and Mac. The folder

mcmlcode includes all the source code of mcml. The folder convcode includes all the

Chapter 8 Installing mcml and conv 89

source code of conv. The folder Mac includes all the executables, a template file of mcml

input (template.mci), a sample mcml output file (sample.mco), and a short manual

(mcmlconv.man) which is Chapter 8-10 of this manual. The executables include

mcml.fpu, conv.fpu, mcml.020, conv.020, mcml.000, and conv.000 for different types of

computers as discussed subsequently.

Before you install the executables, you need to know what kind of Macintosh you

are using. You can test the following conditions to decide which executables to use:

A. MC68040

B. MC68020 or MC68030

C. MC68881 or MC68882

If your Macintosh meets condition A, or conditions B and C, you should copy the

executables with extensions ".fpu". If your Macintosh meets condition B only, you should

keep the executables with extensions ".020". Otherwise, you should use the executables

with extensions ".000". We suggest that you remove the extensions of the executables on

your hard drive to keep consistency with the manual.

8.4 Installing by Electronic Mail

For these users who have electronic mail access on UNIX machines, we can

deliver the software package through electronic mails. The package is archived using the

command tar, compressed using the command compress, then encoded using the

command uuencode before it is mailed out using the mail utilities. After you receive the

mail, you need to do the following.

1. Save the mail as a file, e.g., mc.mail.

2. Decode the file (mc.mail) to get a file named mc.tar.Z using

uudecode mc.mail

3. Uncompress the file mc.tar.Z to get the file mc.tar

uncompress mc.tar.Z

4. Unarchive the file mc.tar to get the package using:

90 Chapter 8 Installing mcml and conv

tar -xvfo mc.tar

At this moment, you should have three directories under the working directory. They are

mcmlcode, convcode, and Sun, or IBMPC, or Mac.

If you ordered a Sun version of the package, you only need to put the executables

under the proper directory., e.g., ~/bin (see Section 8.1).

If you ordered an IBM PC version or a Mac version of the package, you need to

transfer the files to your local computer using FTP or modem. Then refer to Section 8.2

or 8.3 for details.

It is appropriate to describe in more detail how we send the package through

electronic mails which is exactly the opposite of the above procedure. We put the

package in a working directory which include three subdirectories: mcmlcode, convcode,

and Sun, or IBMPC, or Mac. Then:

tar -cvf mc.tar

compress mc.tar

uuencode mc.tar.Z mc.tar.Z > mc.mail

mail your_address

In the mail utility, you can add in any messages in the beginning of the mail, then you need

to use the command r to read in the file mc.mail. Then, you can send the file by typing a

period "." and a return in a new line (see the manual page of mail).

Chapter 9 Instructions for mcml 91

9. Instructions for mcml

This chapter describes the actual instructions to use mcml. Macintoshes, IBM PC

compatibles and UNIX machines are used as examples of computer systems, although

mcml can execute on any computer systems that support ANSI Standard C. The reader is

assumed to be familiar with the operating system and comfortable with at least one of the

text editors on the computer system to be used to execute mcml. Three steps involved in

the Monte Carlo simulation using mcml are included in the following sections: preparing

the input data file, executing the program mcml with the input data file, processing the

output data in the data files named in the input data file. We will also show some known

bugs.

9.1 File of input data

The first step to do the simulation using mcml is to prepare an input data file (e.g.,

"filename.mci"). Any valid filenames on your system without spaces will be acceptable,

but extension ".mci" is recommended. In ANSI C, spaces are used as separators.

Therefore, filenames with spaces may not be accepted by mcml, although they are allowed

by some operating systems themselves such as the Macintosh System. We will use

"filename.mci" as an example in the following discussions.

This input data file may be edited with any text editors such as Apple Edit,

MockWrite or Microsoft Word on Macintoshes, Norton editor NE or Microsoft Word on

IBM PC compatibles, vi editor or EMACS on UNIX systems. However, if you use word

processors like Microsoft Word to edit the file, make sure that you save the file in text

format since mcml does not accept binary files as input. If you are using the UNIX system

and are uncomfortable with vi or other editors available on UNIX, you can use editors on

your personal computer, then transfer the file using Kermit if you use modem or FTP if

your personal computer is on a network. Make sure to use ASCII or text mode when you

transfer this file.

The best way to write an input data file is to make a copy of the template file

called "template.mci" (See Appendix D), then modify the parameters in the file. The input

data file is organized line by line. All parameters must be in the right order. The lines with

parameters in order must also be in order themselves. However, feel free to insert

comment lines or space lines in between to make the file more readable. Comment lines

92 Chapter 9 Instructions for mcml

start with the symbol "#". The symbol "#" can also be used after the parameters in a line

to mark the start of comments.

The parameters in the input data file are read by mcml line by line. If there are

multiple parameters in a line, use tabs or spaces to separate them. A tab is preferred,

because it aligns the parameters for better readability. All dimensional quantities are in cm

or derived from cm. The thickness of each layer is in cm. The grid line separations are

also in cm. Absorption coefficient and scattering coefficient are in 1/cm. Each line of the

input file is explained in the order that they appear in the input data file as follows.

1. File version of the input data file. Always use "1.0" for now.

2. Number of runs (integer). Each run is an independent simulation. You can specify

any number of runs, which is not subject to memory limit. Make sure you use an

integer instead of a floating point number for this parameter, e.g., 5 instead of 5.0.

3. Output filename and file format. Extension ".mco" is recommended for the output

filenames, e.g., "output1.mco". The program mcml currently only supports ASCII

format, therefore always use "A" as the second parameter in this line. Make sure

that you use different output filenames if you have multiple runs in an input data

file, although mcml checks for this mistake. What is more important is that the

filenames should not be the same as the names of existent ones unless you want to

overwrite the existent files on purpose. Since the program mcml does not check

this error, you will lose the existent files.

4. Number of photon packets to be traced (integer).

5. Separations (in cm) between grid lines in z and r direction of the cylindrical

coordinate system. These are floating point numbers. Both z and r originate from

the photon incident point on the surface of first layer, and the z axis points down

into the turbid medium. Make sure these parameters are large enough to give you

an acceptable variance, and small enough to give you an acceptable resolution.

These parameters should be determined coordinately with the number of photons

to achieve both accuracy and resolution. Also note that users should try to choose

grid size in the z direction so that grid boxes do not cross tissue-tissue interfaces

or boundaries (see Section 9.5).

Chapter 9 Instructions for mcml 93

6. Number of grid elements (integers) in the z, r directions of the cylindrical

coordinate system and in the alpha direction, where alpha is the angle spanned

between the photon exiting direction and the surface normal. Since the angle

always covers 0 through 90 degrees, the angular separation is 90 degrees divided

by the number of angular grid elements specified in this line. Be careful with this

line, if the numbers are too large, the output file will be very big because 2D arrays

are written into the output file. If you do not need to resolve one of the directions

(z or r) or the angle, use 1 (not 0) for that parameter. Make sure to use integers

for these three parameters.

7. Number of layers (integer). This number does not include the ambient media above

or below the tissue.

8. Refractive index for the top ambient medium above the first layer (e.g., 1.0 for

air).

9. Layer parameter lines. One line for each layer. In each line are the refractive

index, the absorption coefficient (1/cm), the scattering coefficient (1/cm), the

anisotropy factor, and the thickness (cm). To simulate semi-infinite tissue, use a

very large thickness (e.g., 1E8 cm) compared with the mean free path of the tissue.

10. Refractive index for the bottom ambient medium below the last layer (e.g., 1.0 for

air).

11. Repeat lines 3 through 10 for each additional run if you have multiple runs.

Note: Two points are worth noting. The only limit to the number of grid elements

and layers is the amount of memory allocated to mcml in your system because the arrays

are dynamically allocated according to these parameters. Do not use floating point

numbers for the integers. Otherwise, the program may interpret them incorrectly.

However, you may use integers for floating point numbers, e.g., 100 instead of 100.0.

9.2 Execution

Once the input data file is prepared, the program mcml can be executed using the

input data file. During the execution, the program mcml will report an output message

which gives the number of photons remaining in the simulation, the number of runs left,

and the time of ending the job. The first report is after 10 photon packets are traced, then

94 Chapter 9 Instructions for mcml

it is updated when every 1/10 of the total number of photon packets are traced. The

methods of execution are slightly different on different operating systems.

Macintosh

To run mcml on Macintosh System 6, you have to copy or move the executable

mcml to your working folder where the input data file resides, then double click on the

mcml icon to start the program. The program mcml will prompt for the input data

filename, which is entered through the keyboard. If the input data file cannot be found,

the program will prompt you again until it finds the file or a period "." is typed, where "."

is used to abort the program. If you use Macintosh System 7, you may use an alias of

mcml instead of a copy of it.

IBM PC compatibles

For IBM PC compatibles, make sure that the directory with mcml is in the search

path, which can be checked by typing the command "path" or the file "autoexec.bat". To

run mcml with the input data file as a command parameter under DOS command prompt,

type:

mcml filename.mci

If you want to save the output message as a file (e.g., message.out), type:

mcml filename.mci > message.out

which redirects the output message to the file "message.out". To run mcml in the

interactive mode, type the following command without input data filename:

mcml

Then, the program mcml will prompt for the input data file.

UNIX

On a UNIX system, you should place the executable mcml in a directory that is in

the search path. The directory ~/bin is a good choice. The search path can be found and

modified in the file ".cshrc" if you are using C Shell or the file ".login". The three ways of

Chapter 9 Instructions for mcml 95

invoking mcml under DOS can be used under UNIX operating systems. Moreover, if you

wish to discard the messages during the execution, use the command:

mcml filename.mci > /dev/null

which redirects the output to the "bit bucket" (/dev/null). You can also simply submit a

background job using:

mcml filename.mci > /dev/null &

Refer to your UNIX manual for how to inquire about the status of a background

job. If you are still in the same session, the command "jobs" can be used in C Shell.

Otherwise, you should use the UNIX command "ps" to check for background processes.

You can also directly look for the output files to check if the job is done.

9.3 File of output data

When the job is completed, the results will be written into the output data files as

you named in your input data file. A sample output data file is shown in Appendix E. The

output data files can be read with any text editors if they are ASCII as a result of using

"A" for the file format in the input data file. They may be big if your numbers of grid

elements are large.

The contents of output files are self explanatory. The same policy for the input

data file is used for the output data file, that is, comment lines starting with a symbol "#"

and space lines are written to the file for clarity. The first line is used for file type

identification when the file is read by other applications. Then, the user time spent on the

simulation is reported in a comment line. Then, a few categories of data are reported

sequentially in the following order: pure numbers, 1D arrays and 2D arrays. The

definitions of the output data can be found in Chapter 4. The category "InParm" reports

all the input parameters specified in the input data file again so that the output file is a

complete reference and the input parameters may also be double checked against any

errors in the input data file. The category "RAT" reports the specular reflectance, the

total diffuse reflectance, the total absorption, and the total transmittance. The category

"A_l" is the absorption as a function of layer. The category "A_z" is the absorption as a

function of depth z. The categories "Rd_r" and "Rd_a" are the diffuse reflectances as a

function of radius r and angle alpha respectively. The categories "Tt_r" and "Tt_a" are the

transmittance as a function of radius r and angle alpha respectively. The 2D arrays A_rz,

96 Chapter 9 Instructions for mcml

Rd_ra, and Tt_ra are then reported. The category A_rz is the absorption as a function of

depth z and radius r. The categories Rd_ra and Tt_ra are correspondingly the diffuse

reflectance and transmittance as a function of radius r and angle alpha. The name of each

category is written before the data, such that the data can be easily identified. The units

for these data were discussed in Chapter 4.

9.4 Subset of output data

Sometimes, only a subset of the output data is needed for presentation or

processing. For example, we may need to print a 1D array into a file in XY format,

namely two columns of data, or a 2D array in XYZ format. These files can then be read

into some commercial applications such as AXUM on IBM PC compatibles and

KaleidaGraph on Macintoshes. This subset extraction can be done using another program

-- conv. The program conv is intended to read in the output data file of mcml that gives

responses of infinitely narrow photon beam, and convolve the output data if the responses

of finite size beam are to be computed. The program conv can output the original data or

the convolved data in various formats. The convolution part of the program conv has not

been finished, although it can be used to extract subsets of the original output data.

The program conv is made to be interactive. After the program is invoked, the

menu system will direct the data input, output, or process. On Macintosh, copy or move

the program conv to your working folder. Start conv by double clicking on the icon. On

IBM PC compatibles or UNIX machines, invoke the program conv by typing:

conv

Follow the menu to input an mcml output file (e.g., "filename.mco"). Then, output

specific data to new files. However, for the sake of efficiency, we wrote a C Shell script

file "conv.bat" for UNIX users. For shell programming, refer to Anderson et al. (1986) or

Arthur (1990). A similar file can be written on MS-DOS operating system. The file

"conv.bat" is used for fast batch process. For example, if there are several mcml output

files named "outfile1.mco", "outfile2.mco"... "outfilen.mco", and you need to select the

diffuse reflectance as a function of radius r of these mcml output files on a UNIX system,

then you can use the command:

conv.bat "outfile*.mco" Rr

Chapter 9 Instructions for mcml 97

This command takes two arguments. The first one gives the mcml output files to be

processed. If wild cards, such as * or ?, are used, the argument has to be within quotes to

prevent immediate file expansion. The second argument gives the type of subsets to be

extracted. In this case, it is the diffuse reflectance as a function of r. The files of the

subsets will be named as "outfile*.Rr". In each of these output files, there are two

columns, the first one is the radius, and the second one is the reflectance. To check the

complete usage of "conv.bat", type "conv.bat" on command line. More examples are:

conv.bat "outfile*.mco" Az

for 1D absorption as a function of z. In each of the output files of this command, there

are two columns representing z and the internal absorption respectively.

conv.bat "outfile*.mco" Azr

for 2D absorption as a function of z and r. In each of the output files of this command,

there are three columns representing z, r, and the internal absorption respectively.

If you use UNIX to do the simulation and want to present the results using

Macintosh or IBM PC compatibles, transfer the smaller subset files using KERMIT if you

use modem or FTP if you use Ethernet.

9.5 Bugs of mcml

1. Users have to be careful with several known bugs about the program mcml version

1.0. If a grid element crosses a medium interface, e.g., a glass/tissue interface, the

photon absorption within this grid element is considered to be the absorption in the

medium where the center of the grid element is located. Therefore, if the center is

on the side of the glass, mcml may report small absorption in the glass.

Sometimes, this problem may be avoided by choosing the z-grid system carefully

so that the boundaries of elements align with the layer interfaces.

2. The user time of a simulation can be reported as zero if the simulation is long

enough to overflow the timer (See the function clock() in the file "mcmlmain.c").

3. The input parameters in the input data file have to be in the order as specified.

Furthermore, you have to use integers for number of photon packets, number of

layers, and number of grid elements in the input data file. If floating point numbers

98 Chapter 9 Instructions for mcml

are inadvertently used, mcml can not detect the error and may read in the wrong

parameters.

If you find any new bugs, please report to us using the information in Appendix G.

It is very important that you provide us enough information about the bug so that we can

reproduce it.

Chapter 10 Instructions for conv 99

10. Instructions for conv

This chapter describes the instructions to use the program conv, which is used to

convolve the impulse responses of mcml over incident beams of finite size. This program

reads the output of mcml, then convolves the impulse responses according to the user

specified incident beams. The program can output the original data from mcml or the

convolved data in various ASCII formats as discussed subsequently.

10.1 Start conv

To start conv on IBM PC compatibles or UNIX machines, invoke the program

conv by typing:

conv

To use conv on Macintoshes, copy or move the program conv to your working folder.

Then, double click the conv icon to start it. If you are using System 7, you may take

advantage of the alias mechanism.

10.2 Main menu of conv

Once conv is started, it is in the main menu of the program after showing some

information about the program. In the main menu, the program prompts for a command

as:

> Main menu (h for help) =>

To show all the available command, type "h" and return key. It will show you the

following information and prompt for the next command. You only need to show the help

information when you forget the commands.

i = Input filename of mcml output b = specify laser Beam r = convolution Resolution. e = convolution Error. oo = Output Original data oc = Output Convolved data co = Contour output of Original data cc = Contour output of Convolved data so = Scanning output of Original data sc = Scanning output of Convolved data q = Quit * Commands in conv are not case-sensitive

> Main menu (h for help) =>

100 Chapter 10 Instructions for conv

Each command will be introduced subsequently.

10.3 Command "i" of conv

You have to provide the filename of the mcml output to conv. This can be done

by typing "i" and return key in the main menu prompt, then type in the filename of the

mcml output. For example:

> Main menu (h for help) => i Input filename of mcml output(or . to quit): example.mco

> Main menu (h for help) =>

The program returns to the main menu automatically. If the file cannot be located or

opened, the program will prompt you to type in another filename. You can also type "."

and return key to quit inputting the filename. If the file is not the output of mcml, the

program will quit to the operating system. You need to start the program again.

10.4 Command "b" of conv

You need to specify the type and parameters of the incident beam. In version 1.0

of conv, only Gaussian beams and circularly flat (rectangular) beams are supported. To

enter the incident beam, use command "b". Then you have to choose from "f" for flat

beam, "g" for Gaussian beam, or "q" to quit this command. If you choose either flat beam

or Gaussian beam., conv asks the total energy and the radius of the beam. For example:

> Main menu (h for help) => b Beam profile:f=flat, g=Gaussian. q=quit: f Total energy of the flat beam [J]: 1 Radius of the flat beam [cm]: .1 Total power: 1 J, and radius: 0.1 cm.

> Main menu (h for help) =>

It returns to the main menu automatically. Although we specify units of energy for the

incident beam, you can substitute units of power throughout the program. To get reliable

results, the radius should be much larger than the grid separation in the r direction of the

original mcml output, and much less than the total covered radius by the grid system in the

r direction of the original mcml output. As a rule of thumb, the radius should be in the

range between about 3 times the grid separation in the r direction and the total grid

coverage in the r direction minus the maximum radius of observation (see Eqs. 7.23 &

7.24 in Section 7.4).

Chapter 10 Instructions for conv 101

10.5 Command "r" of conv

This command is used to change the grid separation and the number of grid elements in

the r direction for the convolution. Since they take the values of the mcml output as the

default, you do not have to enter this command if you do not want to change them. The

maximum convolution radius should not be larger than that of the original mcml output to

get reliable results. For example:

> Main menu (h for help) => r Current resolution: 0.01 cm and number of points: 50 Input resolution in r direction [cm]: .02 Input number of points in r direction: 20 Resolution: 0.02 cm and number of points: 20

> Main menu (h for help) =>

Note that if the number of points is chosen too large, the program can exit due to

the lack of memory. This is a bug in the current version of conv.

10.6 Command "e" of conv

The integration is computed iteratively. The iteration stops when the difference

between the new estimate and the old estimate of the integration is a small part of the new

estimate. This small ratio can be controlled by users using command "e". It ranges

between 0 to 1. Small values would give better precision but longer computation time and

vice versa. Normally, 0.001 to 0.1 is recommended. The default value is 0.1. For

example:

> Main menu (h for help) => e Relative convolution error Current value is 0.05 (0.001-0.1 recommended): .01

Special attention has to be paid to this command. The convolution results may

have weird discontinuities if the allowed convolution error is too high (see Fig. 7.11), and

the convolution process may take too long if the convolution error is too low. The rule of

thumb is that you choose the lowest convolution error that does not make the convolution

too long to compute. If the convolution results still have any discontinuities which should

not be there, you need to decrease the convolution error and redo the convolution.

10.7 Command "oo" of conv

After you input the filename of the mcml output , you can output the original data

of the mcml output with various formats. One of the formats can be obtained by the

command "oo". For example:

102 Chapter 10 Instructions for conv

> Main menu (h for help) => oo

> Output mcml data (h for help) => h I = Input parameters of mcml 3 = reflectance, absorption, and transmittance AL = absorption vs layer [-] Az = absorption vs z [1/cm] Arz = absorption vs r & z [1/cm3] Fz = fluence vs z [-] Frz = fluence vs r & z [1/cm2] Rr = diffuse reflectance vs radius r [1/cm2] Ra = diffuse reflectance vs angle alpha [1/sr] Rra = diffuse reflectance vs radius and angle [1/(cm2 sr)] Tr = transmittance vs radius r [1/cm2] Ta = transmittance vs angle alpha [1/sr] Tra = transmittance vs radius and angle [1/(cm2 sr)] K = Keijzer's format Q = Quit to main menu * input filename: example.mco

> Output mcml data (h for help) =>

At this point, you can output various physical quantities by inputting the

subcommands, which can be listed by command "h" as shown above. After you type the

command, the program will ask you for the output filename. The exact physical meanings

of these physical quantities can be found in Chapter 4. The command "i" outputs the input

parameters of mcml to a file. The command "3" outputs three quantities to a file including

specular reflectance, total diffuse reflectance, absorption probability, and total

transmittance, which are actually four numbers. The command "Al" outputs the

absorption probability as a function layer to a file. The command "Az" outputs the

absorption as a function of z coordinate whose dimension is cm–1. The command "Arz"

outputs the absorption probability density as a function of r and z whose dimension is cm–

3. The commands "Fz" and "Frz" output the results of the commands "Az" and "Arz"

divided by the absorption coefficients. The command "Rr" outputs the diffuse reflectance

as a function of r whose unit is cm–2. The command "Ra" outputs the diffuse reflectance

as a function of the exit angle α, whose dimension is sr–1. The command "Rra" outputs the diffuse reflectance as a function of r and α, whose unit is cm–2 sr–1. Similarly, the commands "Tr", "Ta" and "Tra" are the corresponding commands for the transmittance.

The command "K" is used to convert the format of the mcml output to the format of

Marleen Keijzer's convolution program (in PASCAL on Macintoshes) which was used by

our group before the program conv was written. This command is only useful if you have

Marleen Keijzer's program. The command "q" will return the program to the main menu.

For 1D arrays, the outputs are in two columns. The first column gives the

independent variable, and the second column gives the physical quantities. For example,

Chapter 10 Instructions for conv 103

the output of the command "Rr" will have two columns. The first column gives the radius

in cm, and the second column gives the diffuse reflectance in cm–2.

For 2D arrays, the outputs are in three columns. The first two columns give the

first and the second independent variables, and the third column gives the physical

quantities. For example, the command "Arz" will give three columns. The first two

columns give r and z in cm respectively, and the third column gives the absorption

probability density in cm–2 sr–1 as a function of r and z.

An example is shown as follows:

> Output mcml data (h for help) => Rr Enter output filename with extension .Rr (or . to quit): example.Rr

> Output mcml data (h for help) =>

This command will output the diffuse reflectance as a function of r to the file named

"example.Rr".

10.8 Command "oc" of conv

After you input the filename of the mcml output and specify the incident photon

beam, you can output the convolved data with various formats. One of the formats is

writing data in columns, which can be obtained using the command "oc". For example:

> Main menu (h for help) => oc

> Output convolved data (h for help) => h Arz = absorption vs r & z [J/cm3] Frz = fluence vs r & z [J/cm2] Rr = diffuse reflectance vs radius r [J/cm2] Rra = diffuse reflectance vs radius and angle [J/(cm2 sr)] Tr = transmittance vs radius r [J/cm2] Tra = transmittance vs radius and angle [J/(cm2 sr)] Q = Quit to main menu * input filename: example.mco

> Output convolved data (h for help) =>

At this point, you can output various physical quantities by inputting the

subcommands, which can be listed by command "h" as shown above. After you type the

command, the program will ask you for the output filename. The exact physical meanings

of these physical quantities can be found in Chapters 4 and 7. The command "Arz"

outputs the absorption energy density as a function of r and z whose dimension is J cm–3.

The command "Frz" outputs the results of the command "Arz" divided by the absorption

coefficients, which is the fluence in J cm–2. Since we consider steady-state responses only

104 Chapter 10 Instructions for conv

in mcml and conv, you can systematically replace the energy [Joules] with power [Watts]

in conv.

The command "Rr" outputs the diffuse reflectance as a function of r whose unit is

J cm–2. The command "Rra" outputs the diffuse reflectance as a function of r and α, whose unit is J cm–2 sr–1. Similarly, the commands "Tr" and "Tra" are the corresponding

commands for the transmittance. The command "q" will return the program to the main

menu.

For 1D arrays, the outputs are in two columns. The first column gives the

independent variable, and the second column gives the physical quantities. For example,

the output of the command "Rr" will have two columns. The first column gives the radius

in cm, and the second column gives the diffuse reflectance in J cm–2.

For 2D arrays, the outputs are in three columns. The first two columns give the

first and the second independent variables respectively, and the third column gives the

physical quantities. For example, the command "Arz" will give three columns. The first

two columns give r and z in cm respectively, and the third column gives the absorption

energy density in J cm–2 sr–1 as a function of r and z.

An example is shown as follows:

> Output convolved data (h for help) => Rr Enter output filename with extension .Rrc (or . to quit): example.Rrc

> Output convolved data (h for help) =>

This command will output the diffuse reflectance as a function of r to the file named

"example.Rrc".

10.9 Command "co" of conv

After you input the filename of the mcml output, you can output the original data

of the mcml output with various formats. One of the formats for 2D arrays is writing data

in contour lines. Every contour line will be given by two columns. This format can be

obtained using the command "co" standing for "contours of the original data". Then, the

output file can be imported to some plotting software such as KaleidaGraph on

Macintoshes, and the contour lines can be drawn. For example:

> Main menu (h for help) => co

> Contour output of mcml data (h for help) => h

Chapter 10 Instructions for conv 105

A = absorption vs r & z [1/cm3] F = fluence vs r & z [1/cm2] R = diffuse reflectance vs radius and angle [1/(cm2 sr)] T = transmittance vs radius and angle [1/(cm2 sr)] Q = Quit to main menu * input filename: example.mco

> Contour output of mcml data (h for help) =>

Since only the 2D arrays need to be presented in contour lines, there are only four

physical quantities. The command "A" outputs the absorption probability density as a

function of r and z whose dimension is cm–3. The command "F" outputs the probability

fluence as a function of r and z in cm–2. The commands "R" and "T" output diffuse

reflectance and transmittance as a function of r and α in cm–2 sr–1.

After you input one of the commands, the program will prompt for the output

filename and the isovalues for the contour output. The value range of the physical

quantity is shown so that valid isovalues can be provided by users. You can enter as many

isovalues as you want. System memory is the only thing that limits the number of

isovalues. Stop entering isovalues by inputting a period ".". For example:

> Contour output of mcml data (h for help) => A Enter output filename with extension .iso (or . to quit): example.iso The range of the value is 0.156280 to 3294.800000. Input an isovalue or . to stop: 1000 Input an isovalue or . to stop: 100 Input an isovalue or . to stop: 10 Input an isovalue or . to stop: 1 Input an isovalue or . to stop: .

> Contour output of mcml data (h for help) =>

The output file of this example will have eight columns, each pair of columns describe one

contour line. The values of the contour lines are 1000, 100, 10, and 1 respectively.

10.10 Command "cc" of conv

After you input the filename of the mcml output and specify the incident photon

beam, you can output the convolved data with various formats. One of the formats for 2D

arrays is writing data in contour lines. Every contour line will be given by two columns.

This format can be obtained using the command "cc". The output file can be imported to

some plotting software such as KaleidaGraph, and the contour lines can be drawn. For

example:

> Main menu (h for help) => cc

> Contour output of convolved data (h for help) => h A = absorption vs r & z [J/cm3] F = fluence vs r & z [J/cm2]

106 Chapter 10 Instructions for conv

R = diffuse reflectance vs radius and angle [J/(cm2 sr)] T = transmittance vs radius and angle [J/(cm2 sr)] Q = Quit to main menu * input filename: example.mco

> Contour output of convolved data (h for help) =>

Since only the 2D arrays need to be presented in contour lines, there are only four

physical quantities. The command "A" outputs the absorption energy density as a function

of r and z whose dimension is J cm–3. The command "F" outputs the fluence as a function

of r and z in J cm–2. The commands "R" and "T" output diffuse reflectance and

transmittance as a function of r and α in J cm−2 sr–1 respectively.

After you input one of the commands, the program will prompt for the output

filename and the isovalues for the contour output. The value range of the physical

quantity is shown so that valid isovalues can be provided by users. You can enter as many

isovalues as you want. System memory is the only thing that limits the number of

isovalues. Stop entering isovalues by inputting a period ".". For example:

> Contour output of convolved data (h for help) => A Enter output filename with extension .iso (or . to quit): exampleAc.iso The range of the value is 0.048200 to 95.624939. Input an isovalue or . to stop: 80 Input an isovalue or . to stop: 8 Input an isovalue or . to stop: 0.8 Input an isovalue or . to stop: .

> Contour output of convolved data (h for help) =>

The output file of this example will have six columns, each pair of columns describe one

contour line. The values of the contour lines are 80, 8, and 0.8 respectively.

10.11 Command "so" of conv

After you input the filename of the mcml output, you can output the original data

of the mcml output with various formats. One of the formats for 2D arrays is writing data

in two columns, where the two columns give the physical quantity as a function of one of

two independent variables. The other variable is fixed at a certain value which can be

chosen by users. This format, we call scanning output, can be obtained using the

command "so". The output file can be imported to some plotting software such as

KaleidaGraph. For example:

> Main menu (h for help) => so

> Scans of mcml data (h for help) => h Ar = absorption vs r @ fixed z [1/cm3] Az = absorption vs z @ fixed r [1/cm3] Fr = fluence vs r @ fixed z [1/cm2]

Chapter 10 Instructions for conv 107

Fz = fluence vs z @ fixed r [1/cm2] Rr = diffuse reflectance vs r @ fixed angle [1/(cm2 sr)] Ra = diffuse reflectance vs angle @ fixed r [1/(cm2 sr)] Tr = transmittance vs r @ fixed angle [1/(cm2 sr)] Ta = transmittance vs angle @ fixed r [1/(cm2 sr)] Q = quit * input filename: example.mco

> Scans of mcml data (h for help) =>

The command "Ar" outputs the absorption probability density as a function of r for

a fixed z, whose dimension is cm–3. The command "Az" outputs the absorption

probability density as a function of z for a fixed r, whose dimension is cm–3. The

command "Fr" outputs the probability fluence as a function of r for a fixed z in cm–2. The

command "Fz" outputs the probability fluence as a function of z for a fixed r in cm–2. The

command "Rr" outputs the diffuse reflectance as a function of r for a fixed α in cm–2sr–1. The command "Ra" outputs the diffuse reflectance as a function of α for a fixed r in cm– 2sr–1. The commands "Tr" and "Ta" output the transmittance in the same format as for

the diffuse reflectance. The command "q" returns to the main menu.

After you input one of the commands, the program will prompt for the output

filename and the grid index to the value of the fixed variable. If you want to abort this

output, you can input a period "." as the filename. For example:

> Scans of mcml data (h for help) => Ar Enter output filename with extension .Ars (or . to quit): example.Ars z grid separation is 0.01 cm. Input fixed z index (0 - 39): 0

> Scans of mcml data (h for help) =>

This command outputs the absorption as a function of r for a fixed z. The

program shows that the z grid separation is 0.01 cm. The number of grid elements in the z

direction is 40. The grid index in the z direction is in the range from 0 to 39. The

command will generate two columns. The first column is r, and the second is the

absorption.

10.12 Command "sc" of conv

After you input the filename of the mcml output and specify the incident photon

beam, you can output the convolved data with various formats. One of the formats for 2D

arrays is writing data in two columns, where the two columns give the physical quantity as

a function of one of two independent variables. The other variable is fixed at a certain

value which can be chosen by users. This format, we call scanning output, can be

108 Chapter 10 Instructions for conv

obtained using the command "sc". Then, the output file can be imported to some plotting

software such as KaleidaGraph. For example:

> Main menu (h for help) => sc

> Scans of convolved data (h for help) => h Ar = absorption vs r @ fixed z [J/cm3] Az = absorption vs z @ fixed r [J/cm3] Fr = fluence vs r @ fixed z [J/cm2] Fz = fluence vs z @ fixed r [J/cm2] Rr = diffuse reflectance vs r @ fixed angle [J/(cm2 sr)] Ra = diffuse reflectance vs angle @ fixed r [J/(cm2 sr)] Tr = transmittance vs r @ fixed angle [J/(cm2 sr)] Ta = transmittance vs angle @ fixed r [J/(cm2 sr)] Q = quit * input filename: example.mco

> Scans of convolved data (h for help) =>

The command "Ar" outputs the absorption energy density as a function of r for a

fixed z, whose dimension is J cm–3. The command "Az" outputs the absorption energy

density as a function of z for a fixed r, whose dimension is J cm−3. The command "Fr"

outputs the fluence as a function of r for a fixed z in J cm–2. The command "Fz" outputs

the fluence as a function of z for a fixed r in J cm–2. The command "Rr" outputs the

diffuse reflectance as a function of r for a fixed α in J cm−2 sr−1. The command "Ra" outputs the diffuse reflectance as a function of α for a fixed r in J cm–2 sr–1. The commands "Tr" and "Ta" output the transmittance in the same format as for the diffuse

reflectance. The command "q" returns to the main menu.

After you input one of the commands, the program will prompt for the output

filename and the grid index to the value of the fixed variable. If you want to abort this

output, you can input a period "." as the filename. For example:

> Scans of convolved data (h for help) => Ar Enter output filename with extension .Arsc (or . to quit): example.Arsc z grid separation is 0.01 cm. Input fixed z index (0 - 39): 0

> Scans of convolved data (h for help) =>

This command outputs the absorption as a function of r for a fixed z. The

program shows that the z grid separation is 0.01 cm. The number of grids in the z

direction is 40. The grid index in the z direction is in the range from 0 to 39. The

command will generate two columns. The first column is r, and the second is the

absorption.

Chapter 10 Instructions for conv 109

10.13 Command "q" of conv

If you want to quit the program conv, use the command "q" in the main menu.

The program will ask you if you really mean to quit. You can answer yes or no. The

program will quit if the answer is "y". Otherwise, the program will return to the main

menu. For example:

> Main menu (h for help) => q Do you really want to quit conv (y/n): n

> Main menu (h for help) => q Do you really want to quit conv (y/n): y

10.14 Bugs of conv

The convolution results may have weird discontinuities if the allowed convolution

error is too high, and the convolution process may take too long if the convolution error is

too low. We do not have a good way to predict the best convolution error yet. The rule

of thumb is that you choose the lowest convolution error that does not make the

convolution too long to compute. If the convolution results still have any discontinuities

which should not be there, you need to decrease the convolution error and redo the

convolution.

As we discussed in Section 7.5, the radius of the incident beam has to be in the

right range to get reliable convolution integration due to the spatial resolution and the

range of grid system. As a rule of thumb, the radius should be in the range between about

3 times the grid separation in the r direction and the total grid coverage in the r direction

minus the maximum radius of observation (see Eqs. 7.23 & 7.24 in Section 7.4).

If the number of points in the r direction is chosen too large in the command "r",

the program can exit due to the lack of memory.

110 Chapter 11 How to Modify mcml

11. How to Modify mcml

The current version of program mcml simulates responses of an infinitely narrow

photon beam normally incident on multi-layered turbid media. We intend to make mcml

more general in the future. However, if you need to solve a different problem, such as

responses of isotropic photon sources instead of infinitely narrow photon beams,

responses of buried beams instead of externally incident beams, or time-resolved

simulations, then you will need to modify the program slightly. To do so, you need to

have prior knowledge of C language and understand Chapter 5. Appendix A and

Appendix B are provided to aid you in modifying the program. Appendix A gives you an

overall flow of the program, and Appendix B provides line-numbered source codes, which

can be used in combination with Appendix A for quick reference of the detail of the

program.

As an example, let us modify several places of the program mcml to compute the

responses of buried isotropic photon sources. First, we need to allow users to provide the

depth of the isotropic photon source inside the tissue, which can be entered in the input

data file. For example, we can add the depth of the source as the second parameter in the

line for the number of photon packets. Second, we define one more member called

source_z in the structure InputStruct to store the depth of the source. Third, we read the

depth into the member source_z of structure InputStruct in function ReadParm() which is

in the file "mcmlio.c". Fourth, we need to modify the function LaunchPhoton() in the file

"mcmlgo.c" such that the photons are initialized isotropically at the correct depth

according to source_z. If you know the program well, you will know that you can make

the source isotropic utilizing the function Spin() in the file "mcmlgo.c". To do so, we can

initialize the photon packet to be unidirectional (e.g., +z direction) temporarily, then

pretend that the whole photon packet suffers an isotropic scattering on the same spot as

initialized using the function Spin(). All you need to do is to provide an anisotropy factor

g equal 0 as the real parameter for the function. The propagation simulation functions

need no change, and the scoring procedures need no change either because the problem is

still cylindrically symmetric. After you modify the source code, you need to recompile and

link (refer to the manual with the compiler and linker).

After we modified mcml like this, we simulated the diffuse reflectances of a buried

isotropic photon source in two media separately, whose optical properties are equivalent

according to the similarity relations (Wyman et al., 1989a and 1989b). The optical

Chapter 11 How to Modify mcml 111

properties for the isotropic scattering medium (g = 0) are: absorption coefficient µ a = 0.1

cm−1, scattering coefficient µ s = 10 cm−1, anisotropy factor g = 0, relative refractive index

nrel = 1. The optical properties for the anisotropic scattering medium (g � 0) are: µ a =

0.1 cm−1, µ s = 100 cm−1, g = 0.9, nrel = 1. The depth of the isotropic photon source is 1

transport mean free path (mfp'), which is computed by 1 mfp' = 1/(µa + µ s(1–g)) = 1/(0.1

+ 10) ≈ 0.1 cm. The grid separations in the z and r directions are both 0.005 cm, and the numbers of grid elements in the z and r directions are both 200. One million photon

packets were used in the modified mcml.

0.01

0.1

1

10

0

R d ( cm -2 )

0.2 0.4 0.6 0.8 1 r (cm)

C: Isotropic Source, g=0

D: Isotropic Source, g=0.9

Fig. 11.1. Comparison of diffuse reflectances as a function of radius r for

two semi-infinite media whose optical properties are governed by the

similarity relations.The diffuse reflectances and the fluences for the two

media are compared in Figs. 11.1 and 11.2 respectively. The results of the diffuse

reflectances show that the similarity relations work well for photon sources deep inside the

tissue, and the results of the fluences show that the similarity relations work well when the

observation point is far away from the source. The fluence near the source for the

isotropic scattering medium is larger than that for the anisotropic scattering medium.

Similar to the discussion in Section 4.2, the fluences presented here are the responses of

an isotropic infinitely wide plane source with a difference of a constant factor which is the

power density of the source.

112 Chapter 11 How to Modify mcml

0

1

2

3

4

5

6

F lu

e n ce

[ -]

0 0.2 0.4 0.6 0.8 1 z [cm]

g = 0 g = 0.9

Responses to isotropic infinitely wide plane source

Depth of source

Fig. 11.2. Comparison between fluences as a function of z for two semi-

infinite media whose optical properties are governed by the similarity

relations (see Section 4.2 for discussion of an infinitely wide incident

beam).

Appendix A Cflow Output of the Program mcml 113

Appendices

Appendix A. Cflow Output of the Program mcml

We have listed the short format of the UNIX command cflow output in Section

5.5. To show all depth of the nesting levels, we list here the results of the UNIX

command:

cflow mcmlgo.c

1 main: char(), <mcmlmain.c 198> 2 ShowVersion: void*(), <mcmlio.c 49> 3 CenterStr: char*(), <mcmlio.c 28> 4 strlen: <> 5 strcpy: <> 6 strcat: <> 7 puts: <> 8 GetFnameFromArgv: void*(), <mcmlmain.c 150> 9 strcpy: 5 10 GetFile: struct*(), <mcmlio.c 94> 11 printf: <> 12 scanf: <> 13 strlen: 4 14 exit: <> 15 fopen: <> 16 CheckParm: void*(), <mcmlio.c 514> 17 ReadNumRuns: short(), <mcmlio.c 205> 18 FindDataLine: char*(), <mcmlio.c 184> 19 fgets: <> 20 printf: 11 21 CheckChar: char(), <mcmlio.c 139> 22 strlen: 4 23 nrerror: void*(), <mcmlnr.c 19> 24 fprintf: <> 25 exit: 14 26 KillChar: void*(), <mcmlio.c 123> 27 CommentLine: char(), <mcmlio.c 165> 28 strspn: <> 29 strcspn: <> 30 strcpy: 5 31 nrerror: 23 32 sscanf: <> 33 printf: 11 34 ReadParm: void*(), <mcmlio.c 425> 35 ReadFnameFormat: void*(), <mcmlio.c 225> 36 FindDataLine: 18 37 strcpy: 5 38 nrerror: 23 39 sscanf: 32 40 toupper: <> 41 ReadNumPhotons: void*(), <mcmlio.c 243> 42 FindDataLine: 18 43 strcpy: 5 44 nrerror: 23 45 sscanf: 32 46 ReadDzDr: void*(), <mcmlio.c 260> 47 FindDataLine: 18 48 strcpy: 5 49 nrerror: 23 50 sscanf: 32

114 Appendix A Cflow Output of the Program mcml

51 ReadNzNrNa: void*(), <mcmlio.c 276> 52 FindDataLine: 18 53 strcpy: 5 54 nrerror: 23 55 sscanf: 32 56 ReadNumLayers: void*(), <mcmlio.c 299> 57 FindDataLine: 18 58 strcpy: 5 59 nrerror: 23 60 sscanf: 32 61 ReadLayerSpecs: void*(), <mcmlio.c 375> 62 malloc: <> 63 nrerror: 23 64 ReadAmbient: void*(), <mcmlio.c 318> 65 FindDataLine: 18 66 strcpy: 5 67 sprintf: <> 68 nrerror: 23 69 sscanf: 32 70 ReadOneLayer: char(), <mcmlio.c 347> 71 FindDataLine: 18 72 strcpy: 5 73 sscanf: 32 74 sprintf: 67 75 CriticalAngle: void*(), <mcmlio.c 405> 76 sqrt: <> 77 FnameTaken: char(), <mcmlio.c 487> 78 NameInList: char(), <mcmlio.c 446> 79 strcmp: <> 80 AddNameToList: void*(), <mcmlio.c 459> 81 malloc: 62 82 strcpy: 5 83 sprintf: 67 84 free: <> 85 nrerror: 23 86 FreeFnameList: void*(), <mcmlio.c 500> 87 free: 84 88 rewind: <> 89 ReadNumRuns: 17 90 ReadParm: 34 91 DoOneRun: void*(), <mcmlmain.c 163> 92 InitOutputData: void*(), <mcmlio.c 546> 93 nrerror: 23 94 AllocMatrix: double**(), <mcmlnr.c 52> 95 malloc: 62 96 nrerror: 23 97 AllocVector: double*(), <mcmlnr.c 33> 98 malloc: 62 99 nrerror: 23 100 Rspecular: double(), <mcmlgo.c 117> 101 PunchTime: long(), <mcmlmain.c 60> 102 clock: <> 103 time: <> 104 sprintf: 67 105 puts: 7 106 strcpy: 5 107 difftime: <> 108 ReportStatus: void*(), <mcmlmain.c 122> 109 printf: 11 110 PredictDoneTime: void*(), <mcmlmain.c 95> 111 time: 103 112 localtime: <> 113 strftime: <> 114 printf: 11 115 PunchTime: 101 116 LaunchPhoton: void*(), <mcmlgo.c 143> 117 Rspecular: 100

Appendix A Cflow Output of the Program mcml 115

118 HopDropSpin: void*(), <mcmlgo.c 726> 119 HopInGlass: void*(), <mcmlgo.c 675> 120 StepSizeInGlass: void*(), <mcmlgo.c 265> 121 Hop: void*(), <mcmlgo.c 246> 122 CrossOrNot: void*(), <mcmlgo.c 660> 123 CrossUpOrNot: void*(), <mcmlgo.c 547> 124 RFresnel: double(), <mcmlgo.c 421> 125 sqrt: 76 126 RandomNum: double(), <mcmlgo.c 83> 127 time: 103 128 ran3: float(), <mcmlgo.c 32> 129 RecordR: void*(), <mcmlgo.c 480> 130 sqrt: 76 131 acos: <> 132 CrossDnOrNot: void*(), <mcmlgo.c 609> 133 RFresnel: 124 134 RandomNum: 126 135 RecordT: void*(), <mcmlgo.c 508> 136 sqrt: 76 137 acos: 131 138 HopDropSpinInTissue: void*(), <mcmlgo.c 706> 139 StepSizeInTissue: void*(), <mcmlgo.c 295> 140 RandomNum: 126 141 log: <> 142 HitBoundary: char(), <mcmlgo.c 323> 143 Hop: 121 144 CrossOrNot: 122 145 Drop: void*(), <mcmlgo.c 365> 146 sqrt: 76 147 Spin: void*(), <mcmlgo.c 204> 148 SpinTheta: double(), <mcmlgo.c 176> 149 RandomNum: 126 150 sqrt: 76 151 RandomNum: 126 152 cos: <> 153 fabs: <> 154 Roulette: void*(), <mcmlgo.c 395> 155 RandomNum: 126 156 ReportResult: void*(), <mcmlmain.c 133> 157 strcpy: 5 158 PunchTime: 101 159 SumScaleResult: void*(), <mcmlio.c 802> 160 Sum2DRd: void*(), <mcmlio.c 607> 161 Sum2DA: void*(), <mcmlio.c 652> 162 IzToLayer: short(), <mcmlio.c 637> 163 Sum2DTt: void*(), <mcmlio.c 677> 164 ScaleRdTt: void*(), <mcmlio.c 720> 165 sin: <> 166 ScaleA: void*(), <mcmlio.c 766> 167 WriteResult: void*(), <mcmlio.c 1091> 168 fopen: 15 169 nrerror: 23 170 toupper: 40 171 WriteVersion: void*(), <mcmlio.c 819> 172 fprintf: 24 173 fprintf: 24 174 WriteInParm: void*(), <mcmlio.c 833> 175 fprintf: 24 176 WriteRAT: void*(), <mcmlio.c 872> 177 fprintf: 24 178 WriteA_layer: void*(), <mcmlio.c 895> 179 fprintf: 24 180 WriteA_z: void*(), <mcmlio.c 1035> 181 fprintf: 24 182 WriteRd_r: void*(), <mcmlio.c 941> 183 fprintf: 24 184 WriteRd_a: void*(), <mcmlio.c 960>

116 Appendix A Cflow Output of the Program mcml

185 fprintf: 24 186 WriteTt_r: void*(), <mcmlio.c 1054> 187 fprintf: 24 188 WriteTt_a: void*(), <mcmlio.c 1073> 189 fprintf: 24 190 WriteA_rz: void*(), <mcmlio.c 1008> 191 fprintf: 24 192 WriteRd_ra: void*(), <mcmlio.c 914> 193 fprintf: 24 194 WriteTt_ra: void*(), <mcmlio.c 980> 195 fprintf: 24 196 fclose: <> 197 FreeData: void*(), <mcmlio.c 581> 198 free: 84 199 FreeMatrix: void*(), <mcmlnr.c 86> 200 free: 84 201 FreeVector: void*(), <mcmlnr.c 77> 202 free: 84 203 fclose: 196

Section B.1 mcml.h 117

Appendix B. Source Code of the Program mcml

The whole program is divided into several files. The file "mcml.h" is the header

file, which defines data structures and some constants. The file "mcmlmain.c" contains the

function main(). It also deals with the timings and status report. The file "mcmlio.c"

reads or writes data from or to data files. The file "mcmlgo.c" does most of the Monte

Carlo simulations. The file "mcmlnr.c" (nr stands for numerical recipes) contains several

functions for dynamical data allocations and error report.

B.1 mcml.h

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * Monte Carlo simulation of photon distribution in 6 * multi-layered turbid media in ANSI Standard C. 7 **** 8 * Starting Date: 10/1991. 9 * Current Date: 6/1992. 10 * 11 * Lihong Wang, Ph. D. 12 * Steven L. Jacques, Ph. D. 13 * Laser Biology Research Laboratory - 17 14 * M.D. Anderson Cancer Center 15 * University of Texas 16 * 1515 Holcombe Blvd. 17 * Houston, TX 77030 18 * USA 19 * 20 * This program was based on: 21 * (1) The Pascal code written by Marleen Keijzer and 22 * Steven L. Jacques in this laboratory in 1989, which 23 * deals with multi-layered turbid media. 24 * 25 * (2) Algorithm for semi-infinite turbid medium by 26 * S.A. Prahl, M. Keijzer, S.L. Jacques, A.J. Welch, 27 * SPIE Institute Series Vol. IS 5 (1989), and by 28 * A.N. Witt, The Astrophysical journal Supplement 29 * Series 35, 1-6 (1977). 30 * 31 * Major modifications include: 32 * . Conform to ANSI Standard C. 33 * . Removal of limit on number of array elements, 34 * because arrays in this program are dynamically 35 * allocated. This means that the program can accept 36 * any number of layers or gridlines as long as the 37 * memory permits. 38 * . Avoiding global variables whenever possible. This 39 * program has not used global variables so far. 40 * . Grouping variables logically using structures. 41 * . Top-down design, keep each subroutine clear & 42 * short. 43 * . Reflectance and transmittance are angularly 44 * resolved. 45 **** 46 * General Naming Conventions:

118 Appendix B Source Code of the Program mcml

47 * Preprocessor names: all capital letters, 48 * e.g. #define PREPROCESSORS 49 * Globals: first letter of each word is capital, no 50 * underscores, 51 * e.g. short GlobalVar; 52 * Dummy variables: first letter of each word is capital, 53 * and words are connected by underscores, 54 * e.g. void NiceFunction(char Dummy_Var); 55 * Local variables: all lower cases, words are connected 56 * by underscores, 57 * e.g. short local_var; 58 * Function names or data types: same as Globals. 59 * 60 **** 61 * Dimension of length: cm. 62 * 63 ****/ 64 65 #include <math.h> 66 #include <stdlib.h> 67 #include <stdio.h> 68 #include <stddef.h> 69 #include <time.h> 70 #include <string.h> 71 #include <ctype.h> 72 73 #define PI 3.1415926 74 #define WEIGHT 1E-4 /* Critical weight for roulette. */ 75 #define CHANCE 0.1 /* Chance of roulette survival. */ 76 #define STRLEN 256 /* String length. */ 77 78 #define Boolean char 79 80 #define SIGN(x) ((x)>=0 ? 1:-1) 81 82 /****************** Stuctures *****************************/ 83 84 /**** 85 * Structure used to describe a photon packet. 86 ****/ 87 typedef struct { 88 double x, y ,z; /* Cartesian coordinates.[cm] */ 89 double ux, uy, uz;/* directional cosines of a photon. */ 90 double w; /* weight. */ 91 Boolean dead; /* 1 if photon is terminated. */ 92 short layer; /* index to layer where the photon */ 93 /* packet resides. */ 94 double s; /* current step size. [cm]. */ 95 double sleft; /* step size left. dimensionless [-]. */ 96 } PhotonStruct; 97 98 /**** 99 * Structure used to describe the geometry and optical 100 * properties of a layer. 101 * z0 and z1 are the z coordinates for the upper boundary 102 * and lower boundary respectively. 103 * 104 * cos_crit0 and cos_crit1 are the cosines of the 105 * critical angle of total internal reflection for the 106 * upper boundary and lower boundary respectively. 107 * They are set to zero if no total internal reflection 108 * exists. 109 * They are used for computation speed. 110 ****/ 111 typedef struct { 112 double z0, z1; /* z coordinates of a layer. [cm] */ 113 double n; /* refractive index of a layer. */

Section B.1 mcml.h 119

114 double mua; /* absorption coefficient. [1/cm] */ 115 double mus; /* scattering coefficient. [1/cm] */ 116 double g; /* anisotropy. */ 117 118 double cos_crit0, cos_crit1; 119 } LayerStruct; 120 121 /**** 122 * Input parameters for each independent run. 123 * 124 * z and r are for the cylindrical coordinate system. [cm] 125 * a is for the angle alpha between the photon exiting 126 * direction and the surface normal. [radian] 127 * 128 * The grid line separations in z, r, and alpha 129 * directions are dz, dr, and da respectively. The numbers 130 * of grid lines in z, r, and alpha directions are 131 * nz, nr, and na respectively. 132 * 133 * The member layerspecs will point to an array of 134 * structures which store parameters of each layer. 135 * This array has (number_layers + 2) elements. One 136 * element is for a layer. 137 * The layers 0 and (num_layers + 1) are for top ambient 138 * medium and the bottom ambient medium respectively. 139 ****/ 140 typedef struct { 141 char out_fname[STRLEN]; /* output file name. */ 142 char out_fformat; /* output file format. */ 143 /* 'A' for ASCII, */ 144 /* 'B' for binary. */ 145 long num_photons; /* to be traced. */ 146 double Wth; /* play roulette if photon */ 147 /* weight < Wth.*/ 148 149 double dz; /* z grid separation.[cm] */ 150 double dr; /* r grid separation.[cm] */ 151 double da; /* alpha grid separation. */ 152 /* [radian] */ 153 short nz; /* array range 0..nz-1. */ 154 short nr; /* array range 0..nr-1. */ 155 short na; /* array range 0..na-1. */ 156 157 short num_layers; /* number of layers. */ 158 LayerStruct * layerspecs; /* layer parameters. */ 159 } InputStruct; 160 161 /**** 162 * Structures for scoring physical quantities. 163 * z and r represent z and r coordinates of the 164 * cylindrical coordinate system. [cm] 165 * a is the angle alpha between the photon exiting 166 * direction and the normal to the surfaces. [radian] 167 * See comments of the InputStruct. 168 * See manual for the physcial quantities. 169 ****/ 170 typedef struct { 171 double Rsp; /* specular reflectance. [-] */ 172 double ** Rd_ra; /* 2D distribution of diffuse */ 173 /* reflectance. [1/(cm2 sr)] */ 174 double * Rd_r; /* 1D radial distribution of diffuse */ 175 /* reflectance. [1/cm2] */ 176 double * Rd_a; /* 1D angular distribution of diffuse */ 177 /* reflectance. [1/sr] */ 178 double Rd; /* total diffuse reflectance. [-] */ 179 180 double ** A_rz; /* 2D probability density in turbid */

120 Appendix B Source Code of the Program mcml

181 /* media over r & z. [1/cm3] */ 182 double * A_z; /* 1D probability density over z. */ 183 /* [1/cm] */ 184 double * A_l; /* each layer's absorption */ 185 /* probability. [-] */ 186 double A; /* total absorption probability. [-] */ 187 188 double ** Tt_ra; /* 2D distribution of total */ 189 /* transmittance. [1/(cm2 sr)] */ 190 double * Tt_r; /* 1D radial distribution of */ 191 /* transmittance. [1/cm2] */ 192 double * Tt_a; /* 1D angular distribution of */ 193 /* transmittance. [1/sr] */ 194 double Tt; /* total transmittance. [-] */ 195 } OutStruct; 196 197 /*********************************************************** 198 * Routine prototypes for dynamic memory allocation and 199 * release of arrays and matrices. 200 * Modified from Numerical Recipes in C. 201 ****/ 202 double *AllocVector(short, short); 203 double **AllocMatrix(short, short,short, short); 204 void FreeVector(double *, short, short); 205 void FreeMatrix(double **, short, short, short, short); 206 void nrerror(char *);

Section B.2 mcmlmain.c 121

B.2 mcmlmain.c

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * main program for Monte Carlo simulation of photon 6 * distribution in multi-layered turbid media. 7 * 8 ****/ 9 10 /**** 11 * THINKCPROFILER is defined to generate profiler calls in 12 * Think C. If 1, remember to turn on "Generate profiler 13 * calls" in the options menu. 14 ****/ 15 #define THINKCPROFILER 0 16 17 /* GNU cc does not support difftime() and CLOCKS_PER_SEC.*/ 18 #define GNUCC 0 19 20 #if THINKCPROFILER 21 #include <profile.h> 22 #include <console.h> 23 #endif 24 25 #include "mcml.h" 26 27 /* Declare before they are used in main(). */ 28 FILE *GetFile(char *); 29 short ReadNumRuns(FILE* ); 30 void ReadParm(FILE* , InputStruct * ); 31 void CheckParm(FILE* , InputStruct * ); 32 void InitOutputData(InputStruct, OutStruct *); 33 void FreeData(InputStruct, OutStruct *); 34 double Rspecular(LayerStruct * ); 35 void LaunchPhoton(double, LayerStruct *, PhotonStruct *); 36 void HopDropSpin(InputStruct *,PhotonStruct *,OutStruct *); 37 void SumScaleResult(InputStruct, OutStruct *); 38 void WriteResult(InputStruct, OutStruct, char *); 39 40 41 /*********************************************************** 42 * If F = 0, reset the clock and return 0. 43 * 44 * If F = 1, pass the user time to Msg and print Msg on 45 * screen, return the real time since F=0. 46 * 47 * If F = 2, same as F=1 except no printing. 48 * 49 * Note that clock() and time() return user time and real 50 * time respectively. 51 * User time is whatever the system allocates to the 52 * running of the program; 53 * real time is wall-clock time. In a time-shared system, 54 * they need not be the same. 55 * 56 * clock() only hold 16 bit integer, which is about 32768 57 * clock ticks. 58 ****/ 59 time_t PunchTime(char F, char *Msg) 60 { 61 #if GNUCC 62 return(0);

122 Appendix B Source Code of the Program mcml

63 #else 64 static clock_t ut0; /* user time reference. */ 65 static time_t rt0; /* real time reference. */ 66 double secs; 67 char s[STRLEN]; 68 69 if(F==0) { 70 ut0 = clock(); 71 rt0 = time(NULL); 72 return(0); 73 } 74 else if(F==1) { 75 secs = (clock() - ut0)/(double)CLOCKS_PER_SEC; 76 if (secs<0) secs=0; /* clock() can overflow. */ 77 sprintf(s, "User time: %8.0lf sec = %8.2lf hr. %s\n", 78 secs, secs/3600.0, Msg); 79 puts(s); 80 strcpy(Msg, s); 81 return(difftime(time(NULL), rt0)); 82 } 83 else if(F==2) return(difftime(time(NULL), rt0)); 84 else return(0); 85 #endif 86 } 87 88 /*********************************************************** 89 * Print the current time and the estimated finishing time. 90 * 91 * P1 is the number of computed photon packets. 92 * Pt is the total number of photon packets. 93 ****/ 94 void PredictDoneTime(long P1, long Pt) 95 { 96 time_t now, done_time; 97 struct tm *date; 98 char s[80]; 99 100 now = time(NULL); 101 date = localtime(&now); 102 strftime(s, 80, "%H:%M %x", date); 103 printf("Now %s, ", s); 104 105 done_time = now + 106 (time_t) (PunchTime(2,"")*(Pt-P1)/(double)P1); 107 date = localtime(&done_time); 108 strftime(s, 80, "%H:%M %x", date); 109 printf("End %s\n", s); 110 } 111 112 /*********************************************************** 113 * Report estimated time, number of photons and runs left 114 * after calculating 10 photons or every 1/10 of total 115 * number of photons. 116 * 117 * Num_Runs is the number of runs left. 118 * Pi is the index to the current photon, counting down. 119 * Pt is the total number of photons. 120 ****/ 121 void ReportStatus(short Num_Runs,long Pi,long Pt) 122 { 123 if(Pt-Pi == 10 || Pi*10%Pt == 0 && Pi != Pt) { 124 printf("%ld photons & %hd runs left, ", Pi, Num_Runs); 125 PredictDoneTime(Pt-Pi, Pt); 126 } 127 } 128 129 /***********************************************************

Section B.2 mcmlmain.c 123

130 * Report time and write results. 131 ****/ 132 void ReportResult(InputStruct In_Parm, OutStruct Out_Parm) 133 { 134 char time_report[STRLEN]; 135 136 strcpy(time_report, " Simulation time of this run."); 137 PunchTime(1, time_report); 138 139 SumScaleResult(In_Parm, &Out_Parm); 140 WriteResult(In_Parm, Out_Parm, time_report); 141 } 142 143 /*********************************************************** 144 * Get the file name of the input data file from the 145 * argument to the command line. 146 ****/ 147 void GetFnameFromArgv(int argc, 148 char * argv[], 149 char * input_filename) 150 { 151 if(argc>=2) { /* filename in command line */ 152 strcpy(input_filename, argv[1]); 153 } 154 else 155 input_filename[0] = '\0'; 156 } 157 158 159 /*********************************************************** 160 * Execute Monte Carlo simulation for one independent run. 161 ****/ 162 void DoOneRun(short NumRuns, InputStruct *In_Ptr) 163 { 164 register long i_photon; 165 /* index to photon. register for speed.*/ 166 OutStruct out_parm; /* distribution of photons.*/ 167 PhotonStruct photon; 168 169 #if THINKCPROFILER 170 InitProfile(200,200); cecho2file("prof.rpt",0, stdout); 171 #endif 172 173 InitOutputData(*In_Ptr, &out_parm); 174 out_parm.Rsp = Rspecular(In_Ptr->layerspecs); 175 i_photon = In_Ptr->num_photons; 176 PunchTime(0, ""); 177 178 do { 179 ReportStatus(NumRuns, i_photon, In_Ptr->num_photons); 180 LaunchPhoton(out_parm.Rsp, In_Ptr->layerspecs, &photon); 181 do HopDropSpin(In_Ptr, &photon, &out_parm); 182 while (!photon.dead); 183 } while(--i_photon); 184 185 #if THINKCPROFILER 186 exit(0); 187 #endif 188 189 ReportResult(*In_Ptr, out_parm); 190 FreeData(*In_Ptr, &out_parm); 191 } 192 193 /*********************************************************** 194 * The argument to the command line is filename, if any. 195 * Macintosh does not support command line. 196 ****/

124 Appendix B Source Code of the Program mcml

197 char main(int argc, char *argv[]) 198 { 199 char input_filename[STRLEN]; 200 FILE *input_file_ptr; 201 short num_runs; /* number of independent runs. */ 202 InputStruct in_parm; 203 204 ShowVersion("Version 1.1, 1992"); 205 GetFnameFromArgv(argc, argv, input_filename); 206 input_file_ptr = GetFile(input_filename); 207 CheckParm(input_file_ptr, &in_parm); 208 num_runs = ReadNumRuns(input_file_ptr); 209 210 while(num_runs--) { 211 ReadParm(input_file_ptr, &in_parm); 212 DoOneRun(num_runs, &in_parm); 213 } 214 215 fclose(input_file_ptr); 216 return(0); 217 }

Section B.3 mcmlio.c 125

B.3 mcmlio.c

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * Input/output of data. 6 ****/ 7 8 #include "mcml.h" 9 10 /*********************************************************** 11 * Structure used to check against duplicated file names. 12 ****/ 13 struct NameList { 14 char name[STRLEN]; 15 struct NameList * next; 16 }; 17 18 typedef struct NameList NameNode; 19 typedef NameNode * NameLink; 20 21 22 /*********************************************************** 23 * Center a string according to the column width. 24 ****/ 25 char * CenterStr(short Wid, 26 char * InStr, 27 char * OutStr) 28 { 29 size_t nspaces; /* number of spaces to be filled */ 30 /* before InStr. */ 31 32 nspaces = (Wid - strlen(InStr))/2; 33 if(nspaces<0) nspaces = 0; 34 35 strcpy(OutStr, ""); 36 while(nspaces--) strcat(OutStr, " "); 37 38 strcat(OutStr, InStr); 39 40 return(OutStr); 41 } 42 43 /*********************************************************** 44 * Print some messages before starting simulation. 45 * e.g. author, address, program version, year. 46 ****/ 47 #define COLWIDTH 80 48 void ShowVersion(char *version) 49 { 50 char str[STRLEN]; 51 52 CenterStr(COLWIDTH, 53 "mcml - Monte Carlo Simulation of Multi-layered Turbid Media", 54 str); 55 puts(str); 56 puts(""); 57 58 CenterStr(COLWIDTH, "Lihong Wang, Ph. D.", str); 59 puts(str); 60 61 CenterStr(COLWIDTH, "Steven L. Jacques, Ph. D.", str); 62 puts(str);

126 Appendix B Source Code of the Program mcml

63 64 CenterStr(COLWIDTH, 65 "Laser Biology Research Laboratory - Box 17",str); 66 puts(str); 67 68 CenterStr(COLWIDTH, "M.D. Anderson Cancer Center", str); 69 puts(str); 70 71 CenterStr(COLWIDTH, "University of Texas", str); 72 puts(str); 73 74 CenterStr(COLWIDTH, "Houston, TX 77030", str); 75 puts(str); 76 77 CenterStr(COLWIDTH, "Fax: (713)792-3995", str); 78 puts(str); 79 puts(""); 80 81 CenterStr(COLWIDTH, version, str); 82 puts(str); 83 puts("\n\n\n\n"); 84 } 85 #undef COLWIDTH 86 87 /*********************************************************** 88 * Get a filename and open it for reading, retry until 89 * the file can be opened. '.' terminates the program. 90 * 91 * If Fname != NULL, try Fname first. 92 ****/ 93 FILE *GetFile(char *Fname) 94 { 95 FILE * file=NULL; 96 Boolean firsttime=1; 97 98 do { 99 if(firsttime && Fname[0]!='\0') { 100 /* use the filename from command line */ 101 firsttime = 0; 102 } 103 else { 104 printf("Input filename(or . to exit):"); 105 scanf("%s", Fname); 106 firsttime = 0; 107 } 108 109 if(strlen(Fname) == 1 && Fname[0] == '.') 110 exit(1); /* exit if no filename entered. */ 111 112 file = fopen(Fname, "r"); 113 } while(file == NULL); 114 115 return(file); 116 } 117 118 /*********************************************************** 119 * Kill the ith char (counting from 0), push the following 120 * chars forward by one. 121 ****/ 122 void KillChar(size_t i, char * Str) 123 { 124 size_t sl = strlen(Str); 125 126 for(;i<sl;i++) Str[i] = Str[i+1]; 127 } 128 129 /***********************************************************

Section B.3 mcmlio.c 127

130 * Eliminate the chars in a string which are not printing 131 * chars or spaces. 132 * 133 * Spaces include ' ', '\f', '\t' etc. 134 * 135 * Return 1 if no nonprinting chars found, otherwise 136 * return 0. 137 ****/ 138 Boolean CheckChar(char * Str) 139 { 140 Boolean found = 0; /* found bad char. */ 141 size_t sl = strlen(Str); 142 size_t i=0; 143 144 while(i<sl) 145 if (Str[i]<0 || Str[i]>255) 146 nrerror("Non-ASCII file\n"); 147 else if(isprint(Str[i]) || isspace(Str[i])) 148 i++; 149 else { 150 found = 1; 151 KillChar(i, Str); 152 sl--; 153 } 154 155 return(found); 156 } 157 158 /*********************************************************** 159 * Return 1 if this line is a comment line in which the 160 * first non-space character is "#". 161 * 162 * Also return 1 if this line is space line. 163 ****/ 164 Boolean CommentLine(char *Buf) 165 { 166 size_t spn, cspn; 167 168 spn = strspn(Buf, " \t"); 169 /* length spanned by space or tab chars. */ 170 171 cspn = strcspn(Buf, "#\n"); 172 /* length before the 1st # or return. */ 173 174 if(spn == cspn) /* comment line or space line. */ 175 return(1); 176 else /* the line has data. */ 177 return(0); 178 } 179 180 /*********************************************************** 181 * Skip space or comment lines and return a data line only. 182 ****/ 183 char * FindDataLine(FILE *File_Ptr) 184 { 185 char buf[STRLEN]; 186 187 buf[0] = '\0'; 188 do { /* skip space or comment lines. */ 189 if(fgets(buf, 255, File_Ptr) == NULL) { 190 printf("Incomplete data\n"); 191 buf[0]='\0'; 192 break; 193 } 194 else 195 CheckChar(buf); 196 } while(CommentLine(buf));

128 Appendix B Source Code of the Program mcml

197 198 return(buf); 199 } 200 201 /*********************************************************** 202 * Skip file version, then read number of runs. 203 ****/ 204 short ReadNumRuns(FILE* File_Ptr) 205 { 206 char buf[STRLEN]; 207 short n=0; 208 209 FindDataLine(File_Ptr); /* skip file version. */ 210 211 strcpy(buf, FindDataLine(File_Ptr)); 212 if(buf[0]=='\0') nrerror("Reading number of runs\n"); 213 sscanf(buf, "%hd",&n); 214 return(n); 215 } 216 217 218 /*********************************************************** 219 * Read the file name and the file format. 220 * 221 * The file format can be either A for ASCII or B for 222 * binary. 223 ****/ 224 void ReadFnameFormat(FILE *File_Ptr, InputStruct *In_Ptr) 225 { 226 char buf[STRLEN]; 227 228 /** read in file name and format. **/ 229 strcpy(buf, FindDataLine(File_Ptr)); 230 if(buf[0]=='\0') 231 nrerror("Reading file name and format.\n"); 232 sscanf(buf, "%s %c", 233 In_Ptr->out_fname, &(In_Ptr->out_fformat) ); 234 if(toupper(In_Ptr->out_fformat) != 'B') 235 In_Ptr->out_fformat = 'A'; 236 } 237 238 239 /*********************************************************** 240 * Read the number of photons. 241 ****/ 242 void ReadNumPhotons(FILE *File_Ptr, InputStruct *In_Ptr) 243 { 244 char buf[STRLEN]; 245 246 /** read in number of photons. **/ 247 strcpy(buf, FindDataLine(File_Ptr)); 248 if(buf[0]=='\0') 249 nrerror("Reading number of photons.\n"); 250 sscanf(buf, "%ld", &In_Ptr->num_photons); 251 if(In_Ptr->num_photons<=0) 252 nrerror("Nonpositive number of photons.\n"); 253 } 254 255 256 /*********************************************************** 257 * Read the members dz and dr. 258 ****/ 259 void ReadDzDr(FILE *File_Ptr, InputStruct *In_Ptr) 260 { 261 char buf[STRLEN]; 262 263 /** read in dz, dr. **/

Section B.3 mcmlio.c 129

264 strcpy(buf, FindDataLine(File_Ptr)); 265 if(buf[0]=='\0') nrerror("Reading dz, dr.\n"); 266 sscanf(buf, "%lf%lf", &In_Ptr->dz, &In_Ptr->dr); 267 if(In_Ptr->dz<=0) nrerror("Nonpositive dz.\n"); 268 if(In_Ptr->dr<=0) nrerror("Nonpositive dr.\n"); 269 } 270 271 272 /*********************************************************** 273 * Read the members nz, nr, na. 274 ****/ 275 void ReadNzNrNa(FILE *File_Ptr, InputStruct *In_Ptr) 276 { 277 char buf[STRLEN]; 278 279 /** read in number of dz, dr, da. **/ 280 strcpy(buf, FindDataLine(File_Ptr)); 281 if(buf[0]=='\0') 282 nrerror("Reading number of dz, dr, da's.\n"); 283 sscanf(buf, "%hd%hd%hd", 284 &In_Ptr->nz, &In_Ptr->nr, &In_Ptr->na); 285 if(In_Ptr->nz<=0) 286 nrerror("Nonpositive number of dz's.\n"); 287 if(In_Ptr->nr<=0) 288 nrerror("Nonpositive number of dr's.\n"); 289 if(In_Ptr->na<=0) 290 nrerror("Nonpositive number of da's.\n"); 291 In_Ptr->da = 0.5*PI/In_Ptr->na; 292 } 293 294 295 /*********************************************************** 296 * Read the number of layers. 297 ****/ 298 void ReadNumLayers(FILE *File_Ptr, InputStruct *In_Ptr) 299 { 300 char buf[STRLEN]; 301 302 /** read in number of layers. **/ 303 strcpy(buf, FindDataLine(File_Ptr)); 304 if(buf[0]=='\0') 305 nrerror("Reading number of layers.\n"); 306 sscanf(buf, "%hd", &In_Ptr->num_layers); 307 if(In_Ptr->num_layers<=0) 308 nrerror("Nonpositive number of layers.\n"); 309 } 310 311 312 /*********************************************************** 313 * Read the refractive index n of the ambient. 314 ****/ 315 void ReadAmbient(FILE *File_Ptr, 316 LayerStruct * Layer_Ptr, 317 char *side) 318 { 319 char buf[STRLEN], msg[STRLEN]; 320 double n; 321 322 strcpy(buf, FindDataLine(File_Ptr)); 323 if(buf[0]=='\0') { 324 sprintf(msg, "Rading n of %s ambient.\n", side); 325 nrerror(msg); 326 } 327 328 sscanf(buf, "%lf", &n ); 329 if(n<=0) nrerror("Wrong n.\n"); 330 Layer_Ptr->n = n;

130 Appendix B Source Code of the Program mcml

331 } 332 333 334 /*********************************************************** 335 * Read the parameters of one layer. 336 * 337 * Return 1 if error detected. 338 * Return 0 otherwise. 339 * 340 * *Z_Ptr is the z coordinate of the current layer, which 341 * is used to convert thickness of layer to z coordinates 342 * of the two boundaries of the layer. 343 ****/ 344 Boolean ReadOneLayer(FILE *File_Ptr, 345 LayerStruct * Layer_Ptr, 346 double *Z_Ptr) 347 { 348 char buf[STRLEN], msg[STRLEN]; 349 double d, n, mua, mus, g; /* d is thickness. */ 350 351 strcpy(buf, FindDataLine(File_Ptr)); 352 if(buf[0]=='\0') return(1); /* error. */ 353 354 sscanf(buf, "%lf%lf%lf%lf%lf", &n, &mua, &mus, &g, &d); 355 if(d<0 || n<=0 || mua<0 || mus<0 || g<0 || g>1) 356 return(1); /* error. */ 357 358 Layer_Ptr->n = n; 359 Layer_Ptr->mua = mua; 360 Layer_Ptr->mus = mus; 361 Layer_Ptr->g = g; 362 Layer_Ptr->z0 = *Z_Ptr; 363 *Z_Ptr += d; 364 Layer_Ptr->z1 = *Z_Ptr; 365 366 return(0); 367 } 368 369 /*********************************************************** 370 * Read the parameters of one layer at a time. 371 ****/ 372 void ReadLayerSpecs(FILE *File_Ptr, 373 short Num_Layers, 374 LayerStruct ** Layerspecs_PP) 375 { 376 char msg[STRLEN]; 377 short i=0; 378 double z = 0.0; /* z coordinate of the current layer. */ 379 380 /* Allocate an array for the layer parameters. */ 381 /* layer 0 and layer Num_Layers + 1 are for ambient. */ 382 *Layerspecs_PP = (LayerStruct *) 383 malloc((unsigned) (Num_Layers+2)*sizeof(LayerStruct)); 384 if (!(*Layerspecs_PP)) 385 nrerror("allocation failure in ReadLayerSpecs()"); 386 387 ReadAmbient(File_Ptr, &((*Layerspecs_PP)[i]), "top"); 388 for(i=1; i<=Num_Layers; i++) 389 if(ReadOneLayer(File_Ptr, &((*Layerspecs_PP)[i]), &z)) { 390 sprintf(msg, "Error reading %hd of %hd layers\n", 391 i, Num_Layers); 392 nrerror(msg); 393 } 394 ReadAmbient(File_Ptr, &((*Layerspecs_PP)[i]), "bottom"); 395 } 396 397 /***********************************************************

Section B.3 mcmlio.c 131

398 * Compute the critical angles for total internal 399 * reflection according to the relative refractive index 400 * of the layer. 401 * All layers are processed. 402 ****/ 403 void CriticalAngle( short Num_Layers, 404 LayerStruct ** Layerspecs_PP) 405 { 406 short i=0; 407 double n1, n2; 408 409 for(i=1; i<=Num_Layers; i++) { 410 n1 = (*Layerspecs_PP)[i].n; 411 n2 = (*Layerspecs_PP)[i-1].n; 412 (*Layerspecs_PP)[i].cos_crit0 = n1>n2 ? 413 sqrt(1.0 - n2*n2/(n1*n1)) : 0.0; 414 415 n2 = (*Layerspecs_PP)[i+1].n; 416 (*Layerspecs_PP)[i].cos_crit1 = n1>n2 ? 417 sqrt(1.0 - n2*n2/(n1*n1)) : 0.0; 418 } 419 } 420 421 /*********************************************************** 422 * Read in the input parameters for one run. 423 ****/ 424 void ReadParm(FILE* File_Ptr, InputStruct * In_Ptr) 425 { 426 char buf[STRLEN]; 427 428 In_Ptr->Wth = WEIGHT; 429 430 ReadFnameFormat(File_Ptr, In_Ptr); 431 ReadNumPhotons(File_Ptr, In_Ptr); 432 ReadDzDr(File_Ptr, In_Ptr); 433 ReadNzNrNa(File_Ptr, In_Ptr); 434 ReadNumLayers(File_Ptr, In_Ptr); 435 436 ReadLayerSpecs(File_Ptr, In_Ptr->num_layers, 437 &In_Ptr->layerspecs); 438 CriticalAngle(In_Ptr->num_layers, &In_Ptr->layerspecs); 439 } 440 441 /*********************************************************** 442 * Return 1, if the name in the name list. 443 * Return 0, otherwise. 444 ****/ 445 Boolean NameInList(char *Name, NameLink List) 446 { 447 while (List != NULL) { 448 if(strcmp(Name, List->name) == 0) 449 return(1); 450 List = List->next; 451 }; 452 return(0); 453 } 454 455 /*********************************************************** 456 * Add the name to the name list. 457 ****/ 458 void AddNameToList(char *Name, NameLink * List_Ptr) 459 { 460 NameLink list = *List_Ptr; 461 462 if(list == NULL) { /* first node. */ 463 *List_Ptr = list = (NameLink)malloc(sizeof(NameNode)); 464 strcpy(list->name, Name);

132 Appendix B Source Code of the Program mcml

465 list->next = NULL; 466 } 467 else { /* subsequent nodes. */ 468 /* Move to the last node. */ 469 while(list->next != NULL) 470 list = list->next; 471 472 /* Append a node to the list. */ 473 list->next = (NameLink)malloc(sizeof(NameNode)); 474 list = list->next; 475 strcpy(list->name, Name); 476 list->next = NULL; 477 } 478 } 479 480 /*********************************************************** 481 * Check against duplicated file names. 482 * 483 * A linked list is set up to store the file names used 484 * in this input data file. 485 ****/ 486 Boolean FnameTaken(char *fname, NameLink * List_Ptr) 487 { 488 if(NameInList(fname, *List_Ptr)) 489 return(1); 490 else { 491 AddNameToList(fname, List_Ptr); 492 return(0); 493 } 494 } 495 496 /*********************************************************** 497 * Free each node in the file name list. 498 ****/ 499 void FreeFnameList(NameLink List) 500 { 501 NameLink next; 502 503 while(List != NULL) { 504 next = List->next; 505 free(List); 506 List = next; 507 } 508 } 509 510 /*********************************************************** 511 * Check the input parameters for each run. 512 ****/ 513 void CheckParm(FILE* File_Ptr, InputStruct * In_Ptr) 514 { 515 short i_run; 516 short num_runs; /* number of independent runs. */ 517 NameLink head = NULL; 518 Boolean name_taken;/* output files share the same */ 519 /* file name.*/ 520 char msg[STRLEN]; 521 522 num_runs = ReadNumRuns(File_Ptr); 523 for(i_run=1; i_run<=num_runs; i_run++) { 524 printf("Checking input data for run %hd\n", i_run); 525 ReadParm(File_Ptr, In_Ptr); 526 527 name_taken = FnameTaken(In_Ptr->out_fname, &head); 528 if(name_taken) 529 sprintf(msg, "file name %s duplicated.\n", 530 In_Ptr->out_fname); 531

Section B.3 mcmlio.c 133

532 free(In_Ptr->layerspecs); 533 if(name_taken) nrerror(msg); 534 } 535 FreeFnameList(head); 536 rewind(File_Ptr); 537 } 538 539 540 /*********************************************************** 541 * Allocate the arrays in OutStruct for one run, and 542 * array elements are automatically initialized to zeros. 543 ****/ 544 void InitOutputData(InputStruct In_Parm, 545 OutStruct * Out_Ptr) 546 { 547 short nz = In_Parm.nz; 548 short nr = In_Parm.nr; 549 short na = In_Parm.na; 550 short nl = In_Parm.num_layers; 551 /* remember to use nl+2 because of 2 for ambient. */ 552 553 if(nz<=0 || nr<=0 || na<=0 || nl<=0) 554 nrerror("Wrong grid parameters.\n"); 555 556 /* Init pure numbers. */ 557 Out_Ptr->Rsp = 0.0; 558 Out_Ptr->Rd = 0.0; 559 Out_Ptr->A = 0.0; 560 Out_Ptr->Tt = 0.0; 561 562 /* Allocate the arrays and the matrices. */ 563 Out_Ptr->Rd_ra = AllocMatrix(0,nr-1,0,na-1); 564 Out_Ptr->Rd_r = AllocVector(0,nr-1); 565 Out_Ptr->Rd_a = AllocVector(0,na-1); 566 567 Out_Ptr->A_rz = AllocMatrix(0,nr-1,0,nz-1); 568 Out_Ptr->A_z = AllocVector(0,nz-1); 569 Out_Ptr->A_l = AllocVector(0,nl+1); 570 571 Out_Ptr->Tt_ra = AllocMatrix(0,nr-1,0,na-1); 572 Out_Ptr->Tt_r = AllocVector(0,nr-1); 573 Out_Ptr->Tt_a = AllocVector(0,na-1); 574 } 575 576 /*********************************************************** 577 * Undo what InitOutputData did. 578 * i.e. free the data allocations. 579 ****/ 580 void FreeData(InputStruct In_Parm, OutStruct * Out_Ptr) 581 { 582 short nz = In_Parm.nz; 583 short nr = In_Parm.nr; 584 short na = In_Parm.na; 585 short nl = In_Parm.num_layers; 586 /* remember to use nl+2 because of 2 for ambient. */ 587 588 free(In_Parm.layerspecs); 589 590 FreeMatrix(Out_Ptr->Rd_ra, 0,nr-1,0,na-1); 591 FreeVector(Out_Ptr->Rd_r, 0,nr-1); 592 FreeVector(Out_Ptr->Rd_a, 0,na-1); 593 594 FreeMatrix(Out_Ptr->A_rz, 0, nr-1, 0,nz-1); 595 FreeVector(Out_Ptr->A_z, 0, nz-1); 596 FreeVector(Out_Ptr->A_l, 0,nl+1); 597 598 FreeMatrix(Out_Ptr->Tt_ra, 0,nr-1,0,na-1);

134 Appendix B Source Code of the Program mcml

599 FreeVector(Out_Ptr->Tt_r, 0,nr-1); 600 FreeVector(Out_Ptr->Tt_a, 0,na-1); 601 } 602 603 /*********************************************************** 604 * Get 1D array elements by summing the 2D array elements. 605 ****/ 606 void Sum2DRd(InputStruct In_Parm, OutStruct * Out_Ptr) 607 { 608 short nr = In_Parm.nr; 609 short na = In_Parm.na; 610 short ir,ia; 611 double sum; 612 613 for(ir=0; ir<nr; ir++) { 614 sum = 0.0; 615 for(ia=0; ia<na; ia++) sum += Out_Ptr->Rd_ra[ir][ia]; 616 Out_Ptr->Rd_r[ir] = sum; 617 } 618 619 for(ia=0; ia<na; ia++) { 620 sum = 0.0; 621 for(ir=0; ir<nr; ir++) sum += Out_Ptr->Rd_ra[ir][ia]; 622 Out_Ptr->Rd_a[ia] = sum; 623 } 624 625 sum = 0.0; 626 for(ir=0; ir<nr; ir++) sum += Out_Ptr->Rd_r[ir]; 627 Out_Ptr->Rd = sum; 628 } 629 630 /*********************************************************** 631 * Return the index to the layer according to the index 632 * to the grid line system in z direction (Iz). 633 * 634 * Use the center of box. 635 ****/ 636 short IzToLayer(short Iz, InputStruct In_Parm) 637 { 638 short i=1; /* index to layer. */ 639 short num_layers = In_Parm.num_layers; 640 double dz = In_Parm.dz; 641 642 while( (Iz+0.5)*dz >= In_Parm.layerspecs[i].z1 643 && i<num_layers) i++; 644 645 return(i); 646 } 647 648 /*********************************************************** 649 * Get 1D array elements by summing the 2D array elements. 650 ****/ 651 void Sum2DA(InputStruct In_Parm, OutStruct * Out_Ptr) 652 { 653 short nz = In_Parm.nz; 654 short nr = In_Parm.nr; 655 short iz,ir; 656 double sum; 657 658 for(iz=0; iz<nz; iz++) { 659 sum = 0.0; 660 for(ir=0; ir<nr; ir++) sum += Out_Ptr->A_rz[ir][iz]; 661 Out_Ptr->A_z[iz] = sum; 662 } 663 664 sum = 0.0; 665 for(iz=0; iz<nz; iz++) {

Section B.3 mcmlio.c 135

666 sum += Out_Ptr->A_z[iz]; 667 Out_Ptr->A_l[IzToLayer(iz, In_Parm)] 668 += Out_Ptr->A_z[iz]; 669 } 670 Out_Ptr->A = sum; 671 } 672 673 /*********************************************************** 674 * Get 1D array elements by summing the 2D array elements. 675 ****/ 676 void Sum2DTt(InputStruct In_Parm, OutStruct * Out_Ptr) 677 { 678 short nr = In_Parm.nr; 679 short na = In_Parm.na; 680 short ir,ia; 681 double sum; 682 683 for(ir=0; ir<nr; ir++) { 684 sum = 0.0; 685 for(ia=0; ia<na; ia++) sum += Out_Ptr->Tt_ra[ir][ia]; 686 Out_Ptr->Tt_r[ir] = sum; 687 } 688 689 for(ia=0; ia<na; ia++) { 690 sum = 0.0; 691 for(ir=0; ir<nr; ir++) sum += Out_Ptr->Tt_ra[ir][ia]; 692 Out_Ptr->Tt_a[ia] = sum; 693 } 694 695 sum = 0.0; 696 for(ir=0; ir<nr; ir++) sum += Out_Ptr->Tt_r[ir]; 697 Out_Ptr->Tt = sum; 698 } 699 700 /*********************************************************** 701 * Scale Rd and Tt properly. 702 * 703 * "a" stands for angle alpha. 704 **** 705 * Scale Rd(r,a) and Tt(r,a) by 706 * (area perpendicular to photon direction) 707 * x(solid angle)x(No. of photons). 708 * or 709 * [2*PI*r*dr*cos(a)]x[2*PI*sin(a)*da]x[No. of photons] 710 * or 711 * [2*PI*PI*dr*da*r*sin(2a)]x[No. of photons] 712 **** 713 * Scale Rd(r) and Tt(r) by 714 * (area on the surface)x(No. of photons). 715 **** 716 * Scale Rd(a) and Tt(a) by 717 * (solid angle)x(No. of photons). 718 ****/ 719 void ScaleRdTt(InputStruct In_Parm, OutStruct * Out_Ptr) 720 { 721 short nr = In_Parm.nr; 722 short na = In_Parm.na; 723 double dr = In_Parm.dr; 724 double da = In_Parm.da; 725 short ir,ia; 726 double scale1, scale2; 727 728 scale1 = 4.0*PI*PI*dr*sin(da/2)*dr*In_Parm.num_photons; 729 /* The factor (ir+0.5)*sin(2a) to be added. */ 730 731 for(ir=0; ir<nr; ir++) 732 for(ia=0; ia<na; ia++) {

136 Appendix B Source Code of the Program mcml

733 scale2 = 1.0/((ir+0.5)*sin(2.0*(ia+0.5)*da)*scale1); 734 Out_Ptr->Rd_ra[ir][ia] *= scale2; 735 Out_Ptr->Tt_ra[ir][ia] *= scale2; 736 } 737 738 scale1 = 2.0*PI*dr*dr*In_Parm.num_photons; 739 /* area is 2*PI*[(ir+0.5)*dr]*dr.*/ 740 /* ir+0.5 to be added. */ 741 742 for(ir=0; ir<nr; ir++) { 743 scale2 = 1.0/((ir+0.5)*scale1); 744 Out_Ptr->Rd_r[ir] *= scale2; 745 Out_Ptr->Tt_r[ir] *= scale2; 746 } 747 748 scale1 = 2.0*PI*da*In_Parm.num_photons; 749 /* solid angle is 2*PI*sin(a)*da. sin(a) to be added. */ 750 751 for(ia=0; ia<na; ia++) { 752 scale2 = 1.0/(sin((ia+0.5)*da)*scale1); 753 Out_Ptr->Rd_a[ia] *= scale2; 754 Out_Ptr->Tt_a[ia] *= scale2; 755 } 756 757 scale2 = 1.0/(double)In_Parm.num_photons; 758 Out_Ptr->Rd *= scale2; 759 Out_Ptr->Tt *= scale2; 760 } 761 762 /*********************************************************** 763 * Scale absorption arrays properly. 764 ****/ 765 void ScaleA(InputStruct In_Parm, OutStruct * Out_Ptr) 766 { 767 short nz = In_Parm.nz; 768 short nr = In_Parm.nr; 769 double dz = In_Parm.dz; 770 double dr = In_Parm.dr; 771 short nl = In_Parm.num_layers; 772 short iz,ir; 773 short il; 774 double scale1; 775 776 /* Scale A_rz. */ 777 scale1 = 2.0*PI*dr*dr*dz*In_Parm.num_photons; 778 /* volume is 2*pi*(ir+0.5)*dr*dr*dz.*/ 779 /* ir+0.5 to be added. */ 780 for(iz=0; iz<nz; iz++) 781 for(ir=0; ir<nr; ir++) 782 Out_Ptr->A_rz[ir][iz] /= (ir+0.5)*scale1; 783 784 /* Scale A_z. */ 785 scale1 = 1.0/(dz*In_Parm.num_photons); 786 for(iz=0; iz<nz; iz++) 787 Out_Ptr->A_z[iz] *= scale1; 788 789 /* Scale A_l. Avoid int/int. */ 790 scale1 = 1.0/(double)In_Parm.num_photons; 791 for(il=0; il<=nl+1; il++) 792 Out_Ptr->A_l[il] *= scale1; 793 794 Out_Ptr->A *=scale1; 795 } 796 797 /*********************************************************** 798 * Sum and scale results of current run. 799 ****/

Section B.3 mcmlio.c 137

800 void SumScaleResult(InputStruct In_Parm, 801 OutStruct * Out_Ptr) 802 { 803 /* Get 1D & 0D results. */ 804 Sum2DRd(In_Parm, Out_Ptr); 805 Sum2DA(In_Parm, Out_Ptr); 806 Sum2DTt(In_Parm, Out_Ptr); 807 808 ScaleRdTt(In_Parm, Out_Ptr); 809 ScaleA(In_Parm, Out_Ptr); 810 } 811 812 /*********************************************************** 813 * Write the version number as the first string in the 814 * file. 815 * Use chars only so that they can be read as either 816 * ASCII or binary. 817 ****/ 818 void WriteVersion(FILE *file, char *Version) 819 { 820 fprintf(file, 821 "%s \t# Version number of the file format.\n\n", 822 Version); 823 fprintf(file, "####\n# Data categories include: \n"); 824 fprintf(file, "# InParm, RAT, \n"); 825 fprintf(file, "# A_l, A_z, Rd_r, Rd_a, Tt_r, Tt_a, \n"); 826 fprintf(file, "# A_rz, Rd_ra, Tt_ra \n####\n\n"); 827 } 828 829 /*********************************************************** 830 * Write the input parameters to the file. 831 ****/ 832 void WriteInParm(FILE *file, InputStruct In_Parm) 833 { 834 short i; 835 836 fprintf(file, 837 "InParm \t\t\t# Input parameters. cm is used.\n"); 838 839 fprintf(file, 840 "%s \tA\t\t# output file name, ASCII.\n", 841 In_Parm.out_fname); 842 fprintf(file, 843 "%ld \t\t\t# No. of photons\n", In_Parm.num_photons); 844 845 fprintf(file, 846 "%G\t%G\t\t# dz, dr [cm]\n", In_Parm.dz,In_Parm.dr); 847 fprintf(file, "%hd\t%hd\t%hd\t# No. of dz, dr, da.\n\n", 848 In_Parm.nz, In_Parm.nr, In_Parm.na); 849 850 fprintf(file, 851 "%hd\t\t\t\t\t# Number of layers\n", 852 In_Parm.num_layers); 853 fprintf(file, 854 "#n\tmua\tmus\tg\td\t# One line for each layer\n"); 855 fprintf(file, 856 "%G\t\t\t\t\t# n for medium above\n", 857 In_Parm.layerspecs[0].n); 858 for(i=1; i<=In_Parm.num_layers; i++) { 859 LayerStruct s; 860 s = In_Parm.layerspecs[i]; 861 fprintf(file, "%G\t%G\t%G\t%G\t%G\t# layer %hd\n", 862 s.n, s.mua, s.mus, s.g, s.z1-s.z0, i); 863 } 864 fprintf(file, "%G\t\t\t\t\t# n for medium below\n\n", 865 In_Parm.layerspecs[i].n); 866 }

138 Appendix B Source Code of the Program mcml

867 868 /*********************************************************** 869 * Write reflectance, absorption, transmission. 870 ****/ 871 void WriteRAT(FILE * file, OutStruct Out_Parm) 872 { 873 fprintf(file, 874 "RAT #Reflectance, absorption, transmission. \n"); 875 /* flag. */ 876 877 fprintf(file, 878 "%-14.6G \t#Specular reflectance [-]\n", Out_Parm.Rsp); 879 fprintf(file, 880 "%-14.6G \t#Diffuse reflectance [-]\n", Out_Parm.Rd); 881 fprintf(file, 882 "%-14.6G \t#Absorbed fraction [-]\n", Out_Parm.A); 883 fprintf(file, 884 "%-14.6G \t#Transmittance [-]\n", Out_Parm.Tt); 885 886 fprintf(file, "\n"); 887 } 888 889 /*********************************************************** 890 * Write absorption as a function of layer. 891 ****/ 892 void WriteA_layer(FILE * file, 893 short Num_Layers, 894 OutStruct Out_Parm) 895 { 896 short i; 897 898 fprintf(file, 899 "A_l #Absorption as a function of layer. [-]\n"); 900 /* flag. */ 901 902 for(i=1; i<=Num_Layers; i++) 903 fprintf(file, "%12.4G\n", Out_Parm.A_l[i]); 904 fprintf(file, "\n"); 905 } 906 907 /*********************************************************** 908 * 5 numbers each line. 909 ****/ 910 void WriteRd_ra(FILE * file, 911 short Nr, 912 short Na, 913 OutStruct Out_Parm) 914 { 915 short ir, ia; 916 917 fprintf(file, 918 "%s\n%s\n%s\n%s\n%s\n%s\n", /* flag. */ 919 "# Rd[r][angle]. [1/(cm2sr)].", 920 "# Rd[0][0], [0][1],..[0][na-1]", 921 "# Rd[1][0], [1][1],..[1][na-1]", 922 "# ...", 923 "# Rd[nr-1][0], [nr-1][1],..[nr-1][na-1]", 924 "Rd_ra"); 925 926 for(ir=0;ir<Nr;ir++) 927 for(ia=0;ia<Na;ia++) { 928 fprintf(file, "%12.4E ", Out_Parm.Rd_ra[ir][ia]); 929 if( (ir*Na + ia + 1)%5 == 0) fprintf(file, "\n"); 930 } 931 932 fprintf(file, "\n"); 933 }

Section B.3 mcmlio.c 139

934 935 /*********************************************************** 936 * 1 number each line. 937 ****/ 938 void WriteRd_r(FILE * file, 939 short Nr, 940 OutStruct Out_Parm) 941 { 942 short ir; 943 944 fprintf(file, 945 "Rd_r #Rd[0], [1],..Rd[nr-1]. [1/cm2]\n"); /* flag. */ 946 947 for(ir=0;ir<Nr;ir++) { 948 fprintf(file, "%12.4E\n", Out_Parm.Rd_r[ir]); 949 } 950 951 fprintf(file, "\n"); 952 } 953 954 /*********************************************************** 955 * 1 number each line. 956 ****/ 957 void WriteRd_a(FILE * file, 958 short Na, 959 OutStruct Out_Parm) 960 { 961 short ia; 962 963 fprintf(file, 964 "Rd_a #Rd[0], [1],..Rd[na-1]. [sr-1]\n"); /* flag. */ 965 966 for(ia=0;ia<Na;ia++) { 967 fprintf(file, "%12.4E\n", Out_Parm.Rd_a[ia]); 968 } 969 970 fprintf(file, "\n"); 971 } 972 973 /*********************************************************** 974 * 5 numbers each line. 975 ****/ 976 void WriteTt_ra(FILE * file, 977 short Nr, 978 short Na, 979 OutStruct Out_Parm) 980 { 981 short ir, ia; 982 983 fprintf(file, 984 "%s\n%s\n%s\n%s\n%s\n%s\n", /* flag. */ 985 "# Tt[r][angle]. [1/(cm2sr)].", 986 "# Tt[0][0], [0][1],..[0][na-1]", 987 "# Tt[1][0], [1][1],..[1][na-1]", 988 "# ...", 989 "# Tt[nr-1][0], [nr-1][1],..[nr-1][na-1]", 990 "Tt_ra"); 991 992 for(ir=0;ir<Nr;ir++) 993 for(ia=0;ia<Na;ia++) { 994 fprintf(file, "%12.4E ", Out_Parm.Tt_ra[ir][ia]); 995 if( (ir*Na + ia + 1)%5 == 0) fprintf(file, "\n"); 996 } 997 998 fprintf(file, "\n"); 999 } 1000

140 Appendix B Source Code of the Program mcml

1001 /*********************************************************** 1002 * 5 numbers each line. 1003 ****/ 1004 void WriteA_rz(FILE * file, 1005 short Nr, 1006 short Nz, 1007 OutStruct Out_Parm) 1008 { 1009 short iz, ir; 1010 1011 fprintf(file, 1012 "%s\n%s\n%s\n%s\n%s\n%s\n", /* flag. */ 1013 "# A[r][z]. [1/cm3]", 1014 "# A[0][0], [0][1],..[0][nz-1]", 1015 "# A[1][0], [1][1],..[1][nz-1]", 1016 "# ...", 1017 "# A[nr-1][0], [nr-1][1],..[nr-1][nz-1]", 1018 "A_rz"); 1019 1020 for(ir=0;ir<Nr;ir++) 1021 for(iz=0;iz<Nz;iz++) { 1022 fprintf(file, "%12.4E ", Out_Parm.A_rz[ir][iz]); 1023 if( (ir*Nz + iz + 1)%5 == 0) fprintf(file, "\n"); 1024 } 1025 1026 fprintf(file, "\n"); 1027 } 1028 1029 /*********************************************************** 1030 * 1 number each line. 1031 ****/ 1032 void WriteA_z(FILE * file, 1033 short Nz, 1034 OutStruct Out_Parm) 1035 { 1036 short iz; 1037 1038 fprintf(file, 1039 "A_z #A[0], [1],..A[nz-1]. [1/cm]\n"); /* flag. */ 1040 1041 for(iz=0;iz<Nz;iz++) { 1042 fprintf(file, "%12.4E\n", Out_Parm.A_z[iz]); 1043 } 1044 1045 fprintf(file, "\n"); 1046 } 1047 1048 /*********************************************************** 1049 * 1 number each line. 1050 ****/ 1051 void WriteTt_r(FILE * file, 1052 short Nr, 1053 OutStruct Out_Parm) 1054 { 1055 short ir; 1056 1057 fprintf(file, 1058 "Tt_r #Tt[0], [1],..Tt[nr-1]. [1/cm2]\n"); /* flag. */ 1059 1060 for(ir=0;ir<Nr;ir++) { 1061 fprintf(file, "%12.4E\n", Out_Parm.Tt_r[ir]); 1062 } 1063 1064 fprintf(file, "\n"); 1065 } 1066 1067 /***********************************************************

Section B.3 mcmlio.c 141

1068 * 1 number each line. 1069 ****/ 1070 void WriteTt_a(FILE * file, 1071 short Na, 1072 OutStruct Out_Parm) 1073 { 1074 short ia; 1075 1076 fprintf(file, 1077 "Tt_a #Tt[0], [1],..Tt[na-1]. [sr-1]\n"); /* flag. */ 1078 1079 for(ia=0;ia<Na;ia++) { 1080 fprintf(file, "%12.4E\n", Out_Parm.Tt_a[ia]); 1081 } 1082 1083 fprintf(file, "\n"); 1084 } 1085 1086 /*********************************************************** 1087 ****/ 1088 void WriteResult(InputStruct In_Parm, 1089 OutStruct Out_Parm, 1090 char * TimeReport) 1091 { 1092 FILE *file; 1093 1094 file = fopen(In_Parm.out_fname, "w"); 1095 if(file == NULL) nrerror("Cannot open file to write.\n"); 1096 1097 if(toupper(In_Parm.out_fformat) == 'A') 1098 WriteVersion(file, "A1"); 1099 else 1100 WriteVersion(file, "B1"); 1101 1102 fprintf(file, "# %s", TimeReport); 1103 fprintf(file, "\n"); 1104 1105 WriteInParm(file, In_Parm); 1106 WriteRAT(file, Out_Parm); 1107 /* reflectance, absorption, transmittance. */ 1108 1109 /* 1D arrays. */ 1110 WriteA_layer(file, In_Parm.num_layers, Out_Parm); 1111 WriteA_z(file, In_Parm.nz, Out_Parm); 1112 WriteRd_r(file, In_Parm.nr, Out_Parm); 1113 WriteRd_a(file, In_Parm.na, Out_Parm); 1114 WriteTt_r(file, In_Parm.nr, Out_Parm); 1115 WriteTt_a(file, In_Parm.na, Out_Parm); 1116 1117 /* 2D arrays. */ 1118 WriteA_rz(file, In_Parm.nr, In_Parm.nz, Out_Parm); 1119 WriteRd_ra(file, In_Parm.nr, In_Parm.na, Out_Parm); 1120 WriteTt_ra(file, In_Parm.nr, In_Parm.na, Out_Parm); 1121 1122 fclose(file); 1123 }

142 Appendix B Source Code of the Program mcml

B.4 mcmlgo.c

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * Launch, move, and record photon weight. 6 ****/ 7 8 #include "mcml.h" 9 10 #define STANDARDTEST 0 11 /* testing program using fixed rnd seed. */ 12 13 #define PARTIALREFLECTION 0 14 /* 1=split photon, 0=statistical reflection. */ 15 16 #define COSZERO (1.0-1.0E-12) 17 /* cosine of about 1e-6 rad. */ 18 19 #define COS90D 1.0E-6 20 /* cosine of about 1.57 - 1e-6 rad. */ 21 22 23 /*********************************************************** 24 * A random number generator from Numerical Recipes in C. 25 ****/ 26 #define MBIG 1000000000 27 #define MSEED 161803398 28 #define MZ 0 29 #define FAC 1.0E-9 30 31 float ran3(int *idum) 32 { 33 static int inext,inextp; 34 static long ma[56]; 35 static int iff=0; 36 long mj,mk; 37 int i,ii,k; 38 39 if (*idum < 0 || iff == 0) { 40 iff=1; 41 mj=MSEED-(*idum < 0 ? -*idum : *idum); 42 mj %= MBIG; 43 ma[55]=mj; 44 mk=1; 45 for (i=1;i<=54;i++) { 46 ii=(21*i) % 55; 47 ma[ii]=mk; 48 mk=mj-mk; 49 if (mk < MZ) mk += MBIG; 50 mj=ma[ii]; 51 } 52 for (k=1;k<=4;k++) 53 for (i=1;i<=55;i++) { 54 ma[i] -= ma[1+(i+30) % 55]; 55 if (ma[i] < MZ) ma[i] += MBIG; 56 } 57 inext=0; 58 inextp=31; 59 *idum=1; 60 } 61 if (++inext == 56) inext=1; 62 if (++inextp == 56) inextp=1;

Section B.4 mcmlgo.c 143

63 mj=ma[inext]-ma[inextp]; 64 if (mj < MZ) mj += MBIG; 65 ma[inext]=mj; 66 return mj*FAC; 67 } 68 69 #undef MBIG 70 #undef MSEED 71 #undef MZ 72 #undef FAC 73 74 75 /*********************************************************** 76 * Generate a random number between 0 and 1. Take a 77 * number as seed the first time entering the function. 78 * The seed is limited to 1<<15. 79 * We found that when idum is too large, ran3 may return 80 * numbers beyond 0 and 1. 81 ****/ 82 double RandomNum(void) 83 { 84 static Boolean first_time=1; 85 static int idum; /* seed for ran3. */ 86 87 if(first_time) { 88 #if STANDARDTEST /* Use fixed seed to test the program. */ 89 idum = - 1; 90 #else 91 idum = -(int)time(NULL)%(1<<15); 92 /* use 16-bit integer as the seed. */ 93 #endif 94 ran3(&idum); 95 first_time = 0; 96 idum = 1; 97 } 98 99 return( (double)ran3(&idum) ); 100 } 101 102 /*********************************************************** 103 * Compute the specular reflection. 104 * 105 * If the first layer is a turbid medium, use the Fresnel 106 * reflection from the boundary of the first layer as the 107 * specular reflectance. 108 * 109 * If the first layer is glass, multiple reflections in 110 * the first layer is considered to get the specular 111 * reflectance. 112 * 113 * The subroutine assumes the Layerspecs array is correctly 114 * initialized. 115 ****/ 116 double Rspecular(LayerStruct * Layerspecs_Ptr) 117 { 118 double r1, r2; 119 /* direct reflections from the 1st and 2nd layers. */ 120 double temp; 121 122 temp =(Layerspecs_Ptr[0].n - Layerspecs_Ptr[1].n) 123 /(Layerspecs_Ptr[0].n + Layerspecs_Ptr[1].n); 124 r1 = temp*temp; 125 126 if((Layerspecs_Ptr[1].mua == 0.0) 127 && (Layerspecs_Ptr[1].mus == 0.0)) { /* glass layer. */ 128 temp = (Layerspecs_Ptr[1].n - Layerspecs_Ptr[2].n) 129 /(Layerspecs_Ptr[1].n + Layerspecs_Ptr[2].n);

144 Appendix B Source Code of the Program mcml

130 r2 = temp*temp; 131 r1 = r1 + (1-r1)*(1-r1)*r2/(1-r1*r2); 132 } 133 134 return (r1); 135 } 136 137 /*********************************************************** 138 * Initialize a photon packet. 139 ****/ 140 void LaunchPhoton(double Rspecular, 141 LayerStruct * Layerspecs_Ptr, 142 PhotonStruct * Photon_Ptr) 143 { 144 Photon_Ptr->w = 1.0 - Rspecular; 145 Photon_Ptr->dead = 0; 146 Photon_Ptr->layer = 1; 147 Photon_Ptr->s = 0; 148 Photon_Ptr->sleft= 0; 149 150 Photon_Ptr->x = 0.0; 151 Photon_Ptr->y = 0.0; 152 Photon_Ptr->z = 0.0; 153 Photon_Ptr->ux = 0.0; 154 Photon_Ptr->uy = 0.0; 155 Photon_Ptr->uz = 1.0; 156 157 if((Layerspecs_Ptr[1].mua == 0.0) 158 && (Layerspecs_Ptr[1].mus == 0.0)) { /* glass layer. */ 159 Photon_Ptr->layer = 2; 160 Photon_Ptr->z = Layerspecs_Ptr[2].z0; 161 } 162 } 163 164 /*********************************************************** 165 * Choose (sample) a new theta angle for photon propagation 166 * according to the anisotropy. 167 * 168 * If anisotropy g is 0, then 169 * cos(theta) = 2*rand-1. 170 * otherwise 171 * sample according to the Henyey-Greenstein function. 172 * 173 * Returns the cosine of the polar deflection angle theta. 174 ****/ 175 double SpinTheta(double g) 176 { 177 double cost; 178 179 if(g == 0.0) 180 cost = 2*RandomNum() -1; 181 else { 182 double temp = (1-g*g)/(1-g+2*g*RandomNum()); 183 cost = (1+g*g - temp*temp)/(2*g); 184 } 185 return(cost); 186 } 187 188 189 /*********************************************************** 190 * Choose a new direction for photon propagation by 191 * sampling the polar deflection angle theta and the 192 * azimuthal angle psi. 193 * 194 * Note: 195 * theta: 0 - pi so sin(theta) is always positive 196 * feel free to use sqrt() for cos(theta).

Section B.4 mcmlgo.c 145

197 * 198 * psi: 0 - 2pi 199 * for 0-pi sin(psi) is + 200 * for pi-2pi sin(psi) is - 201 ****/ 202 void Spin(double g, 203 PhotonStruct * Photon_Ptr) 204 { 205 double cost, sint; /* cosine and sine of the */ 206 /* polar deflection angle theta. */ 207 double cosp, sinp; /* cosine and sine of the */ 208 /* azimuthal angle psi. */ 209 double ux = Photon_Ptr->ux; 210 double uy = Photon_Ptr->uy; 211 double uz = Photon_Ptr->uz; 212 double psi; 213 214 cost = SpinTheta(g); 215 sint = sqrt(1.0 - cost*cost); 216 /* sqrt() is faster than sin(). */ 217 218 psi = 2.0*PI*RandomNum(); /* spin psi 0-2pi. */ 219 cosp = cos(psi); 220 if(psi<PI) 221 sinp = sqrt(1.0 - cosp*cosp); 222 /* sqrt() is faster than sin(). */ 223 else 224 sinp = - sqrt(1.0 - cosp*cosp); 225 226 if(fabs(uz) > COSZERO) { /* normal incident. */ 227 Photon_Ptr->ux = sint*cosp; 228 Photon_Ptr->uy = sint*sinp; 229 Photon_Ptr->uz = cost*SIGN(uz); 230 /* SIGN() is faster than division. */ 231 } 232 else { /* regular incident. */ 233 double temp = sqrt(1.0 - uz*uz); 234 Photon_Ptr->ux = sint*(ux*uz*cosp - uy*sinp) 235 /temp + ux*cost; 236 Photon_Ptr->uy = sint*(uy*uz*cosp + ux*sinp) 237 /temp + uy*cost; 238 Photon_Ptr->uz = -sint*cosp*temp + uz*cost; 239 } 240 } 241 242 /*********************************************************** 243 * Move the photon s away in the current layer of medium. 244 ****/ 245 void Hop(PhotonStruct * Photon_Ptr) 246 { 247 double s = Photon_Ptr->s; 248 249 Photon_Ptr->x += s*Photon_Ptr->ux; 250 Photon_Ptr->y += s*Photon_Ptr->uy; 251 Photon_Ptr->z += s*Photon_Ptr->uz; 252 } 253 254 /*********************************************************** 255 * If uz != 0, return the photon step size in glass, 256 * Otherwise, return 0. 257 * 258 * The step size is the distance between the current 259 * position and the boundary in the photon direction. 260 * 261 * Make sure uz !=0 before calling this function. 262 ****/ 263 void StepSizeInGlass(PhotonStruct * Photon_Ptr,

146 Appendix B Source Code of the Program mcml

264 InputStruct * In_Ptr) 265 { 266 double dl_b; /* step size to boundary. */ 267 short layer = Photon_Ptr->layer; 268 double uz = Photon_Ptr->uz; 269 270 /* Stepsize to the boundary. */ 271 if(uz>0.0) 272 dl_b = (In_Ptr->layerspecs[layer].z1 - Photon_Ptr->z) 273 /uz; 274 else if(uz<0.0) 275 dl_b = (In_Ptr->layerspecs[layer].z0 - Photon_Ptr->z) 276 /uz; 277 else 278 dl_b = 0.0; 279 280 Photon_Ptr->s = dl_b; 281 } 282 283 /*********************************************************** 284 * Pick a step size for a photon packet when it is in 285 * tissue. 286 * If the member sleft is zero, make a new step size 287 * with: -log(rnd)/(mua+mus). 288 * Otherwise, pick up the leftover in sleft. 289 * 290 * Layer is the index to layer. 291 * In_Ptr is the input parameters. 292 ****/ 293 void StepSizeInTissue(PhotonStruct * Photon_Ptr, 294 InputStruct * In_Ptr) 295 { 296 short layer = Photon_Ptr->layer; 297 double mua = In_Ptr->layerspecs[layer].mua; 298 double mus = In_Ptr->layerspecs[layer].mus; 299 300 if(Photon_Ptr->sleft == 0.0) { /* make a new step. */ 301 double rnd; 302 303 do rnd = RandomNum(); 304 while( rnd <= 0.0 ); /* avoid zero. */ 305 Photon_Ptr->s = -log(rnd)/(mua+mus); 306 } 307 else { /* take the leftover. */ 308 Photon_Ptr->s = Photon_Ptr->sleft/(mua+mus); 309 Photon_Ptr->sleft = 0.0; 310 } 311 } 312 313 /*********************************************************** 314 * Check if the step will hit the boundary. 315 * Return 1 if hit boundary. 316 * Return 0 otherwise. 317 * 318 * If the projected step hits the boundary, the members 319 * s and sleft of Photon_Ptr are updated. 320 ****/ 321 Boolean HitBoundary(PhotonStruct * Photon_Ptr, 322 InputStruct * In_Ptr) 323 { 324 double dl_b; /* length to boundary. */ 325 short layer = Photon_Ptr->layer; 326 double uz = Photon_Ptr->uz; 327 Boolean hit; 328 329 /* Distance to the boundary. */ 330 if(uz>0.0)

Section B.4 mcmlgo.c 147

331 dl_b = (In_Ptr->layerspecs[layer].z1 332 - Photon_Ptr->z)/uz; /* dl_b>0. */ 333 else if(uz<0.0) 334 dl_b = (In_Ptr->layerspecs[layer].z0 335 - Photon_Ptr->z)/uz; /* dl_b>0. */ 336 337 if(uz != 0.0 && Photon_Ptr->s > dl_b) { 338 /* not horizontal & crossing. */ 339 double mut = In_Ptr->layerspecs[layer].mua 340 + In_Ptr->layerspecs[layer].mus; 341 342 Photon_Ptr->sleft = (Photon_Ptr->s - dl_b)*mut; 343 Photon_Ptr->s = dl_b; 344 hit = 1; 345 } 346 else 347 hit = 0; 348 349 return(hit); 350 } 351 352 /*********************************************************** 353 * Drop photon weight inside the tissue (not glass). 354 * 355 * The photon is assumed not dead. 356 * 357 * The weight drop is dw = w*mua/(mua+mus). 358 * 359 * The dropped weight is assigned to the absorption array 360 * elements. 361 ****/ 362 void Drop(InputStruct * In_Ptr, 363 PhotonStruct * Photon_Ptr, 364 OutStruct * Out_Ptr) 365 { 366 double dwa; /* absorbed weight.*/ 367 double x = Photon_Ptr->x; 368 double y = Photon_Ptr->y; 369 short iz, ir; /* index to z & r. */ 370 short layer = Photon_Ptr->layer; 371 double mua, mus; 372 373 /* compute array indices. */ 374 iz = (short)(Photon_Ptr->z/In_Ptr->dz); 375 if(iz>In_Ptr->nz-1) iz=In_Ptr->nz-1; 376 377 ir = (short)(sqrt(x*x+y*y)/In_Ptr->dr); 378 if(ir>In_Ptr->nr-1) ir=In_Ptr->nr-1; 379 380 /* update photon weight. */ 381 mua = In_Ptr->layerspecs[layer].mua; 382 mus = In_Ptr->layerspecs[layer].mus; 383 dwa = Photon_Ptr->w * mua/(mua+mus); 384 Photon_Ptr->w -= dwa; 385 386 /* assign dwa to the absorption array element. */ 387 Out_Ptr->A_rz[ir][iz] += dwa; 388 } 389 390 /*********************************************************** 391 * The photon weight is small, and the photon packet tries 392 * to survive a roulette. 393 ****/ 394 void Roulette(PhotonStruct * Photon_Ptr) 395 { 396 if(Photon_Ptr->w == 0.0) 397 Photon_Ptr->dead = 1;

148 Appendix B Source Code of the Program mcml

398 else if(RandomNum() < CHANCE) /* survived the roulette.*/ 399 Photon_Ptr->w /= CHANCE; 400 else 401 Photon_Ptr->dead = 1; 402 } 403 404 /*********************************************************** 405 * Compute the Fresnel reflectance. 406 * 407 * Make sure that the cosine of the incident angle a1 408 * is positive, and the case when the angle is greater 409 * than the critical angle is ruled out. 410 * 411 * Avoid trigonometric function operations as much as 412 * possible, because they are computation-intensive. 413 ****/ 414 double RFresnel(double n1, /* incident refractive index.*/ 415 double n2, /* transmit refractive index.*/ 416 double ca1, /* cosine of the incident */ 417 /* angle. 0<a1<90 degrees. */ 418 double * ca2_Ptr) /* pointer to the */ 419 /* cosine of the transmission */ 420 /* angle. a2>0. */ 421 { 422 double r; 423 424 if(n1==n2) { /** matched boundary. **/ 425 *ca2_Ptr = ca1; 426 r = 0.0; 427 } 428 else if(ca1>COSZERO) { /** normal incident. **/ 429 *ca2_Ptr = ca1; 430 r = (n2-n1)/(n2+n1); 431 r *= r; 432 } 433 else if(ca1<COS90D) { /** very slant. **/ 434 *ca2_Ptr = 0.0; 435 r = 1.0; 436 } 437 else { /** general. **/ 438 double sa1, sa2; 439 /* sine of the incident and transmission angles. */ 440 double ca2; 441 442 sa1 = sqrt(1-ca1*ca1); 443 sa2 = n1*sa1/n2; 444 if(sa2>=1.0) { 445 /* double check for total internal reflection. */ 446 *ca2_Ptr = 0.0; 447 r = 1.0; 448 } 449 else { 450 double cap, cam; /* cosines of the sum ap or */ 451 /* difference am of the two */ 452 /* angles. ap = a1+a2 */ 453 /* am = a1 - a2. */ 454 double sap, sam; /* sines. */ 455 456 *ca2_Ptr = ca2 = sqrt(1-sa2*sa2); 457 458 cap = ca1*ca2 - sa1*sa2; /* c+ = cc - ss. */ 459 cam = ca1*ca2 + sa1*sa2; /* c- = cc + ss. */ 460 sap = sa1*ca2 + ca1*sa2; /* s+ = sc + cs. */ 461 sam = sa1*ca2 - ca1*sa2; /* s- = sc - cs. */ 462 r = 0.5*sam*sam*(cam*cam+cap*cap)/(sap*sap*cam*cam); 463 /* rearranged for speed. */ 464 }

Section B.4 mcmlgo.c 149

465 } 466 return(r); 467 } 468 469 /*********************************************************** 470 * Record the photon weight exiting the first layer(uz<0), 471 * no matter whether the layer is glass or not, to the 472 * reflection array. 473 * 474 * Update the photon weight as well. 475 ****/ 476 void RecordR(double Refl, /* reflectance. */ 477 InputStruct * In_Ptr, 478 PhotonStruct * Photon_Ptr, 479 OutStruct * Out_Ptr) 480 { 481 double x = Photon_Ptr->x; 482 double y = Photon_Ptr->y; 483 short ir, ia; /* index to r & angle. */ 484 485 ir = (short)(sqrt(x*x+y*y)/In_Ptr->dr); 486 if(ir>In_Ptr->nr-1) ir=In_Ptr->nr-1; 487 488 ia = (short)(acos(-Photon_Ptr->uz)/In_Ptr->da); 489 if(ia>In_Ptr->na-1) ia=In_Ptr->na-1; 490 491 /* assign photon to the reflection array element. */ 492 Out_Ptr->Rd_ra[ir][ia] += Photon_Ptr->w*(1.0-Refl); 493 494 Photon_Ptr->w *= Refl; 495 } 496 497 /*********************************************************** 498 * Record the photon weight exiting the last layer(uz>0), 499 * no matter whether the layer is glass or not, to the 500 * transmittance array. 501 * 502 * Update the photon weight as well. 503 ****/ 504 void RecordT(double Refl, 505 InputStruct * In_Ptr, 506 PhotonStruct * Photon_Ptr, 507 OutStruct * Out_Ptr) 508 { 509 double x = Photon_Ptr->x; 510 double y = Photon_Ptr->y; 511 short ir, ia; /* index to r & angle. */ 512 513 ir = (short)(sqrt(x*x+y*y)/In_Ptr->dr); 514 if(ir>In_Ptr->nr-1) ir=In_Ptr->nr-1; 515 516 ia = (short)(acos(Photon_Ptr->uz)/In_Ptr->da); 517 if(ia>In_Ptr->na-1) ia=In_Ptr->na-1; 518 519 /* assign photon to the transmittance array element. */ 520 Out_Ptr->Tt_ra[ir][ia] += Photon_Ptr->w*(1.0-Refl); 521 522 Photon_Ptr->w *= Refl; 523 } 524 525 /*********************************************************** 526 * Decide whether the photon will be transmitted or 527 * reflected on the upper boundary (uz<0) of the current 528 * layer. 529 * 530 * If "layer" is the first layer, the photon packet will 531 * be partially transmitted and partially reflected if

150 Appendix B Source Code of the Program mcml

532 * PARTIALREFLECTION is set to 1, 533 * or the photon packet will be either transmitted or 534 * reflected determined statistically if PARTIALREFLECTION 535 * is set to 0. 536 * 537 * Record the transmitted photon weight as reflection. 538 * 539 * If the "layer" is not the first layer and the photon 540 * packet is transmitted, move the photon to "layer-1". 541 * 542 * Update the photon parmameters. 543 ****/ 544 void CrossUpOrNot(InputStruct * In_Ptr, 545 PhotonStruct * Photon_Ptr, 546 OutStruct * Out_Ptr) 547 { 548 double uz = Photon_Ptr->uz; /* z directional cosine. */ 549 double uz1; /* cosines of transmission alpha. always */ 550 /* positive. */ 551 double r=0.0; /* reflectance */ 552 short layer = Photon_Ptr->layer; 553 double ni = In_Ptr->layerspecs[layer].n; 554 double nt = In_Ptr->layerspecs[layer-1].n; 555 556 /* Get r. */ 557 if( - uz <= In_Ptr->layerspecs[layer].cos_crit0) 558 r=1.0; /* total internal reflection. */ 559 else r = RFresnel(ni, nt, -uz, &uz1); 560 561 #if PARTIALREFLECTION 562 if(layer == 1 && r<1.0) { /* partially transmitted. */ 563 Photon_Ptr->uz = -uz1; /* transmitted photon. */ 564 RecordR(r, In_Ptr, Photon_Ptr, Out_Ptr); 565 Photon_Ptr->uz = -uz; /* reflected photon. */ 566 } 567 else if(RandomNum() > r) {/* transmitted to layer-1. */ 568 Photon_Ptr->layer--; 569 Photon_Ptr->ux *= ni/nt; 570 Photon_Ptr->uy *= ni/nt; 571 Photon_Ptr->uz = -uz1; 572 } 573 else /* reflected. */ 574 Photon_Ptr->uz = -uz; 575 #else 576 if(RandomNum() > r) { /* transmitted to layer-1. */ 577 if(layer==1) { 578 Photon_Ptr->uz = -uz1; 579 RecordR(0.0, In_Ptr, Photon_Ptr, Out_Ptr); 580 Photon_Ptr->dead = 1; 581 } 582 else { 583 Photon_Ptr->layer--; 584 Photon_Ptr->ux *= ni/nt; 585 Photon_Ptr->uy *= ni/nt; 586 Photon_Ptr->uz = -uz1; 587 } 588 } 589 else /* reflected. */ 590 Photon_Ptr->uz = -uz; 591 #endif 592 } 593 594 /*********************************************************** 595 * Decide whether the photon will be transmitted or be 596 * reflected on the bottom boundary (uz>0) of the current 597 * layer. 598 *

Section B.4 mcmlgo.c 151

599 * If the photon is transmitted, move the photon to 600 * "layer+1". If "layer" is the last layer, record the 601 * transmitted weight as transmittance. See comments for 602 * CrossUpOrNot. 603 * 604 * Update the photon parmameters. 605 ****/ 606 void CrossDnOrNot(InputStruct * In_Ptr, 607 PhotonStruct * Photon_Ptr, 608 OutStruct * Out_Ptr) 609 { 610 double uz = Photon_Ptr->uz; /* z directional cosine. */ 611 double uz1; /* cosines of transmission alpha. */ 612 double r=0.0; /* reflectance */ 613 short layer = Photon_Ptr->layer; 614 double ni = In_Ptr->layerspecs[layer].n; 615 double nt = In_Ptr->layerspecs[layer+1].n; 616 617 /* Get r. */ 618 if( uz <= In_Ptr->layerspecs[layer].cos_crit1) 619 r=1.0; /* total internal reflection. */ 620 else r = RFresnel(ni, nt, uz, &uz1); 621 622 #if PARTIALREFLECTION 623 if(layer == In_Ptr->num_layers && r<1.0) { 624 Photon_Ptr->uz = uz1; 625 RecordT(r, In_Ptr, Photon_Ptr, Out_Ptr); 626 Photon_Ptr->uz = -uz; 627 } 628 else if(RandomNum() > r) {/* transmitted to layer+1. */ 629 Photon_Ptr->layer++; 630 Photon_Ptr->ux *= ni/nt; 631 Photon_Ptr->uy *= ni/nt; 632 Photon_Ptr->uz = uz1; 633 } 634 else /* reflected. */ 635 Photon_Ptr->uz = -uz; 636 #else 637 if(RandomNum() > r) { /* transmitted to layer+1. */ 638 if(layer == In_Ptr->num_layers) { 639 Photon_Ptr->uz = uz1; 640 RecordT(0.0, In_Ptr, Photon_Ptr, Out_Ptr); 641 Photon_Ptr->dead = 1; 642 } 643 else { 644 Photon_Ptr->layer++; 645 Photon_Ptr->ux *= ni/nt; 646 Photon_Ptr->uy *= ni/nt; 647 Photon_Ptr->uz = uz1; 648 } 649 } 650 else /* reflected. */ 651 Photon_Ptr->uz = -uz; 652 #endif 653 } 654 655 /*********************************************************** 656 ****/ 657 void CrossOrNot(InputStruct * In_Ptr, 658 PhotonStruct * Photon_Ptr, 659 OutStruct * Out_Ptr) 660 { 661 if(Photon_Ptr->uz < 0.0) 662 CrossUpOrNot(In_Ptr, Photon_Ptr, Out_Ptr); 663 else 664 CrossDnOrNot(In_Ptr, Photon_Ptr, Out_Ptr); 665 }

152 Appendix B Source Code of the Program mcml

666 667 /*********************************************************** 668 * Move the photon packet in glass layer. 669 * Horizontal photons are killed because they will 670 * never interact with tissue again. 671 ****/ 672 void HopInGlass(InputStruct * In_Ptr, 673 PhotonStruct * Photon_Ptr, 674 OutStruct * Out_Ptr) 675 { 676 double dl; /* step size. 1/cm */ 677 678 if(Photon_Ptr->uz == 0.0) { 679 /* horizontal photon in glass is killed. */ 680 Photon_Ptr->dead = 1; 681 } 682 else { 683 StepSizeInGlass(Photon_Ptr, In_Ptr); 684 Hop(Photon_Ptr); 685 CrossOrNot(In_Ptr, Photon_Ptr, Out_Ptr); 686 } 687 } 688 689 /*********************************************************** 690 * Set a step size, move the photon, drop some weight, 691 * choose a new photon direction for propagation. 692 * 693 * When a step size is long enough for the photon to 694 * hit an interface, this step is divided into two steps. 695 * First, move the photon to the boundary free of 696 * absorption or scattering, then decide whether the 697 * photon is reflected or transmitted. 698 * Then move the photon in the current or transmission 699 * medium with the unfinished stepsize to interaction 700 * site. If the unfinished stepsize is still too long, 701 * repeat the above process. 702 ****/ 703 void HopDropSpinInTissue(InputStruct * In_Ptr, 704 PhotonStruct * Photon_Ptr, 705 OutStruct * Out_Ptr) 706 { 707 StepSizeInTissue(Photon_Ptr, In_Ptr); 708 709 if(HitBoundary(Photon_Ptr, In_Ptr)) { 710 Hop(Photon_Ptr); /* move to boundary plane. */ 711 CrossOrNot(In_Ptr, Photon_Ptr, Out_Ptr); 712 } 713 else { 714 Hop(Photon_Ptr); 715 Drop(In_Ptr, Photon_Ptr, Out_Ptr); 716 Spin(In_Ptr->layerspecs[Photon_Ptr->layer].g, 717 Photon_Ptr); 718 } 719 } 720 721 /*********************************************************** 722 ****/ 723 void HopDropSpin(InputStruct * In_Ptr, 724 PhotonStruct * Photon_Ptr, 725 OutStruct * Out_Ptr) 726 { 727 short layer = Photon_Ptr->layer; 728 729 if((In_Ptr->layerspecs[layer].mua == 0.0) 730 && (In_Ptr->layerspecs[layer].mus == 0.0)) 731 /* glass layer. */ 732 HopInGlass(In_Ptr, Photon_Ptr, Out_Ptr);

Section B.4 mcmlgo.c 153

733 else 734 HopDropSpinInTissue(In_Ptr, Photon_Ptr, Out_Ptr); 735 736 if( Photon_Ptr->w < In_Ptr->Wth && !Photon_Ptr->dead) 737 Roulette(Photon_Ptr); 738 }

154 Appendix B Source Code of the Program mcml

B.5 mcmlnr.c

1 /*********************************************************** 2 * Copyright Univ. of Texas M.D. Anderson Cancer Center 3 * 1992. 4 * 5 * Some routines modified from Numerical Recipes in C, 6 * including error report, array or matrix declaration 7 * and releasing. 8 ****/ 9 #include <stdlib.h> 10 #include <stdio.h> 11 #include <math.h> 12 13 /*********************************************************** 14 * Report error message to stderr, then exit the program 15 * with signal 1. 16 ****/ 17 void nrerror(char error_text[]) 18 19 { 20 fprintf(stderr,"%s\n",error_text); 21 fprintf(stderr,"...now exiting to system...\n"); 22 exit(1); 23 } 24 25 /*********************************************************** 26 * Allocate an array with index from nl to nh inclusive. 27 * 28 * Original matrix and vector from Numerical Recipes in C 29 * don't initialize the elements to zero. This will 30 * be accomplished by the following functions. 31 ****/ 32 double *AllocVector(short nl, short nh) 33 { 34 double *v; 35 short i; 36 37 v=(double *)malloc((unsigned) (nh-nl+1)*sizeof(double)); 38 if (!v) nrerror("allocation failure in vector()"); 39 40 v -= nl; 41 for(i=nl;i<=nh;i++) v[i] = 0.0; /* init. */ 42 return v; 43 } 44 45 /*********************************************************** 46 * Allocate a matrix with row index from nrl to nrh 47 * inclusive, and column index from ncl to nch 48 * inclusive. 49 ****/ 50 double **AllocMatrix(short nrl,short nrh, 51 short ncl,short nch) 52 { 53 short i,j; 54 double **m; 55 56 m=(double **) malloc((unsigned) (nrh-nrl+1) 57 *sizeof(double*)); 58 if (!m) nrerror("allocation failure 1 in matrix()"); 59 m -= nrl; 60 61 for(i=nrl;i<=nrh;i++) { 62 m[i]=(double *) malloc((unsigned) (nch-ncl+1)

Section B.5 mcmlnr.c 155

63 *sizeof(double)); 64 if (!m[i]) nrerror("allocation failure 2 in matrix()"); 65 m[i] -= ncl; 66 } 67 68 for(i=nrl;i<=nrh;i++) 69 for(j=ncl;j<=nch;j++) m[i][j] = 0.0; 70 return m; 71 } 72 73 /*********************************************************** 74 * Release the memory. 75 ****/ 76 void FreeVector(double *v,short nl,short nh) 77 { 78 free((char*) (v+nl)); 79 } 80 81 /*********************************************************** 82 * Release the memory. 83 ****/ 84 void FreeMatrix(double **m,short nrl,short nrh, 85 short ncl,short nch) 86 { 87 short i; 88 89 for(i=nrh;i>=nrl;i--) free((char*) (m[i]+ncl)); 90 free((char*) (m+nrl)); 91 }

156 Appendix C Makefile for the Program mcml

Appendix C. Makefile for the Program mcml

We present the make file used for compiling and linking the code for UNIX users.

This make file (named makefile) can be placed under the same directory as the source

code, and used by the UNIX command make.

CFLAGS = CC=cc RM=/bin/rm -rf LOCAL_LIBRARIES= -lm OBJS = mcmlmain.o mcmlgo.o mcmlio.o mcmlnr.o

.c.o: $(RM) $@ $(CC) -c $(CFLAGS) $*.c #####

all : mcml

mcml: $(OBJS) $(RM) $@ $(CC) -o $@ $(OBJS) $(LOCAL_LIBRARIES)

clean:: $(RM) mcml $(RM) mcmlmain.o

If you use ANSI Standard C compiler (acc) on SPARCstation 2, you need to change cc to

acc in the second line. Similarly, if you want to use GNU C compiler, you need to use gcc

instead of cc. If you need to use a debugger (e.g., dbx), you need to add the option -g to

CFLAGS in the first line, and recompile the source codes using the new make file. Then,

you can call the debugger (e.g., dbx mcml). To compile and link use the makefile, use:

make

Appendix D A Template of mcml Input Data File 157

Appendix D. A Template of mcml Input Data File

This is a template for the input data file. The template file is named as

"template.mci" if nobody has changed its name. You can copy this file to a new file whose

extension is preferably ".mci", and modify the parameters in the new file to solve your

specific problem. Any valid filenames without spaces are acceptable (see Section 9.1 for

detail).

#### # Template of input files for Monte Carlo simulation (mcml). # Anything in a line after "#" is ignored as comments. # Space lines are also ignored. # Lengths are in cm, mua and mus are in 1/cm. ####

1.0 # file version 2 # number of runs

### Specify data for run 1 temp1.mco A # output filename, ASCII/Binary 10 # No. of photons 20E-4 20E-4 # dz, dr 10 20 30 # No. of dz, dr & da.

2 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.3 20 200 0.70 0.01 # layer 1 1.4 10 200 0.90 1.0E+8 # layer 2 1.0 # n for medium below.

### Specify data for run 2 temp2.mco A # output filename, ASCII/Binary 20 # No. of photons 20E-4 20E-4 # dz, dr 80 80 30 # No. of dz, dr & da.

1 # No. of layers # n mua mus g d # One line for each layer 1.0 # n for medium above. 1.4 10 200 0.70 1.0E+8 # layer 1 1.0 # n for medium below.

158 Appendix E A Sample Output Data File of mcml

Appendix E. A Sample Output Data File of mcml

This is a sample output data file. To limit the length of the file, we used very small

numbers of grid elements as can be seen in the section of the input parameter in the file.

A1 # Version number of the file format.

#### # Data categories include: # InParm, RAT, # A_l, A_z, Rd_r, Rd_a, Tt_r, Tt_a, # A_rz, Rd_ra, Tt_ra ####

# User time: 0.42 sec = 0.00 hr. Simulation time of this run.

InParm # Input parameters. cm is used. sample.mco A # output file name, ASCII. 100 # No. of photons 0.1 0.1 # dz, dr [cm] 3 3 4 # No. of dz, dr, da.

2 # Number of layers #n mua mus g d # One line for each layer 1 # n for medium above 1.3 5 100 0.7 0.1 # layer 1 1.4 2 10 0 0.2 # layer 2 1 # n for medium below

RAT #Reflectance, absorption, transmission. 0.0170132 #Specular reflectance [-] 0.259251 #Diffuse reflectance [-] 0.708072 #Absorbed fraction [-] 0.0156549 #Transmittance [-]

A_l #Absorption as a function of layer. [-] 0.6418 0.06624

A_z #A[0], [1],..A[nz-1]. [1/cm] 6.4184E+00 4.2203E-01 2.4034E-01

Rd_r #Rd[0], [1],..Rd[nr-1]. [1/cm2] 7.1961E+00 3.1968E-01 1.9419E-02

Rd_a #Rd[0], [1],..Rd[na-1]. [sr-1] 5.5689E-02 6.2845E-02 3.6396E-02 2.9598E-02

Tt_r #Tt[0], [1],..Tt[nr-1]. [1/cm2] 1.5008E-01 4.8650E-02 4.0457E-02

Tt_a #Tt[0], [1],..Tt[na-1]. [sr-1] 1.0965E-03 3.5961E-03

Appendix E A Sample Output Data File of mcml 159

3.1196E-03 1.5692E-03

# A[r][z]. [1/cm3] # A[0][0], [0][1],..[0][nz-1] # A[1][0], [1][1],..[1][nz-1] # ... # A[nr-1][0], [nr-1][1],..[nr-1][nz-1] A_rz 1.7934E+02 5.9590E+00 2.9838E+00 7.4003E+00 1.3974E+00 7.6865E-01 5.5296E-01 6.5647E-01 4.7210E-01

# Rd[r][angle]. [1/(cm2sr)]. # Rd[0][0], [0][1],..[0][na-1] # Rd[1][0], [1][1],..[1][na-1] # ... # Rd[nr-1][0], [nr-1][1],..[nr-1][na-1] Rd_ra 1.7856E+00 2.0312E+00 1.8606E+00 4.0608E+00 0.0000E+00 1.0438E-01 7.3502E-02 2.4768E-01 4.3564E-03 1.2307E-02 8.2665E-04 5.0690E-03

# Tt[r][angle]. [1/(cm2sr)]. # Tt[0][0], [0][1],..[0][na-1] # Tt[1][0], [1][1],..[1][na-1] # ... # Tt[nr-1][0], [nr-1][1],..[nr-1][na-1] Tt_ra 0.0000E+00 2.6871E-02 0.0000E+00 2.5301E-01 0.0000E+00 9.0115E-05 4.2593E-02 0.0000E+00 7.1173E-03 2.2105E-02 1.0191E-02 6.0383E-04

160 Appendix F Several C Shell Scripts

Appendix F. Several C Shell Scripts

F.1 conv.bat for batch processing conv

The C Shell script "conv.bat" is used to batch process the program conv (see

Section 9.4 for description of use of conv.bat).

# Shell script for the convolution program "conv" # Feb. 2, 1992 # # Format: conv.bat filename(s) output_type # output_type includes: Rr, Ra, Az ...

# Check parm, echo the help if something is wrong. if ($#argv == 0 || $#argv >= 3) then echo 'Usage: conv.bat "input_filename(s)" output_type' echo "output_type includes: " echo "I, 3, K" echo "Al, Az, Arz" echo "Rr, Ra, Rra" echo "Tr, Ta, Tra" exit endif

# Check the second parameter. if (! ($2 =~ [Ii3Kk] || \ $2 =~ [Aa][LlZz] || \ $2 =~ [Aa][Rr][Zz] || \ $2 =~ [RrTt][RrAa] || \ $2 =~ [RrTt][Rr][Aa])) then echo "Wrong parm -- $2" echo "output_type includes: " echo "I, 3, K" echo "Al, Az, Arz" echo "Rr, Ra, Rra" echo "Tr, Ta, Tra" exit(3) endif

foreach infile ($1) # make sure the file is existent and readable. if (! -e $infile) then echo "File $infile not exist" exit(2); else if (! -r $infile) then echo "File $infile not readable" exit(2) endif

# remove the existent output files, if any. if ( -e $infile:r.$2) then rm $infile:r.$2 endif

# echo the command sequence to conv. (echo i;echo $infile;\ echo oo;echo $2;echo $infile:r.$2;echo q;echo q;echo y)\ |conv>/dev/null

end # of foreach

Appendix F Several C Shell Scripts 161

F.2 p1 for pasting files of 1D arrays

The C Shell script p1 is used to paste side by side multiple files of 1D arrays,

which are in 2 columns. If the files of 1D arrays share the same first column, p1 will not

duplicate the first column in the pasted file. If the output file is existent, then it is backed

up as the original filename appended with ".bak". The original output file is still kept as

part of the new output file. This script is particularly useful to prepare the data for

processing and presentation by some commercial plotting softwares such as KaleidaGraph.

For example, if you have three mcml output files file1.Rr, file2.Rr, and file3.Rr saved in 2

columns representing the diffuse reflectance as a function of radius r. You can combine

these three files into one file by:

p1 "file?.Rr" filex.Rrs

where filex.Rrs is the filename of the output.

# Paste 1D files (in 2 columns) together side by side. # February 7, 1992.

# Check number of arguments. if ($#argv != 2) then echo 'Usage: p1 "input_file(s)" output_fname' exit(1) endif

onintr catch # Prepare to catch interrpts.

set com = $0 set infiles = $1:q set outfile = $2

# Check validity of arguments for outfile. if ( -e $outfile) then if (! -w $outfile) then echo $outfile not writable exit(2) endif

cp $outfile $outfile.bak # Backup existent files. endif

# Setup temp files with the PID number. set outbuf = /tmp/$com:t.$$.outbuf set buf1 = /tmp/$com:t.$$.buf1 set buf2 = /tmp/$com:t.$$.buf2 set cmp1 = /tmp/$com:t.$$.cmp1 set cmp2 = /tmp/$com:t.$$.cmp2

# Run through each file, keep the results in $outbuf. foreach infile ($infiles) if (! -r $infile) then echo $infile not readable exit(3) endif

162 Appendix F Several C Shell Scripts

# Delimit by tab. awk -F" " '{print $1 "\t" $2}' $infile >! $buf1

if (! -e $outbuf) then cut -f1 $buf1 >! $cmp1 cp $buf1 $outbuf else cut -f1 $buf1 >! $cmp2 diff $cmp1 $cmp2 > /dev/null

if ($status) then # 1st rows are not the same. Paste both columns. paste $outbuf $buf1 >! $buf2; cp $buf2 $outbuf cut -f1 $buf1 >! $cmp1 else # 1st rows are the same. Paste only the 2nd column. cut -f2 $buf1 >! $buf2 paste $outbuf $buf2 >! $buf1; cp $buf1 $outbuf endif endif

end

# Copy $outbuf to $outfile if (! -e $outfile) then cp $outbuf $outfile else paste $outfile $outbuf >! $buf1; cp $buf1 $outfile endif

catch: # jump to here if interrupted rm -f $outbuf $buf1 $buf2 $cmp1 $cmp2 exit(1)

Appendix G Where to Get the Programs mcml and conv 163

Appendix G. Where to Get the Programs mcml and conv

We will eventually set up a bulletin board of our own so that you can download

the software over the network. For now, you can contact Lihong Wang, or Steven L.

Jacques using the following information to get the software.

Lihong Wang, Ph. D.

Laser Biology Research Laboratory – Box 17

University of Texas M. D. Anderson Cancer Center

1515 Holcombe Blvd.

Houston, Texas 77030

Phone: (713)745-1742

Fax: (713)792-3995

email: [email protected]

or

Steven L. Jacques, Ph. D.

Laser Biology Research Laboratory – Box 17

University of Texas M. D. Anderson Cancer Center

1515 Holcombe Blvd.

Houston, Texas 77030

Phone: (713)792-3662

Fax: (713)792-3995

email: [email protected]

Make sure that you tell us the specific machines (including brand and model) you will use

for the simulation, so that we can compile the code correctly for you. If you use IBM PC

compatibles, please also tell us whether you use a math co-processor or not.

164 Appendix H Future Developments of the Package

Appendix H. Future Developments of the Package

The release is by no means the end of the package. We plan to make at least the

following improvements. As we gather comments from users, we may consider even more

improvements.

Collimated responses in mcml

In this version (version 1.0) of mcml, the first photon interactions in the media are

scored into the first grid elements in the r direction together with the later interactions.

The first interactions are all on the z-axis, and should yield a delta function of r (Gardner

et al., 1992b) to be exact. Therefore, they should be scored separately as demonstrated by

Gardner et al.

The specular reflectance is computed analytically using Fresnel's formulas.

However, the reflected photons that are uninteracted inside the tissue are scored into the

first grid elements in the r direction of the diffuse reflectance. To be strict, these photons

should contribute to the specular reflectance rather than the diffuse reflectance although

this is small in thick tissues.

The transmittance in version 1.0 of mcml does not differentiate between diffuse

transmittance and unscattered transmittance. This problem and the problem with the

reflectance can be solved by keeping track of the number of interactions.

Best points for each grid element

As we have discussed in Section 4.3, we should use:

rb = [(n + 0.5) + 1

12 (n + 0.5) ] ∆r

instead of the center of the grid element as the coordinate of the simulated data in the nth

grid element in the r direction. In the case when the radius of a Gaussian beam is

comparable with the grid separation ∆r, this may make a considerably large difference in the convolution program conv.

Appendix H Future Developments of the Package 165

Flexible photon sources

In version 1.0 of mcml, only collimated photon beams incident on the tissue

surface are supported. Several other cylindrically symmetric sources should be able to be

incorporated without substantially modifying the program.

The convolution program conv 1.0 only supports Gaussian beams and circularly

flat beams, it can be easily adopted to convolve over arbitrary beam profiles that are

cylindrically symmetric. For beams that are not cylindrically symmetric, the convolution

still can be done but with longer integration time.

Faster sampling procedures

The sampling of the step size involves an exponential computation which is

computation intensive. Faster approaches can be used as discussed in Section 3.2.

166 References

References

Ahrens, J.H. and U. Dieter, "Computer Methods for Sampling for the Exponential and

Normal Distributions," Comm. ACM, 15, 873 (1972).

Anderson, G., and P. Anderson, "The UNIX C Shell Field Guide," Prentice-Hall (1986).

Arthur, L.J., "UNIX Shell Programming," Second Ed., John Wiley & Sons, Inc. (1990).

Born, M., and E. Wolf, "Principles of Optics: Electromagnetic Theory of Propagation,

Interference and Diffraction of Light," Sixth corrected Ed., Pergamon Press (1986).

Cashwell E.D., C.J. Everett, "A Practical Manual on the Monte Carlo Method for

Random Walk Problems," Pergamon Press, New York (1959).

Cheong W.F., S.A. Prahl, A.J. Welch, "A Review of the Optical Properties of Biological

Tissues," IEEE J Quantum Electronics, 26, 2166-2185 (1990).

Gardner, C.M., and A.J. Welch, private communication, Biomedical Eng. Program, Univ.

of Texas, Austin (1992).

Gardner, C.M., and A.J. Welch, in SPIE Proceedings, Laser-tissue Interaction III, Vol.

1646 (1992b).

Giovanelli, R.G., "Reflection by Semi-Infinite Diffusers," Optica Acta, 2, 153-162 (1955).

Hecht, E., "Optics," Second Ed., Addison-Wesley Publishing Company, Inc. (1987).

Hendricks, J.S., and T.E. Booth, "MCNP Variance Reduction Overview," Lecture Notes

in Physics, 240, 83-92 (1985).

Henyey, L.G., and J.L. Greenstein, "Diffuse Radiation in the Galaxy," Astrophys. J., 93,

70-83 (1941).

Kalos, M.H., and P.A. Whitlock, "Monte Carlo Methods, I: Basics," John Wiley & Sons,

Inc. (1986).

References 167

Keijzer, M., S.L. Jacques, S.A. Prahl, and A.J. Welch, "Light Distributions in Artery

Tissue: Monte Carlo simulations for Finite-Diameter Laser Beams," Lasers in Surg. &.

Med., 9, 148-154 (1989).

Kelley, A., and I. Pohl, "A Book on C: Programming in C," Second Ed.,

Benjamin/Cummings Publishing Company, Inc. (1990).

Lux, I., and L. Koblinger, "Monte Carlo Particle Transport Methods: Neutron and Photon

Calculations," CRC Press (1991).

MacLaren, M.D., G. Marsaglia, and T. Bray, "A Fast Procedure for Generating

Exponential Random Variables," Comm. ACM, 7, 298 (1964).

Marsaglia, G., "Generating Exponential Random Variables," Ann. Math. Stat., 32, 899

(1961).

Plauger, P.J., and J. Brodie, "Standard C," Microsoft Press (1989).

Prahl, S.A., "Calculation of Light Distributions and Optical Properties of Tissue," Ph.D.

Dissertation, Department of Biomedical Engineering, U. Texas at Austin (1988).

Prahl, S.A., M. Keijzer, S.L. Jacques, and A.J. Welch, "A Monte Carlo Model of Light

Propagation in Tissue," Dosimetry of Laser Radiation in Medicine and Biology, SPIE

Institute Series, IS 5, 102-111 (1989). (Note the typo in Eq. (10), where the denominator should be 1 – g0 + 2 g0 ξ).

Press, W.H., B.P. Flannery, S.A. Teukolsky, and W.T. Vetterling, "Numerical Recipes in

C," Cambridge Univ. Press (1988).

Spiegel, M. R., "Mathematical Handbook of Formulas and Tables," McGraw-Hill, Inc.,

(1968).

Symantec Corporation, "THINKC User Manual," Symantec Corporation (1991).

van de Hulst, H.C, "Multiple Light Scattering, Volume II," Academic Press, New York

(1980).

Wang, L.-H., and S.L. Jacques, "Hybrid Model of Monte Carlo Simulation Diffusion

Theory for Light Reflectance by Turbid Media," unpublished.

168 References

Wilson, B.C., and S. L. Jacques, "Optical Reflectance and Transmittance of Tissues:

Principles and Applications," IEEE J. of Quant. Electronics, 26 (12), 2186-2199 (1990).

Witt, A.N., "Multiple Scattering in Reflection Nebulae I. a Monte Carlo approach," The

Astrophysical J. Supp. Series, 35, 1-6 (1977).

Wyman, D. R., M. S. Patterson, and B. C. Wilson, "Similarity relations for anisotropic

scattering in Monte Carlo simulations of deeply penetrating neutral particles," J. Comput.

Phys., 81, 137-150 (1989a).

Wyman, D. R., M. S. Patterson, and B. C. Wilson, "Similarity relations for the interaction

parameters in radiation transport," Appl. Opt., 28, 5243-5249 (1989b).

Index 169

Index

A, 37

A_l, 37, 95

A_rz, 36, 96

A_z, 36, 95

absorption, 4

absorption, 15, 24, 37

absorption coefficient, 35

absorption coefficient, 4, 5, 25

Absorption coefficient, 92

absorption-only, 30

angularly resolved diffuse reflectance and

transmittance, 48

anisotropy, 16

anisotropy, 4

anisotropy factor, 35

ANSI C, 87

ANSI Standard C, 87, 91

ANSI Standard C, 32

Apple Edit, 91

arrows, 14

ASCII format, 92

AXUM, 96

azimuthal angle, 15

background job, 95

background processes, 95

batch process, 96

Bessel function, 71, 76

binary files, 91

binary tree, 74

binary tree, 77

bit bucket, 95

Boolean, 35

bugs, 97

buried isotropic photon sources, 110

Cartesian coordinate system, 5

cell, the last, 22

cflow, 41

CHANCE, 33

comment lines, 91, 95

compress, 90

computation results, 47

computation time, 52

constants, 33

contour, 66

Contour, 83

conv, 80

convolution, 67

convolution, 67

convolution error, 85

convolve, 96

coordinate systems, 5

cos_crit0, 35

cos_crit1, 35

COS90D, 33

COSZERO, 33

critical angles, 35

cross the boundary, 38

cumulative distribution function, 7

cylindrical coordinate system, 5

da, 36

Data structures, 34

dead, 34

deflection angle, 15

delta function, 30

depth resolved internal fluence, 50

diffuse reflectance, 22, 37

170 Index

diffuse reflectances, 63, 81, 84

diffuse transmittance, 48

diffuse transmittance, 19

dimensional step size, 14

dimensionless step size, 14

directional cosines, 17

directional cosines, 11, 34

divergence, 71

dr, 36

duplicated filenames, 43

dynamic allocation, 37

dz, 36

editor, 91

eject, 88

electronic mail, 89

EMACS, 91

empirical formulas, 55, 58

empirical formulas, 62

Ethernet, 97

execution, 93

extended trapezoidal integration, 31

extended trapezoidal integration, 74

extraction, subset, 96

extrapolations, 76

file expansion, 97

file format, 92

file of input data, 91

file of output data, 95

File version, 92

filename for data output, 36

first photon interactions, 30

flat beam, 69

flat beams, 72

flow graph, 41

flow of the program, 32

flowchart, 38

fluences, 65, 82, 85

folder, 96

folder, 94

free path, 12

Fresnel reflectances, 12

Fresnel's formulas, 18

FTP, 91, 97

g, 35

Gaussian beam, 69

glass, 12

glass layer, 14, 38

GNUCC, 33

grid line separations, 92

grid system, 5

Henyey and Greenstein, 15

hybrid model, 55

IBM PC compatibles, 94

implicit photon capture, 11

impulse response, 67

impulse response, 66

index range of the array, 37

input data file, 91, 97

input parameters, 35, 95, 97

InputStruct, 36

instructions, 91

integers, 97

integral limits, 77, 79

integrand evaluation, 76, 78

intensity profile, 68

interaction coefficient, 4, 12, 40

interpolations, 76

invariance, 67

isotropic photon sources, 110

isotropic photon sources, buried, 110

job, background, 95

KaleidaGraph, 161

Index 171

KaleidaGraph, 96

Kermit, 91

KERMIT, 97

last cells, 22

Launch photon, 38

layer, 34

Layer parameter, 93

layerspecs, 36

LayerStruct, 35

linear and invariant, 67

linear approximations, 27

linear linked list, 43

linearity, 67

logarithmic operation, 45

log-log plot, 55

log-log scale, 55

long int, 36

Macintosh, 94

mail, 90

mails, 89

make file, 32

matched boundaries, 53

matched boundary, 47

Max, 78, 79

Max, 72

mean free path, 93

mean free path, 13

Mean free path, 2

mean free path, transport, 52, 111

memory, iii, 37, 43, 93

memory, release, 43

Microsoft Word, 91

Min, 78, 79

mismatched boundaries, 58

mismatched boundary, 48

mismatched boundary, 11

MockWrite, 91

modem, 97

modified Bessel function, 71

modify the program, 110

Monte Carlo, 1

mua, 35

multi-layered tissue, 4

multi-layered tissues, 62

multiple runs, 93

multiple simulations, 43

mus, 35

n, 35

na, 36

nesting depth, 41

network, 91

Norton editor, 91

nr, 36

num_layers, 36

num_photons, 36

Number of grid elements, 93

Number of layers, 93

number of photon packets, 36

Number of photon packets, 92

Number of runs, 92

numerical computation, 73

nz, 36

observation point, 68

operating system, 91

out_fformat, 36

out_fname, 35

output data files, 95

Output filename, 92

OutStruct, 37

overflow, 77

overflow, 22

partial reflection approach, 19

172 Index

PARTIALREFLECTION, 33

path, search, 94

photon absorption, 15

photon fluence, 25

photon packet, 11, 34

photon propagation, 11

photon scattering, 15

photon termination, 20

PhotonStruct, 34

physical quantities, 4, 22

position of the photon packet, 14

probability density function, 7

probability of interaction, 12

process, background, 95

profiler, 44, 46

pseudo-random number generator, 7

radially resolved diffuse reflectance, 49

random variable, 7

RAT, 95

Rd, 36

Rd_a, 36, 95

Rd_r, 36, 95

Rd_ra, 36, 96

real time, 43

reflectance, 18

reflection at boundary, 17

reflection at interface, 19

refractive index, 35

Refractive index, 93

release the memory, and continues

execution, 43

resolution, 92

roulette, 36

roulette, 20, 41

Rsp, 36

s, 34

sampling random variables, 7

scattering coefficient, 35

scattering coefficient, 4, 5, 92

scattering function ), 15

script file, 96

search path, 94

search path, 88

semi-infinite turbid medium, 47

shell programming, 96

short, 35

similarity relations, 50, 111

sleft, 34

Snell's law, 18

software installation, 87

solid angle, 23, 48

source, 68

source code, 32

source or error, 79

source point, 68

space lines, 91, 95

specular reflectance, 37, 95

specular reflectance, 12, 22

spherical coordinate system, 5

standard errors, 47

STANDARDTEST, 33

step size, 12, 35

STRLEN, 33

subset extraction, 96

subset of the output data, 96

tar, 90

Taylor series, 28

template file, 91

termination, 20

text editors, 91

text format, 91

THINKCPROFILER, 33

Index 173

threshold weight, 36

time of computation, 52

timer, 97

time-shared system, 53

timing profile, 43

Timing profile, 45

tissue/tissue interface, 19

total absorption, 95

total diffuse reflectance, 95

total diffuse reflectance, 37, 47, 48

total diffuse reflectance and

transmittance, 23

total diffuse transmittance, 48

total transmittance, 95

total transmittance, 47

trajectory, 2

transformation of variables, 69

transmission at boundary, 17

transmission at interface, 19

transmittance, 18

transmittance, 22

transmittances, 63

transmittances, 82, 84

transport mean free path, 52

transport mean free path, 111

trapezoidal rule, 73

Tt, 37

Tt_a, 37, 95

Tt_r, 37, 95

Tt_ra, 37

Tt_ra, 96

uncompress, 89

unit of length, 5

UNIX, 94

unscattered transmittance, 19

unscattered transmittance, 48

user time, 43, 95, 97

user times, 52

uudecode, 89

uuencode, 90

ux, 34

uy, 34

uz, 34

variance, 92

variance reduction technique, 11

vi, 91

w, 34

wave phenomenon,, 2

weight, 11

wild cards, 97

Word, 91

Wth, 36

x, 34

y, 34

z, 34

z0, 35

z1, 35

__MACOSX/FTP-Uploads/ch04Convolution/._Mcman.pdf

FTP-Uploads/ch04Convolution/Mcman.txt

8. Installing mcml and conv This chapter provides the instructions on how to install the software. software installationSince both mcml and conv are written in ANSI Standard C, they in principle should be able to be compiled on any computer systems that support ANSI C. Subject to the computer systems available to this laboratory, we will only provide the executables for Sun workstations, IBM PC compatibles, and Macintoshes. On Sun SPARCstations 2, we have compiled the mcml and conv using the ANSI C (acc). On IBM PC compatibles, we used Microsoft QuickC. And on Macintoshes, we used Symantec THINK C. We will provide the source code, users can feel free to compile them on their computer systems. Consult corresponding manuals for information on how to compile the code. As Monte Carlo simulations are computationally intensive, we suggest that you use workstations such as Sun SPARCstations on which you can submit background jobs and which provide high speed computation. The convolution program is also more pleasant to use if you have a fast computer, although it is not as computation-intensive as Monte Carlo simulations. 8.1 Installing on Sun workstations The distribution disk is an IBM format double density 3 1/2" disk, which Sun SPARCstation 2 should be able to read. The disk includes three directories: mcmlcode, convcode, and Sun. The directory mcmlcode includes all the source code of mcml and the makefile used for acc. The directory convcode includes all the source code of conv and the corresponding makefile. You need to modify the makefiles for other compilers (See Appendix C). The directory Sun includes all the executables, a template file of mcml input (template.mci), a sample mcml output file (sample.mco), and a short manual (mcmlconv.man) which is Chapter 8-10 of this manual. To install the source code of mcml and conv, make two directories called mcml and conv respectively. Then, while in the directories mcml or conv, copy the files under mcmlcode and convcode to the directories mcml and conv correspondingly using command mcopy, e.g.: mkdir mcml cd mcml mcopy -t "a:mcmlcode/*" . where the option "-t" means text transfer and the period "." represents the current directory. More information can be retrieved using "man mtools". Use this command to copy the text files in directory Sun to your working directory. To install the executables, copy the executables to the sub directory ~/bin under your home directory using the command mcopy without options. Then, put the directory ~/bin under the search path in .cshrc or .login if you are using C Shell. Consult manual if you are using other shells. Having finished copying, you can eject the disk using the command eject. If your Sun workstation does not have a floppy drive, you can transfer the files through a networked IBM PC or a compatible. If you have an electronic mail address, we can also send the package to you through mail. 8.2 Installing on IBM PC compatibles For IBM PC's or compatibles, the distribution disk is a double density 3 1/2" disk. An alternative 5 1/4" disk can be sent upon request. The disk includes three directories: mcmlcode, convcode, and IBMPC. The directory mcmlcode includes all the source code of mcml. The directory convcode includes all the source code of conv. The directory IBMPC includes all the executables, a template file of mcml input (template.mci), a sample mcml output file (sample.mco), and a short manual (mcmlconv.man) which is Chapter 8-10 of this manual. The executables include mcml.exe and conv.exe. The code was compiled and linked using Microsoft QuickC 2.5. The executables will be able to detect whether your computer has math coprocessor, and take advantage of the math coprocessors if they are present. The simplest installing process would be: xcopy a: c:/s where /s means copying the subdirectories if they are present. This command will make three directories: mcmlcode, convcode and IBMPC, and copy all the files in each directory. If you want to be able to execute the programs under any directory, you should put the directory IBMPC in the search path. The search path can be changed in the file autoexec.bat. 8.3 Installing on Macintoshes For Macintoshes, the distribution disk is a double density 3 1/2" disk. The disk includes three folders: mcmlcode, convcode, and Mac. The folder mcmlcode includes all the source code of mcml. The folder convcode includes all the source code of conv. The folder Mac includes all the executables, a template file of mcml input (template.mci), a sample mcml output file (sample.mco), and a short manual (mcmlconv.man) which is Chapter 8-10 of this manual. The executables include mcml.fpu, conv.fpu, mcml.020, conv.020, mcml.000, and conv.000 for different types of computers as discussed subsequently. To install the source code of mcml and conv, make two folders called mcml and conv respectively. Then, copy the files under mcmlcode and convcode to the folders mcml and conv correspondingly. Before you install the executables, you need to know what kind of Macintosh you are using. You can test the following conditions to decide which executables to use: A. MC68040 B. MC68020 or MC68030 C. MC68881 or MC68882 If your Macintosh meets condition A, or conditions B and C, you should copy the executables with extensions ".fpu". If your Macintosh meets condition B only, you should copy the executables with extensions ".020". Otherwise, you should use the executables with extensions ".000". We suggest that you remove the extensions of the executables on your hard drive to keep consistency with the manual. 8.4 Installing by Electronic Mail For these users who have electronic mail access on UNIX machines, we can deliver the software package through electronic mails. The package is archived using the command tar, compressed using the command compress, then encoded using the command uuencode before it is mailed out using the mail utilities. After you receive the mail, you need to do the following. 1. Save the mail as a file, e.g., mc.mail. 2. Decode the file (mc.mail) to get a file named mc.tar.Z using uudecode mc.mail 3. Uncompress the file mc.tar.Z to get the file mc.tar uncompress mc.tar.Z 4. Unarchive the file mc.tar to get the package using: tar -xvfo mc.tar At this moment, you should have three directories under the working directory. They are mcmlcode, convcode, and Sun, or IBMPC, or Mac. If you ordered a Sun version of the package, you only need to put the executables under the proper directory., e.g., ~/bin (see Section 8.1). If you ordered an IBM PC version or a Mac version of the package, you need to transfer the files to your local computer using FTP or modem. Then refer to Section 8.2 or 8.3 for details. It is appropriate to describe in more detail how we send the package through electronic mails which is exactly the opposite of the above procedure. We put the package in a working directory which include three subdirectories: mcmlcode, convcode, and Sun, or IBMPC, or Mac. Then: tar -cvf mc.tar compress mc.tar uuencode mc.tar.Z mc.tar.Z > mc.mail mail your_address In the mail utility, you can add in any messages in the beginning of the mail, then you need to use the command r to read in the file mc.mail. Then, you can send the file by typing a period "." and a return in a new line (see the manual page of mail). 9. Instructions for mcml This chapter describes the actual instructions to use mcml. Macintoshes, IBM PC compatibles and UNIX machines are used as examples of computer systems, although mcml can execute on any computer systems that support ANSI Standard C. The reader is assumed to be familiar with the operating system and comfortable with at least one of the text editors on the computer system to be used to execute mcml. Three steps involved in the Monte Carlo simulation using mcml are included in the following sections: preparing the input data file, executing the program mcml with the input data file, processing the output data in the data files named in the input data file. We will also show some known bugs. 9.1 File of input data The first step to do the simulation using mcml is to prepare an input data file (e.g., "filename.mci")file of input data. Any valid filenames on your system without spaces will be acceptable, but extension ".mci" is recommended. In ANSI C, spaces are used as separators. Therefore, filenames with spaces may not be accepted by mcml, although they are allowed by some operating systems themselves such as the Macintosh System. We will use "filename.mci" as an example in the following discussions. This input data file may be edited with any text editors such as Apple Edit, MockWrite or Microsoft Word Wordon Macintoshes, Norton editor NE or Microsoft Word on IBM PC compatibles, vi editor or EMACS on UNIX systems. However, if you use word processors like Microsoft Word to edit the file, make sure that you save the file in text format since mcml does not accept binary files as input. If you are using the UNIX system and are uncomfortable with vi or other editors available on UNIX, you can use editors on your personal computer, then transfer the file using Kermit if you use modem or FTP if your personal computer is on a network. Make sure to use ASCII or text mode when you transfer this file. The best way to write an input data file is to make a copy of the template file called "template.mci" (See Appendix D), then modify the parameters in the file. The input data file is organized line by line. All parameters must be in the right order. The lines with parameters in order must also be in order themselves. However, feel free to insert comment lines or space lines in between to make the file more readable. Comment lines start with the symbol "#". The symbol "#" can also be used after the parameters in a line to mark the start of comments. The parameters in the input data file are read by mcml line by line. If there are multiple parameters in a line, use tabs or spaces to separate them. A tab is preferred, because it aligns the parameters for better readability. All dimensional quantities are in cm or derived from cm. The thickness of each layer is in cm. The grid line separations are also in cm. Absorption coefficient and scattering coefficient are in 1/cm. Each line of the input file is explained in the order that they appear in the input data file as follows. 1. File version of the input data file. Always use "1.0" for now. 2. Number of runs (integer). Each run is an independent simulation. You can specify any number of runs, which is not subject to memory limit. Make sure you use an integer instead of a floating point number for this parameter, e.g., 5 instead of 5.0. 3. Output filename and file format. Extension ".mco" is recommended for the output filenames, e.g., "output1.mco". The program mcml currently only supports ASCII format, therefore always use "A" as the second parameter in this line. Make sure that you use different output filenames if you have multiple runs in an input data file, although mcml checks for this mistake. What is more important is that the filenames should not be the same as the names of existent ones unless you want to overwrite the existent files on purpose. Since the program mcml does not check this error, you will lose the existent files. 4. Number of photon packets to be traced (integer). 5. Separations (in cm) between grid lines in z and r direction of the cylindrical coordinate system. These are floating point numbers. Both z and r originate from the photon incident point on the surface of first layer, and the z axis points down into the turbid medium. Make sure these parameters are large enough to give you an acceptable variance, and small enough to give you an acceptable resolution. These parameters should be determined coordinately with the number of photons to achieve both accuracy and resolution. Also note that users should try to choose grid size in the z direction so that grid boxes do not cross tissue-tissue interfaces or boundaries (see Section 9.5). 6. Number of grid elements (integers) in the z, r directions of the cylindrical coordinate system and in the alpha direction, where alpha is the angle spanned between the photon exiting direction and the surface normal. Since the angle always covers 0 through 90 degrees, the angular separation is 90 degrees divided by the number of angular grid elements specified in this line. Be careful with this line, if the numbers are too large, the output file will be very big because 2D arrays are written into the output file. If you do not need to resolve one of the directions (z or r) or the angle, use 1 (not 0) for that parameter. Make sure to use integers for these three parameters. 7. Number of layers (integer). This number does not include the ambient media above or below the tissue. 8. Refractive index for the top ambient medium above the first layer (e.g., 1.0 for air). 9. Layer parameter lines. One line for each layer. In each line are the refractive index, the absorption coefficient (1/cm), the scattering coefficient (1/cm), the anisotropy factor, and the thickness (cm). To simulate semi-infinite tissue, use a very large thickness (e.g., 1E8 cm) compared with the mean free path of the tissue. 10. Refractive index for the bottom ambient medium below the last layer (e.g., 1.0 for air). 11. Repeat lines 3 through 10 for each additional run if you have multiple runs. Note: Two points are worth noting. The only limit to the number of grid elements and layers is the amount of memory allocated to mcml in your system because the arrays are dynamically allocated according to these parameters. Do not use floating point numbers for the integers. Otherwise, the program may interpret them incorrectly. However, you may use integers for floating point numbers, e.g., 100 instead of 100.0. 9.2 Execution Once the input data file is prepared, the program mcml can be executed using the input data file.execution During the execution, the program mcml will report an output message which gives the number of photons remaining in the simulation, the number of runs left, and the time of ending the job. The first report is after 10 photon packets are traced, then it is updated when every 1/10 of the total number of photon packets are traced. The methods of execution are slightly different on different operating systems. Macintosh To run mcml on Macintosh System 6, you have to copy or move the executable mcml to your working folder where the input data file resides, then double click on the mcml icon to start the program. The program mcml will prompt for the input data filename, which is entered through the keyboard. If the input data file cannot be found, the program will prompt you again until it finds the file or a period "." is typed, where "." is used to abort the program. If you use Macintosh System 7, you may use an alias of mcml instead of a copy of it. IBM PC compatibles For IBM PC compatibles, make sure that the directory with mcml is in the search pathpath, search, which can be checked by typing the command "path" or the file "autoexec.bat". To run mcml with the input data file as a command parameter under DOS command prompt, type: mcml filename.mci If you want to save the output message as a file (e.g., message.out), type: mcml filename.mci > message.out which redirects the output message to the file "message.out". To run mcml in the interactive mode, type the following command without input data filename: mcml Then, the program mcml will prompt for the input data file. UNIX On a UNIX system, you should place the executable mcml in a directory that is in the search path. The directory ~/bin is a good choice. The search path can be found and modified in the file ".cshrc" if you are using C Shell or the file ".login". The three ways of invoking mcml under DOS can be used under UNIX operating systems. Moreover, if you wish to discard the messages during the execution, use the command: mcml filename.mci > /dev/null which redirects the output to the "bit bucket" (/dev/null). You can also simply submit a background job job, backgroundusing: mcml filename.mci > /dev/null & Refer to your UNIX manual for how to inquire about the status of a background job. If you are still in the same session, the command "jobs" can be used in C Shell. Otherwise, you should use the UNIX command "ps" to check for background processesprocess, background. You can also directly look for the output files to check if the job is done. 9.3 File of output data When the job is completed, the results will be written into the output data files file of output dataas you named in your input data file. A sample output data file is shown in Appendix E. The output data files can be read with any text editors if they are ASCII as a result of using "A" for the file format in the input data file. They may be big if your numbers of grid elements are large. The contents of output files are self explanatory. The same policy for the input data file is used for the output data file, that is, comment lines starting with a symbol "#" and space lines are written to the file for clarity. The first line is used for file type identification when the file is read by other applications. Then, the user time spent on the simulation is reported in a comment line. Then, a few categories of data are reported sequentially in the following order: pure numbers, 1D arrays and 2D arrays. The definitions of the output data can be found in Chapter 4. The category "InParm" reports all the input parameters specified in the input data file again so that the output file is a complete reference and the input parameters may also be double checked against any errors in the input data file. The category "RAT" reports the specular reflectance, the total diffuse reflectance, the total absorption, and the total transmittance. The category "A_l" is the absorption as a function of layer. The category "A_z" is the absorption as a function of depth z. The categories "Rd_r" and "Rd_a" are the diffuse reflectances as a function of radius r and angle alpha respectively. The categories "Tt_r" and "Tt_a" are the transmittance as a function of radius r and angle alpha respectively. The 2D arrays A_rz, Rd_ra, and Tt_ra are then reported. The category A_rz is the absorption as a function of depth z and radius r. The categories Rd_ra and Tt_ra are correspondingly the diffuse reflectance and transmittance as a function of radius r and angle alpha. The name of each category is written before the data, such that the data can be easily identified. The units for these data were discussed in Chapter 4. 9.4 Subset of output data Sometimes, only a subset of the output data is needed for presentation or processing. For example, we may need to print a 1D array into a file in XY format, namely two columns of data, or a 2D array in XYZ format. These files can then be read into some commercial applications such as AXUM on IBM PC compatibles and KaleidaGraph on Macintoshes. This subset extraction extraction, subsetcan be done using another program that we have partially completed -- conv. The program conv is intended to read in the output data file of mcml that gives responses of infinitely narrow photon beam, and convolve the output data if the responses of finite size beam are to be computed. The program conv can output the original data or the convolved data in various formats. The convolution part of the program conv has not been finished, although it can be used to extract subsets of the original output data. The program conv is made to be interactive. After the program is invoked, the menu system will direct the data input, output, or process. On Macintosh, copy or move the program conv to your working folder. Start conv by double clicking on the icon. On IBM PC compatibles or UNIX machines, invoke the program conv by typing: conv Follow the menu to input an mcml output file (e.g., "filename.mco"). Then, output specific data to new files. However, for the sake of efficiency, we wrote a C Shell script file "conv.bat" for UNIX users. For shell programming, refer to Anderson et al. (1986) or Arthur (1990). A similar file can be written on MS-DOS operating system. The file "conv.bat" is used for fast batch process. For example, if there are several mcml output files named "outfile1.mco", "outfile2.mco"... "outfilen.mco", and you need to select the diffuse reflectance as a function of radius r of these mcml output files on a UNIX system, then you can use the command: conv.bat "outfile*.mco" Rr This command takes two arguments. The first one gives the mcml output files to be processed. If wild cards, such as * or ?, are used, the argument has to be within quotes to prevent immediate file expansion. The second argument gives the type of subsets to be extracted. In this case, it is the diffuse reflectance as a function of r. The files of the subsets will be named as "outfile*.Rr". In each of these output files, there are two columns, the first one is the radius, and the second one is the reflectance. To check the complete usage of "conv.bat", type "conv.bat" on command line. More examples are: conv.bat "outfile*.mco" Az for 1D absorption as a function of z. In each of the output files of this command, there are two columns representing z and the internal absorption respectively. conv.bat "outfile*.mco" Azr for 2D absorption as a function of z and r. In each of the output files of this command, there are three columns representing z, r, and the internal absorption respectively. If you use UNIX to do the simulation and want to present the results using Macintosh or IBM PC compatibles, transfer the smaller subset files using KERMIT if you use modem or FTP if you use Ethernet. 9.5 Bugs of mcml 1. Users have to be careful with several known bugs about the program mcml version 1.0. If a grid element crosses a medium interface, e.g., a glass/tissue interface, the photon absorption within this grid element is considered to be the absorption in the medium where the center of the grid element is located. Therefore, if the center is on the side of the glass, mcml may report small absorption in the glass. Sometimes, this problem may be avoided by choosing the z-grid system carefully so that the boundaries of elements align with the layer interfaces. 2. The user time of a simulation can be reported as zero if the simulation is long enough to overflow the timer (See the function clock() in the file "mcmlmain.c"). 3. The input parameters in the input data file have to be in the order as specified. Furthermore, you have to use integers for number of photon packets, number of layers, and number of grid elements in the input data file. If floating point numbers are inadvertently used, mcml can not detect the error and may read in the wrong parameters. If you find any new bugs, please report to us using the information in Appendix G -- "Where to Get the Program mcml". It is very important that you provide us enough information about the bug so that we can reproduce it. 10. Instructions for conv This chapter describes the instructions to use the program conv, which is used to convolve the impulse responses of mcml over incident beams of finite size. This program reads the output of mcml, then convolves the impulse responses according to the user specified incident beams. The program can output the original data from mcml or the convolved data in various ASCII formats as discussed subsequently. 10.1 Start conv To start conv on IBM PC compatibles or UNIX machines, invoke the program conv by typing: conv To use conv on Macintoshes, copy or move the program conv to your working folder. Then, double click the conv icon to start it. If you are using System 7, you may take advantage of the alias mechanism. 10.2 Main menu of conv Once conv is started, it is in the main menu of the program after showing some information about the program. In the main menu, the program prompts for a command as: > Main menu (h for help) => To show all the available command, type "h" and return key. It will show you the following information and prompt for the next command. You only need to show the help information when you forget the commands. i = Input filename of mcml output b = specify laser Beam r = convolution Resolution. e = convolution Error. oo = Output Original data oc = Output Convolved data co = Contour output of Original data cc = Contour output of Convolved data so = Scanning output of Original data sc = Scanning output of Convolved data q = Quit * Commands in conv are not case-sensitive > Main menu (h for help) => Each command will be introduced subsequently. 10.3 Command "i" of conv You have to provide the filename of the mcml output to conv. This can be done by typing "i" and return key in the main menu prompt, then type in the filename of the mcml output. For example: > Main menu (h for help) => i Input filename of mcml output(or . to quit): example.mco > Main menu (h for help) => The program returns to the main menu automatically. If the file cannot be located or opened, the program will prompt you to type in another filename. You can also type "." and return key to quit inputting the filename. If the file is not the output of mcml, the program will quit to the operating system. You need to start the program again. 10.4 Command "b" of conv You need to specify the type and parameters of the incident beam. In version 1.0 of conv, only Gaussian beams and circularly flat (rectangular) beams are supported. To enter the incident beam, use command "b". Then you have to choose from "f" for flat beam, "g" for Gaussian beam, or "q" to quit this command. If you choose either flat beam or Gaussian beam., conv asks the total energy and the radius of the beam. For example: > Main menu (h for help) => b Beam profile:f=flat, g=Gaussian. q=quit: f Total energy of the flat beam [J]: 1 Radius of the flat beam [cm]: .1 Total power: 1 J, and radius: 0.1 cm. > Main menu (h for help) => It returns to the main menu automatically. Although we specify units of energy for the incident beam, you can substitute units of power throughout the program. To get reliable results, the radius should be much larger than the grid separation in the r direction of the original mcml output, and much less than the total covered radius by the grid system in the r direction of the original mcml output. As a rule of thumb, the radius should be in the range between about 3 times the grid separation in the r direction and the total grid coverage in the r direction minus the maximum radius of observation (see Eqs. 7.23 & 7.24 in Section 7.4). 10.5 Command "r" of conv This command is used to change the grid separation and the number of grid elements in the r direction for the convolution. Since they take the values of the mcml output as the default, you do not have to enter this command if you do not want to change them. The maximum convolution radius should not be larger than that of the original mcml output to get reliable results. For example: > Main menu (h for help) => r Current resolution: 0.01 cm and number of points: 50 Input resolution in r direction [cm]: .02 Input number of points in r direction: 20 Resolution: 0.02 cm and number of points: 20 > Main menu (h for help) => Note that if the number of points is chosen too large, the program can exit due to the lack of memory. This is a bug in the current version of conv. 10.6 Command "e" of conv The integration is computed iteratively. The iteration stops when the difference between the new estimate and the old estimate of the integration is a small part of the new estimate. This small ratio can be controlled by users using command "e". It ranges between 0 to 1. Small values would give better precision but longer computation time and vice versa. Normally, 0.001 to 0.1 is recommended. The default value is 0.1. For example: > Main menu (h for help) => e Relative convolution error Current value is 0.05 (0.001-0.1 recommended): .01 Special attention has to be paid to this command. The convolution results may have weird discontinuities if the allowed convolution error is too high (see Fig. 7.11), and the convolution process may take too long if the convolution error is too low. The rule of thumb is that you choose the lowest convolution error that does not make the convolution too long to compute. If the convolution results still have any discontinuities which should not be there, you need to decrease the convolution error and redo the convolution. 10.7 Command "oo" of conv After you input the filename of the mcml output , you can output the original data of the mcml output with various formats. One of the formats can be obtained by the command "oo". For example: > Main menu (h for help) => oo > Output mcml data (h for help) => h I = Input parameters of mcml 3 = reflectance, absorption, and transmittance AL = absorption vs layer [-] Az = absorption vs z [1/cm] Arz = absorption vs r & z [1/cm3] Fz = fluence vs z [-] Frz = fluence vs r & z [1/cm2] Rr = diffuse reflectance vs radius r [1/cm2] Ra = diffuse reflectance vs angle alpha [1/sr] Rra = diffuse reflectance vs radius and angle [1/(cm2 sr)] Tr = transmittance vs radius r [1/cm2] Ta = transmittance vs angle alpha [1/sr] Tra = transmittance vs radius and angle [1/(cm2 sr)] K = Keijzer's format Q = Quit to main menu * input filename: example.mco > Output mcml data (h for help) => At this point, you can output various physical quantities by inputting the subcommands, which can be listed by command "h" as shown above. After you type the command, the program will ask you for the output filename. The exact physical meanings of these physical quantities can be found in Chapter 4. The command "i" outputs the input parameters of mcml to a file. The command "3" outputs three quantities to a file including specular reflectance, total diffuse reflectance, absorption probability, and total transmittance, which are actually four numbers. The command "Al" outputs the absorption probability as a function layer to a file. The command "Az" outputs the absorption as a function of z coordinate whose dimension is cm-1. The command "Arz" outputs the absorption probability density as a function of r and z whose dimension is cm-3. The commands "Fz" and "Frz" output the results of the commands "Az" and "Arz" divided by the absorption coefficients. The command "Rr" outputs the diffuse reflectance as a function of r whose unit is cm-2. The command "Ra" outputs the diffuse reflectance as a function of the exit angle a, whose dimension is sr-1. The command "Rra" outputs the diffuse reflectance as a function of r and a, whose unit is cm-2 sr- 1. Similarly, the commands "Tr", "Ta" and "Tra" are the corresponding commands for the transmittance. The command "K" is used to convert the format of the mcml output to the format of Marleen Keijzer's convolution program (in PASCAL on Macintoshes) which was used by our group before the program conv was written. This command is only useful if you have Marleen Keijzer's program. The command "q" will return the program to the main menu. For 1D arrays, the outputs are in two columns. The first column gives the independent variable, and the second column gives the physical quantities. For example, the output of the command "Rr" will have two columns. The first column gives the radius in cm, and the second column gives the diffuse reflectance in cm-2. For 2D arrays, the outputs are in three columns. The first two columns give the first and the second independent variables, and the third column gives the physical quantities. For example, the command "Arz" will give three columns. The first two columns give r and z in cm respectively, and the third column gives the absorption probability density in cm-2 sr-1 as a function of r and z. An example is shown as follows: > Output mcml data (h for help) => Rr Enter output filename with extension .Rr (or . to quit): example.Rr > Output mcml data (h for help) => This command will output the diffuse reflectance as a function of r to the file named "example.Rr". 10.8 Command "oc" of conv After you input the filename of the mcml output and specify the incident photon beam, you can output the convolved data with various formats. One of the formats is writing data in columns, which can be obtained using the command "oc". For example: > Main menu (h for help) => oc > Output convolved data (h for help) => h Arz = absorption vs r & z [J/cm3] Frz = fluence vs r & z [J/cm2] Rr = diffuse reflectance vs radius r [J/cm2] Rra = diffuse reflectance vs radius and angle [J/(cm2 sr)] Tr = transmittance vs radius r [J/cm2] Tra = transmittance vs radius and angle [J/(cm2 sr)] Q = Quit to main menu * input filename: example.mco > Output convolved data (h for help) => At this point, you can output various physical quantities by inputting the subcommands, which can be listed by command "h" as shown above. After you type the command, the program will ask you for the output filename. The exact physical meanings of these physical quantities can be found in Chapters 4 and 7. The command "Arz" outputs the absorption energy density as a function of r and z whose dimension is J cm-3. The command "Frz" outputs the results of the command "Arz" divided by the absorption coefficients, which is the fluence in J cm-2. Since we consider steady- state responses only in mcml and conv, you can systematically replace the energy [Joules] with power [Watts] in conv. The command "Rr" outputs the diffuse reflectance as a function of r whose unit is J cm-2. The command "Rra" outputs the diffuse reflectance as a function of r and a, whose unit is J cm-2 sr-1. Similarly, the commands "Tr" and "Tra" are the corresponding commands for the transmittance. The command "q" will return the program to the main menu. For 1D arrays, the outputs are in two columns. The first column gives the independent variable, and the second column gives the physical quantities. For example, the output of the command "Rr" will have two columns. The first column gives the radius in cm, and the second column gives the diffuse reflectance in J cm-2. For 2D arrays, the outputs are in three columns. The first two columns give the first and the second independent variables respectively, and the third column gives the physical quantities. For example, the command "Arz" will give three columns. The first two columns give r and z in cm respectively, and the third column gives the absorption energy density in J cm-2 sr-1 as a function of r and z. An example is shown as follows: > Output convolved data (h for help) => Rr Enter output filename with extension .Rrc (or . to quit): example.Rrc > Output convolved data (h for help) => This command will output the diffuse reflectance as a function of r to the file named "example.Rrc". 10.9 Command "co" of conv After you input the filename of the mcml output, you can output the original data of the mcml output with various formats. One of the formats for 2D arrays is writing data in contour lines. Every contour line will be given by two columns. This format can be obtained using the command "co" standing for "contours of the original data". Then, the output file can be imported to some plotting software such as KaleidaGraph on Macintoshes, and the contour lines can be drawn. For example: > Main menu (h for help) => co > Contour output of mcml data (h for help) => h A = absorption vs r & z [1/cm3] F = fluence vs r & z [1/cm2] R = diffuse reflectance vs radius and angle [1/(cm2 sr)] T = transmittance vs radius and angle [1/(cm2 sr)] Q = Quit to main menu * input filename: example.mco > Contour output of mcml data (h for help) => Since only the 2D arrays need to be presented in contour lines, there are only four physical quantities. The command "A" outputs the absorption probability density as a function of r and z whose dimension is cm-3. The command "F" outputs the probability fluence as a function of r and z in cm-2. The commands "R" and "T" output diffuse reflectance and transmittance as a function of r and a in cm- 2 sr-1. After you input one of the commands, the program will prompt for the output filename and the isovalues for the contour output. The value range of the physical quantity is shown so that valid isovalues can be provided by users. You can enter as many isovalues as you want. System memory is the only thing that limits the number of isovalues. Stop entering isovalues by inputting a period ".". For example: > Contour output of mcml data (h for help) => A Enter output filename with extension .iso (or . to quit): example.iso The range of the value is 0.156280 to 3294.800000. Input an isovalue or . to stop: 1000 Input an isovalue or . to stop: 100 Input an isovalue or . to stop: 10 Input an isovalue or . to stop: 1 Input an isovalue or . to stop: . > Contour output of mcml data (h for help) => The output file of this example will have eight columns, each pair of columns describe one contour line. The values of the contour lines are 1000, 100, 10, and 1 respectively. 10.10 Command "cc" of conv After you input the filename of the mcml output and specify the incident photon beam, you can output the convolved data with various formats. One of the formats for 2D arrays is writing data in contour lines. Every contour line will be given by two columns. This format can be obtained using the command "cc". The output file can be imported to some plotting software such as KaleidaGraph, and the contour lines can be drawn. For example: > Main menu (h for help) => cc > Contour output of convolved data (h for help) => h A = absorption vs r & z [J/cm3] F = fluence vs r & z [J/cm2] R = diffuse reflectance vs radius and angle [J/(cm2 sr)] T = transmittance vs radius and angle [J/(cm2 sr)] Q = Quit to main menu * input filename: example.mco > Contour output of convolved data (h for help) => Since only the 2D arrays need to be presented in contour lines, there are only four physical quantities. The command "A" outputs the absorption energy density as a function of r and z whose dimension is J cm-3. The command "F" outputs the fluence as a function of r and z in J cm-2. The commands "R" and "T" output diffuse reflectance and transmittance as a function of r and a in J cm-2 sr-1 respectively. After you input one of the commands, the program will prompt for the output filename and the isovalues for the contour output. The value range of the physical quantity is shown so that valid isovalues can be provided by users. You can enter as many isovalues as you want. System memory is the only thing that limits the number of isovalues. Stop entering isovalues by inputting a period ".". For example: > Contour output of convolved data (h for help) => A Enter output filename with extension .iso (or . to quit): exampleAc.iso The range of the value is 0.048200 to 95.624939. Input an isovalue or . to stop: 80 Input an isovalue or . to stop: 8 Input an isovalue or . to stop: 0.8 Input an isovalue or . to stop: . > Contour output of convolved data (h for help) => The output file of this example will have six columns, each pair of columns describe one contour line. The values of the contour lines are 80, 8, and 0.8 respectively. 10.11 Command "so" of conv After you input the filename of the mcml output, you can output the original data of the mcml output with various formats. One of the formats for 2D arrays is writing data in two columns, where the two columns give the physical quantity as a function of one of two independent variables. The other variable is fixed at a certain value which can be chosen by users. This format, we call scanning output, can be obtained using the command "so". The output file can be imported to some plotting software such as KaleidaGraph. For example: > Main menu (h for help) => so > Scans of mcml data (h for help) => h Ar = absorption vs r @ fixed z [1/cm3] Az = absorption vs z @ fixed r [1/cm3] Fr = fluence vs r @ fixed z [1/cm2] Fz = fluence vs z @ fixed r [1/cm2] Rr = diffuse reflectance vs r @ fixed angle [1/(cm2 sr)] Ra = diffuse reflectance vs angle @ fixed r [1/(cm2 sr)] Tr = transmittance vs r @ fixed angle [1/(cm2 sr)] Ta = transmittance vs angle @ fixed r [1/(cm2 sr)] Q = quit * input filename: example.mco > Scans of mcml data (h for help) => The command "Ar" outputs the absorption probability density as a function of r for a fixed z, whose dimension is cm-3. The command "Az" outputs the absorption probability density as a function of z for a fixed r, whose dimension is cm-3. The command "Fr" outputs the probability fluence as a function of r for a fixed z in cm-2. The command "Fz" outputs the probability fluence as a function of z for a fixed r in cm-2. The command "Rr" outputs the diffuse reflectance as a function of r for a fixed a in cm-2sr-1. The command "Ra" outputs the diffuse reflectance as a function of a for a fixed r in cm-2sr-1. The commands "Tr" and "Ta" output the transmittance in the same format as for the diffuse reflectance. The command "q" returns to the main menu. After you input one of the commands, the program will prompt for the output filename and the grid index to the value of the fixed variable. If you want to abort this output, you can input a period "." as the filename. For example: > Scans of mcml data (h for help) => Ar Enter output filename with extension .Ars (or . to quit): example.Ars z grid separation is 0.01 cm. Input fixed z index (0 - 39): 0 > Scans of mcml data (h for help) => This command outputs the absorption as a function of r for a fixed z. The program shows that the z grid separation is 0.01 cm. The number of grid elements in the z direction is 40. The grid index in the z direction is in the range from 0 to 39. The command will generate two columns. The first column is r, and the second is the absorption. 10.12 Command "sc" of conv After you input the filename of the mcml output and specify the incident photon beam, you can output the convolved data with various formats. One of the formats for 2D arrays is writing data in two columns, where the two columns give the physical quantity as a function of one of two independent variables. The other variable is fixed at a certain value which can be chosen by users. This format, we call scanning output, can be obtained using the command "sc". Then, the output file can be imported to some plotting software such as KaleidaGraph. For example: > Main menu (h for help) => sc > Scans of convolved data (h for help) => h Ar = absorption vs r @ fixed z [J/cm3] Az = absorption vs z @ fixed r [J/cm3] Fr = fluence vs r @ fixed z [J/cm2] Fz = fluence vs z @ fixed r [J/cm2] Rr = diffuse reflectance vs r @ fixed angle [J/(cm2 sr)] Ra = diffuse reflectance vs angle @ fixed r [J/(cm2 sr)] Tr = transmittance vs r @ fixed angle [J/(cm2 sr)] Ta = transmittance vs angle @ fixed r [J/(cm2 sr)] Q = quit * input filename: example.mco > Scans of convolved data (h for help) => The command "Ar" outputs the absorption energy density as a function of r for a fixed z, whose dimension is J cm-3. The command "Az" outputs the absorption energy density as a function of z for a fixed r, whose dimension is J cm-3. The command "Fr" outputs the fluence as a function of r for a fixed z in J cm-2. The command "Fz" outputs the fluence as a function of z for a fixed r in J cm-2. The command "Rr" outputs the diffuse reflectance as a function of r for a fixed a in J cm-2 sr-1. The command "Ra" outputs the diffuse reflectance as a function of a for a fixed r in J cm- 2 sr-1. The commands "Tr" and "Ta" output the transmittance in the same format as for the diffuse reflectance. The command "q" returns to the main menu. After you input one of the commands, the program will prompt for the output filename and the grid index to the value of the fixed variable. If you want to abort this output, you can input a period "." as the filename. For example: > Scans of convolved data (h for help) => Ar Enter output filename with extension .Arsc (or . to quit): example.Arsc z grid separation is 0.01 cm. Input fixed z index (0 - 39): 0 > Scans of convolved data (h for help) => This command outputs the absorption as a function of r for a fixed z. The program shows that the z grid separation is 0.01 cm. The number of grids in the z direction is 40. The grid index in the z direction is in the range from 0 to 39. The command will generate two columns. The first column is r, and the second is the absorption. 10.13 Command "q" of conv If you want to quit the program conv, use the command "q" in the main menu. The program will ask you if you really mean to quit. You can answer yes or no. The program will quit if the answer is "y". Otherwise, the program will return to the main menu. For example: > Main menu (h for help) => q Do you really want to quit conv (y/n): n > Main menu (h for help) => q Do you really want to quit conv (y/n): y 10.14 Bugs of conv The convolution results may have weird discontinuities if the allowed convolution error is too high, and the convolution process may take too long if the convolution error is too low. We do not have a good way to predict the best convolution error yet. The rule of thumb is that you choose the lowest convolution error that does not make the convolution too long to compute. If the convolution results still have any discontinuities which should not be there, you need to decrease the convolution error and redo the convolution. As we discussed in Section 7.5, the radius of the incident beam has to be in the right range to get reliable convolution integration due to the spatial resolution and the range of grid system. As a rule of thumb, the radius should be in the range between about 3 times the grid separation in the r direction and the total grid coverage in the r direction minus the maximum radius of observation (see Eqs. 7.23 & 7.24 in Section 7.4). If the number of points in the r direction is chosen too large in the command "r", the program can exit due to the lack of memory. Appendix G. Where to Get the Programs mcml and conv We will eventually set up a bulletin board of our own so that you can download the software over the network. For now, you can contact Lihong Wang, or Steven L. Jacques using the following information to get the software. Lihong Wang, Ph. D. Laser Biology Research Laboratory - 017 University of Texas M. D. Anderson Cancer Center 1515 Holcombe Blvd. Houston, Texas 77030 Fax: (713)792-3995 email: [email protected] or Steven L. Jacques, Ph. D. Laser Biology Research Laboratory - 017 University of Texas M. D. Anderson Cancer Center 1515 Holcombe Blvd. Houston, Texas 77030 Fax: (713)792-3995 email: [email protected] Make sure that you tell us the specific machines (including brand and model) you will use for the simulation, so that we can compile the code correctly for you. If you use IBM PC compatibles, please also tell us whether you use a math co- processor or not. August 20, 1992

__MACOSX/FTP-Uploads/ch04Convolution/._Mcman.txt

FTP-Uploads/ch04Convolution/ReadmeR5.txt

Last update: 3/1/2000 MCML is a Monte Carlo simulation program for Multi-layered Turbid Media with an infinitely narrow photon beam as the light source. The simulation is specified by an input text file called, for example, "sample.mci", which can be altered by any simple text editor. The output is another text file called, for example, "sample.mco". (The names are arbitrary.) CONV is a convolution program which uses the MCML output file to convolve for photon beams of variable size or shape (Gaussian or flat field). CONV can provide a variety of output formats (reflectance, transmission, iso-fluence contours, etc.) which are compatible with standard graphics applications. HISTORY ====================================================================== MCML 1.2 corrected a bug that sometimes led to a sqrt() domain error due to the finite machine precision. However, this error has not affected previously computed results because if this error happened, the program would have stopped execution and yielded no output. MCML 1.2.1 corrected a bug that sometimes led to memory problems when the grid size was too small. MCML 1.2.2 corrected a bug that was introduced in 1.2.1. CONV 1.1 corrected a bug of the qtrap() function in the convnr.c file. The integration by the original qtrap() sometimes converged incorrectly to zero when the Monte Carlo data was noisy. ====================================================================== Lihong Wang, Ph.D. Associate Professor Biomedical Engineering Program 234C Zachry Engineering Center Texas A&M University College Station, TX 77843-3120 Tel: 979-847-9040 Fax: 979-845-4450 Email: [email protected] URL: http://oilab.tamu.edu Steven L. Jacques, Ph.D. Professor Oregon Medical Laser Center Providence/St. Vincent Hospital 9205 SW Barnes Rd. Portland, OR 97225 Tel: 503-216-4092 Fax: 503-291-2422 Email: [email protected] URL: http://omlc.ogi.edu/staff/jacques.html

__MACOSX/FTP-Uploads/ch04Convolution/._ReadmeR5.txt

FTP-Uploads/ch04Convolution/SAMPLE.MCO

A1 # Version number of the file format. #### # Data categories include: # InParm, RAT, # A_l, A_z, Rd_r, Rd_a, Tt_r, Tt_a, # A_rz, Rd_ra, Tt_ra #### # User time: 1488.13 sec = 0.41 hr. Simulation time of this run. InParm # Input parameters. cm is used. sample.mco A # output file name, ASCII. 1000000 # No. of photons 0.01 0.01 # dz, dr [cm] 40 50 1 # No. of dz, dr, da. 3 # Number of layers #n mua mus g d # One line for each layer 1 # n for medium above 1.37 1 100 0.9 0.1 # layer 1 1.37 1 10 0 0.1 # layer 2 1.37 2 10 0.7 0.2 # layer 3 1 # n for medium below RAT #Reflectance, absorption, transmission. 0.0243729 #Specular reflectance [-] 0.237544 #Diffuse reflectance [-] 0.641594 #Absorbed fraction [-] 0.0964896 #Transmittance [-] A_l #Absorption as a function of layer. [-] 0.2612 0.1488 0.2316 A_z #A[0], [1],..A[nz-1]. [1/cm] 2.7990E+00 2.8099E+00 2.8125E+00 2.7845E+00 2.7265E+00 2.6497E+00 2.5552E+00 2.4446E+00 2.3323E+00 2.2037E+00 2.0549E+00 1.9209E+00 1.7779E+00 1.6612E+00 1.5438E+00 1.4102E+00 1.2974E+00 1.1851E+00 1.0694E+00 9.5971E-01 1.7314E+00 1.6297E+00 1.5446E+00 1.4724E+00 1.4007E+00 1.3238E+00 1.2726E+00 1.2104E+00 1.1565E+00 1.1089E+00 1.0698E+00 1.0307E+00 9.9160E-01 9.5890E-01 9.3477E-01 9.0365E-01 8.7446E-01 8.6162E-01 8.4770E-01 8.3724E-01 Rd_r #Rd[0], [1],..Rd[nr-1]. [1/cm2] 3.0924E+01 1.0071E+01 6.1187E+00 4.4015E+00 3.5138E+00 2.8528E+00 2.4344E+00 2.0692E+00 1.7715E+00 1.5409E+00 1.3425E+00 1.2006E+00 1.0319E+00 8.9424E-01 7.9300E-01 7.0183E-01 6.1493E-01 5.4537E-01 4.8518E-01 4.2576E-01 3.7759E-01 3.2900E-01 2.9997E-01 2.6421E-01 2.3595E-01 2.0775E-01 1.8657E-01 1.6498E-01 1.4860E-01 1.4064E-01 1.2364E-01 1.0955E-01 9.9937E-02 8.9281E-02 8.3917E-02 7.1952E-02 6.5441E-02 5.8384E-02 5.3626E-02 4.8753E-02 4.5503E-02 3.8753E-02 3.6688E-02 3.1858E-02 3.1348E-02 2.6778E-02 2.5402E-02 2.2791E-02 2.0812E-02 3.1424E-01 Rd_a #Rd[0], [1],..Rd[na-1]. [sr-1] 3.4038E-02 Tt_r #Tt[0], [1],..Tt[nr-1]. [1/cm2] 5.8942E-01 6.0085E-01 5.6453E-01 5.5132E-01 5.3655E-01 5.3603E-01 5.2027E-01 4.9193E-01 4.6582E-01 4.3591E-01 4.1109E-01 3.9854E-01 3.6636E-01 3.3945E-01 3.2122E-01 2.9694E-01 2.8041E-01 2.6182E-01 2.3866E-01 2.2141E-01 2.1004E-01 1.9047E-01 1.7161E-01 1.6091E-01 1.5083E-01 1.3239E-01 1.2549E-01 1.1543E-01 9.9789E-02 9.4186E-02 8.4806E-02 7.8703E-02 7.4215E-02 6.6476E-02 6.0723E-02 5.6045E-02 5.1798E-02 4.6792E-02 4.3551E-02 4.0498E-02 3.6332E-02 3.4432E-02 3.0875E-02 2.9627E-02 2.6649E-02 2.3876E-02 2.2584E-02 2.1318E-02 1.8962E-02 3.3392E-01 Tt_a #Tt[0], [1],..Tt[na-1]. [sr-1] 1.3826E-02 # A[r][z]. [1/cm3] # A[0][0], [0][1],..[0][nz-1] # A[1][0], [1][1],..[1][nz-1] # ... # A[nr-1][0], [nr-1][1],..[nr-1][nz-1] A_rz 3.2948E+03 3.0386E+03 2.5033E+03 1.8514E+03 1.2758E+03 8.5363E+02 5.5268E+02 3.6460E+02 2.4357E+02 1.7068E+02 1.1574E+02 8.3167E+01 6.3684E+01 4.9714E+01 3.7366E+01 3.1753E+01 2.3568E+01 2.0270E+01 1.5639E+01 1.3584E+01 1.9635E+01 1.7311E+01 1.4253E+01 1.2972E+01 9.3743E+00 9.7259E+00 8.8083E+00 7.1041E+00 6.9629E+00 5.8110E+00 4.5151E+00 5.3302E+00 3.9514E+00 3.7088E+00 3.5495E+00 3.2786E+00 3.3136E+00 3.2377E+00 2.6409E+00 2.3877E+00 7.7127E+01 1.3435E+02 2.1857E+02 2.8337E+02 3.0338E+02 2.8525E+02 2.4567E+02 1.9975E+02 1.5713E+02 1.2392E+02 9.4105E+01 7.2111E+01 5.4411E+01 4.3708E+01 3.5286E+01 2.7410E+01 2.2960E+01 1.8669E+01 1.5932E+01 1.1772E+01 1.9931E+01 1.6372E+01 1.3586E+01 1.2065E+01 1.1066E+01 9.0454E+00 8.0976E+00 7.3499E+00 6.8339E+00 5.2720E+00 5.5565E+00 4.3873E+00 4.1344E+00 3.8387E+00 3.8148E+00 3.2499E+00 3.0570E+00 2.8812E+00 2.6316E+00 2.3044E+00 4.5798E+01 5.7578E+01 7.8613E+01 1.0230E+02 1.1924E+02 1.2629E+02 1.2437E+02 1.1403E+02 1.0157E+02 8.7131E+01 7.1093E+01 5.7353E+01 4.5795E+01 3.7662E+01 3.0809E+01 2.4446E+01 2.1182E+01 1.7132E+01 1.4133E+01 1.0873E+01 1.8306E+01 1.5789E+01 1.2574E+01 1.1981E+01 9.9820E+00 9.3130E+00 8.0804E+00 7.3127E+00 5.9439E+00 5.5170E+00 5.0146E+00 4.6705E+00 4.3288E+00 3.9200E+00 3.9201E+00 3.3529E+00 3.1221E+00 2.7360E+00 2.6868E+00 2.4713E+00 3.3587E+01 3.7233E+01 4.5398E+01 5.5696E+01 6.4872E+01 7.1115E+01 7.3677E+01 7.1838E+01 6.7887E+01 6.2389E+01 5.4319E+01 4.5239E+01 3.8338E+01 3.2079E+01 2.7404E+01 2.2464E+01 1.8471E+01 1.5310E+01 1.3049E+01 1.0919E+01 1.6960E+01 1.4700E+01 1.2635E+01 1.1121E+01 1.0090E+01 8.5985E+00 7.5797E+00 6.6712E+00 6.0644E+00 5.5476E+00 4.8234E+00 4.7161E+00 3.9237E+00 3.8259E+00 3.5619E+00 3.0617E+00 2.8766E+00 2.5836E+00 2.4047E+00 2.4255E+00 2.6644E+01 2.8171E+01 3.2086E+01 3.6863E+01 4.1943E+01 4.6057E+01 4.8637E+01 4.8976E+01 4.8396E+01 4.5849E+01 4.0957E+01 3.5323E+01 3.1106E+01 2.6812E+01 2.2661E+01 1.9077E+01 1.6448E+01 1.3865E+01 1.1552E+01 9.5818E+00 1.4848E+01 1.3116E+01 1.1504E+01 1.0503E+01 9.6053E+00 8.4464E+00 7.5264E+00 6.7147E+00 5.8786E+00 5.2765E+00 4.7106E+00 4.1899E+00 3.9374E+00 3.5330E+00 3.4797E+00 3.0063E+00 2.8387E+00 2.8062E+00 2.4622E+00 2.3508E+00 2.1769E+01 2.2587E+01 2.4633E+01 2.7557E+01 3.0373E+01 3.2957E+01 3.4955E+01 3.5829E+01 3.5786E+01 3.4572E+01 3.1326E+01 2.8770E+01 2.4797E+01 2.2481E+01 1.9479E+01 1.6438E+01 1.4432E+01 1.2402E+01 1.0480E+01 8.6868E+00 1.4272E+01 1.2484E+01 1.0723E+01 9.7950E+00 8.6256E+00 7.7732E+00 6.8854E+00 6.0300E+00 5.6092E+00 4.9945E+00 4.7133E+00 4.4940E+00 3.9896E+00 3.5302E+00 3.3458E+00 3.0541E+00 2.7935E+00 2.7386E+00 2.5252E+00 2.4299E+00 1.8557E+01 1.9028E+01 2.0184E+01 2.1805E+01 2.3519E+01 2.5307E+01 2.6769E+01 2.7288E+01 2.7692E+01 2.6946E+01 2.5086E+01 2.3027E+01 2.0763E+01 1.8581E+01 1.6527E+01 1.4494E+01 1.2629E+01 1.0987E+01 9.3922E+00 7.6509E+00 1.2637E+01 1.1234E+01 9.9387E+00 8.9271E+00 7.8422E+00 7.4306E+00 6.3949E+00 5.9860E+00 5.5244E+00 4.8229E+00 4.5907E+00 4.0413E+00 3.6031E+00 3.3596E+00 3.1579E+00 3.0289E+00 2.6747E+00 2.6619E+00 2.4597E+00 2.3225E+00 1.5837E+01 1.5968E+01 1.6731E+01 1.7910E+01 1.8979E+01 2.0103E+01 2.0990E+01 2.1492E+01 2.1883E+01 2.1438E+01 2.0009E+01 1.8525E+01 1.7294E+01 1.5881E+01 1.4110E+01 1.2771E+01 1.1079E+01 9.6995E+00 8.2199E+00 7.0207E+00 1.1976E+01 1.0106E+01 9.3992E+00 8.3850E+00 7.5713E+00 6.7604E+00 6.2219E+00 5.7219E+00 5.1321E+00 4.5468E+00 4.1636E+00 3.9668E+00 3.5604E+00 3.4351E+00 3.1329E+00 2.8123E+00 2.5073E+00 2.5055E+00 2.4030E+00 2.2625E+00 1.3692E+01 1.3767E+01 1.4192E+01 1.5102E+01 1.5881E+01 1.6485E+01 1.7048E+01 1.7423E+01 1.7758E+01 1.7489E+01 1.6025E+01 1.5352E+01 1.4124E+01 1.3168E+01 1.2044E+01 1.0632E+01 9.6758E+00 8.7004E+00 7.1331E+00 6.1714E+00 1.0291E+01 9.1579E+00 8.6549E+00 7.7945E+00 7.1404E+00 6.3682E+00 5.9560E+00 5.3392E+00 4.7046E+00 4.3253E+00 4.0107E+00 3.7120E+00 3.3102E+00 2.9969E+00 2.9080E+00 2.8208E+00 2.5675E+00 2.4533E+00 2.3506E+00 2.1292E+00 1.1819E+01 1.1862E+01 1.2249E+01 1.2661E+01 1.3357E+01 1.3784E+01 1.4262E+01 1.4510E+01 1.4560E+01 1.4310E+01 1.3919E+01 1.3218E+01 1.1891E+01 1.1382E+01 1.0392E+01 9.1382E+00 8.4358E+00 7.3946E+00 6.3984E+00 5.4761E+00 9.5240E+00 8.6090E+00 8.0760E+00 7.1709E+00 6.3642E+00 5.7821E+00 5.4007E+00 5.0000E+00 4.6240E+00 4.0829E+00 3.7252E+00 3.4252E+00 3.2010E+00 2.9890E+00 2.7288E+00 2.6249E+00 2.3309E+00 2.2733E+00 2.2435E+00 2.1760E+00 1.0362E+01 1.0415E+01 1.0572E+01 1.0897E+01 1.1371E+01 1.1700E+01 1.1960E+01 1.2075E+01 1.2037E+01 1.1804E+01 1.1215E+01 1.0859E+01 1.0225E+01 9.6035E+00 8.9408E+00 8.2965E+00 7.4993E+00 6.5605E+00 5.6238E+00 5.1037E+00 8.6443E+00 7.9103E+00 7.2582E+00 6.5296E+00 6.0156E+00 5.3984E+00 4.9701E+00 4.7211E+00 4.1764E+00 3.9868E+00 3.6498E+00 3.3606E+00 3.1273E+00 2.8215E+00 2.7774E+00 2.4791E+00 2.3020E+00 2.3076E+00 2.1799E+00 2.0431E+00 9.0281E+00 9.1032E+00 9.3139E+00 9.5394E+00 9.7003E+00 9.9873E+00 1.0214E+01 1.0375E+01 1.0257E+01 9.9403E+00 9.5851E+00 9.1534E+00 8.9164E+00 8.3010E+00 7.8457E+00 7.2651E+00 6.3887E+00 5.9515E+00 5.2296E+00 4.5439E+00 7.9243E+00 7.1457E+00 6.6062E+00 6.1034E+00 5.4878E+00 5.1499E+00 4.6596E+00 4.1946E+00 3.9657E+00 3.7315E+00 3.4249E+00 3.3085E+00 2.9649E+00 2.8133E+00 2.5261E+00 2.4239E+00 2.2333E+00 2.2547E+00 2.1056E+00 1.9888E+00 7.9646E+00 7.9781E+00 8.0863E+00 8.2070E+00 8.5358E+00 8.6571E+00 8.8068E+00 8.8001E+00 8.7034E+00 8.4443E+00 8.1076E+00 7.8013E+00 7.5140E+00 7.2249E+00 6.8036E+00 6.2009E+00 5.6449E+00 5.2171E+00 4.5902E+00 4.0062E+00 7.1777E+00 6.4568E+00 5.8626E+00 5.3574E+00 5.1476E+00 4.6606E+00 4.4206E+00 3.9834E+00 3.7526E+00 3.3636E+00 3.1625E+00 2.8835E+00 2.7793E+00 2.7127E+00 2.5184E+00 2.3613E+00 2.2660E+00 2.0981E+00 1.9176E+00 1.8950E+00 7.0649E+00 7.0114E+00 7.1066E+00 7.2279E+00 7.3939E+00 7.4875E+00 7.6020E+00 7.6677E+00 7.5113E+00 7.2802E+00 7.1935E+00 6.8070E+00 6.5576E+00 6.1879E+00 5.9335E+00 5.4395E+00 4.9940E+00 4.5055E+00 4.0932E+00 3.5941E+00 6.2394E+00 5.8917E+00 5.4310E+00 5.1358E+00 4.7087E+00 4.1338E+00 4.0093E+00 3.6311E+00 3.4929E+00 3.3013E+00 2.9364E+00 2.8346E+00 2.6777E+00 2.5114E+00 2.4420E+00 2.2343E+00 2.0929E+00 2.0433E+00 1.9338E+00 1.8857E+00 6.2266E+00 6.2354E+00 6.2716E+00 6.3906E+00 6.4348E+00 6.5073E+00 6.5240E+00 6.5502E+00 6.4985E+00 6.3182E+00 6.1591E+00 5.9325E+00 5.7944E+00 5.4294E+00 5.2393E+00 4.8229E+00 4.3716E+00 4.0413E+00 3.7395E+00 3.2180E+00 5.6994E+00 5.3573E+00 4.9850E+00 4.5945E+00 4.3285E+00 3.9799E+00 3.7148E+00 3.4853E+00 3.2735E+00 3.0653E+00 2.9187E+00 2.5750E+00 2.4637E+00 2.3800E+00 2.2750E+00 2.1405E+00 1.9644E+00 1.8857E+00 1.9172E+00 1.7609E+00 5.4537E+00 5.5089E+00 5.5399E+00 5.6687E+00 5.6923E+00 5.7056E+00 5.7152E+00 5.6769E+00 5.6501E+00 5.4633E+00 5.3417E+00 5.2286E+00 4.9677E+00 4.8664E+00 4.6801E+00 4.2210E+00 3.9307E+00 3.6070E+00 3.2019E+00 2.9075E+00 5.2872E+00 4.8748E+00 4.4075E+00 4.2307E+00 3.9033E+00 3.6261E+00 3.4584E+00 3.2527E+00 3.0409E+00 2.8763E+00 2.6800E+00 2.5402E+00 2.3760E+00 2.2169E+00 2.0827E+00 1.9918E+00 1.8835E+00 1.8409E+00 1.7515E+00 1.7566E+00 4.8300E+00 4.9039E+00 4.8767E+00 4.9456E+00 5.0058E+00 4.9989E+00 5.0306E+00 5.0415E+00 4.9517E+00 4.7606E+00 4.7016E+00 4.5485E+00 4.4384E+00 4.3015E+00 4.0411E+00 3.8119E+00 3.6438E+00 3.2462E+00 2.9596E+00 2.5896E+00 4.5996E+00 4.4269E+00 4.2537E+00 3.8945E+00 3.6990E+00 3.3238E+00 3.2292E+00 3.0258E+00 2.8117E+00 2.7648E+00 2.5739E+00 2.3794E+00 2.2843E+00 2.1082E+00 2.0670E+00 1.9998E+00 1.8138E+00 1.7746E+00 1.7416E+00 1.7167E+00 4.3227E+00 4.2962E+00 4.3576E+00 4.3955E+00 4.3858E+00 4.4455E+00 4.3667E+00 4.3620E+00 4.2911E+00 4.1866E+00 4.0525E+00 4.2358E+00 3.9172E+00 3.7217E+00 3.5521E+00 3.2899E+00 3.2155E+00 2.9223E+00 2.5808E+00 2.3775E+00 4.2192E+00 4.0754E+00 3.7023E+00 3.4863E+00 3.3427E+00 3.1473E+00 2.9822E+00 2.7715E+00 2.6390E+00 2.4763E+00 2.3538E+00 2.1870E+00 2.1172E+00 1.9862E+00 1.8458E+00 1.8783E+00 1.7053E+00 1.6858E+00 1.5939E+00 1.5487E+00 3.8441E+00 3.8201E+00 3.8322E+00 3.8768E+00 3.8953E+00 3.9315E+00 3.8682E+00 3.8417E+00 3.7866E+00 3.6940E+00 3.5972E+00 3.6598E+00 3.4032E+00 3.2728E+00 3.2002E+00 2.9572E+00 2.7837E+00 2.5807E+00 2.3471E+00 2.0870E+00 3.7585E+00 3.6084E+00 3.4494E+00 3.2375E+00 3.0661E+00 2.8923E+00 2.7718E+00 2.6048E+00 2.4136E+00 2.2818E+00 2.2162E+00 2.0986E+00 2.0066E+00 1.9325E+00 1.7892E+00 1.7223E+00 1.6464E+00 1.5918E+00 1.5976E+00 1.5252E+00 3.4042E+00 3.3939E+00 3.4196E+00 3.3998E+00 3.4399E+00 3.4368E+00 3.4221E+00 3.3850E+00 3.3712E+00 3.2602E+00 3.2158E+00 3.1496E+00 3.1109E+00 3.0433E+00 2.8492E+00 2.6698E+00 2.5418E+00 2.2903E+00 2.1338E+00 1.9124E+00 3.4701E+00 3.3644E+00 3.1269E+00 3.1197E+00 2.8830E+00 2.6198E+00 2.5674E+00 2.3702E+00 2.3236E+00 2.1630E+00 2.0463E+00 1.9430E+00 1.8960E+00 1.7544E+00 1.7251E+00 1.5915E+00 1.5482E+00 1.5379E+00 1.4951E+00 1.4678E+00 3.0228E+00 3.0053E+00 3.0492E+00 3.0233E+00 3.0514E+00 3.1059E+00 3.0431E+00 3.0004E+00 2.9467E+00 2.8778E+00 2.8755E+00 2.8583E+00 2.7004E+00 2.5293E+00 2.5154E+00 2.4103E+00 2.2703E+00 2.0917E+00 1.9551E+00 1.7175E+00 3.1418E+00 2.9918E+00 2.8694E+00 2.7751E+00 2.6233E+00 2.5055E+00 2.3457E+00 2.2310E+00 2.1772E+00 1.9991E+00 1.9690E+00 1.8801E+00 1.7490E+00 1.6873E+00 1.5655E+00 1.5704E+00 1.4470E+00 1.4075E+00 1.4045E+00 1.3554E+00 2.6630E+00 2.6920E+00 2.6873E+00 2.7066E+00 2.7111E+00 2.7203E+00 2.6770E+00 2.6574E+00 2.6217E+00 2.5517E+00 2.5694E+00 2.5422E+00 2.4160E+00 2.3775E+00 2.2898E+00 2.1394E+00 2.0408E+00 1.9010E+00 1.7838E+00 1.5589E+00 2.8598E+00 2.7066E+00 2.6432E+00 2.5265E+00 2.4575E+00 2.2568E+00 2.2319E+00 2.0411E+00 1.9566E+00 1.9285E+00 1.8201E+00 1.7365E+00 1.6620E+00 1.6084E+00 1.4762E+00 1.4000E+00 1.4189E+00 1.4195E+00 1.3489E+00 1.3735E+00 2.3876E+00 2.3626E+00 2.4352E+00 2.4015E+00 2.4079E+00 2.3902E+00 2.3653E+00 2.3408E+00 2.3281E+00 2.2576E+00 2.2886E+00 2.2317E+00 2.1830E+00 2.1600E+00 2.0673E+00 1.9381E+00 1.8657E+00 1.6526E+00 1.5798E+00 1.4443E+00 2.6620E+00 2.5482E+00 2.4804E+00 2.3450E+00 2.2386E+00 2.1159E+00 2.0492E+00 1.9377E+00 1.8545E+00 1.7612E+00 1.7561E+00 1.6344E+00 1.5849E+00 1.5136E+00 1.4112E+00 1.3888E+00 1.3180E+00 1.3103E+00 1.2635E+00 1.2869E+00 2.1119E+00 2.1256E+00 2.1295E+00 2.1536E+00 2.1385E+00 2.1148E+00 2.1143E+00 2.1177E+00 2.0659E+00 2.0042E+00 2.0440E+00 1.9885E+00 1.9518E+00 1.9156E+00 1.8581E+00 1.7383E+00 1.5976E+00 1.5347E+00 1.4418E+00 1.2964E+00 2.4686E+00 2.2954E+00 2.2674E+00 2.0815E+00 2.1004E+00 1.9503E+00 1.8882E+00 1.7545E+00 1.7012E+00 1.6693E+00 1.5983E+00 1.5034E+00 1.4942E+00 1.4012E+00 1.3337E+00 1.3058E+00 1.2432E+00 1.2629E+00 1.2168E+00 1.2610E+00 1.8946E+00 1.9057E+00 1.9044E+00 1.9001E+00 1.9014E+00 1.8965E+00 1.8995E+00 1.8601E+00 1.8465E+00 1.8037E+00 1.8405E+00 1.7795E+00 1.7141E+00 1.7282E+00 1.6805E+00 1.5760E+00 1.4835E+00 1.3979E+00 1.2656E+00 1.1949E+00 2.2488E+00 2.1137E+00 1.9997E+00 2.0246E+00 1.9324E+00 1.8407E+00 1.7628E+00 1.7034E+00 1.5686E+00 1.5391E+00 1.4519E+00 1.4477E+00 1.3778E+00 1.3015E+00 1.3479E+00 1.2863E+00 1.1986E+00 1.1677E+00 1.1879E+00 1.1687E+00 1.6987E+00 1.6827E+00 1.7047E+00 1.7061E+00 1.7158E+00 1.7094E+00 1.6983E+00 1.6846E+00 1.6665E+00 1.6463E+00 1.5849E+00 1.6117E+00 1.5663E+00 1.5510E+00 1.4628E+00 1.4230E+00 1.3551E+00 1.2776E+00 1.1620E+00 1.0669E+00 2.0121E+00 1.9544E+00 1.8696E+00 1.8270E+00 1.7656E+00 1.7353E+00 1.6360E+00 1.5358E+00 1.5129E+00 1.4390E+00 1.3534E+00 1.3619E+00 1.2700E+00 1.2677E+00 1.2437E+00 1.1999E+00 1.1305E+00 1.1560E+00 1.1133E+00 1.0908E+00 1.5207E+00 1.5165E+00 1.5117E+00 1.5359E+00 1.5276E+00 1.5194E+00 1.5301E+00 1.5007E+00 1.4826E+00 1.4596E+00 1.4404E+00 1.4635E+00 1.4691E+00 1.3764E+00 1.3633E+00 1.3128E+00 1.2118E+00 1.1337E+00 1.0491E+00 9.7646E-01 1.8755E+00 1.7494E+00 1.6946E+00 1.6781E+00 1.6460E+00 1.5509E+00 1.5292E+00 1.4754E+00 1.3700E+00 1.3268E+00 1.2798E+00 1.2537E+00 1.2267E+00 1.1463E+00 1.1520E+00 1.1316E+00 1.0604E+00 1.0777E+00 1.0566E+00 1.0649E+00 1.3499E+00 1.3468E+00 1.3543E+00 1.3488E+00 1.3537E+00 1.3669E+00 1.3556E+00 1.3486E+00 1.3079E+00 1.3256E+00 1.3347E+00 1.2939E+00 1.2715E+00 1.2377E+00 1.2155E+00 1.1451E+00 1.0981E+00 1.0534E+00 9.5789E-01 9.0263E-01 1.7257E+00 1.6633E+00 1.6120E+00 1.4884E+00 1.4672E+00 1.4304E+00 1.4086E+00 1.3613E+00 1.2911E+00 1.2122E+00 1.1902E+00 1.1806E+00 1.1569E+00 1.1083E+00 1.1014E+00 1.0783E+00 1.0283E+00 1.0584E+00 9.9759E-01 9.8248E-01 1.2163E+00 1.2074E+00 1.2026E+00 1.2330E+00 1.2098E+00 1.2253E+00 1.2125E+00 1.1995E+00 1.1789E+00 1.1688E+00 1.1565E+00 1.1547E+00 1.1377E+00 1.1110E+00 1.0881E+00 1.0172E+00 1.0387E+00 9.6031E-01 8.6833E-01 8.5469E-01 1.5956E+00 1.5020E+00 1.4598E+00 1.4521E+00 1.3910E+00 1.3254E+00 1.2741E+00 1.2691E+00 1.2072E+00 1.1662E+00 1.1441E+00 1.1013E+00 1.0421E+00 1.0260E+00 1.0301E+00 9.7867E-01 9.9286E-01 9.4405E-01 9.3844E-01 9.1040E-01 1.0755E+00 1.0862E+00 1.0925E+00 1.0933E+00 1.0929E+00 1.1082E+00 1.0942E+00 1.0766E+00 1.0711E+00 1.0551E+00 1.0601E+00 1.0737E+00 1.0516E+00 1.0177E+00 9.9355E-01 9.5875E-01 8.9918E-01 8.7552E-01 8.2110E-01 7.5315E-01 1.3746E+00 1.3826E+00 1.3237E+00 1.3093E+00 1.2601E+00 1.1968E+00 1.1861E+00 1.1802E+00 1.1385E+00 1.0846E+00 1.0894E+00 1.0158E+00 1.0090E+00 9.5829E-01 9.8144E-01 9.4751E-01 9.3959E-01 8.8338E-01 8.9983E-01 9.0228E-01 9.8226E-01 9.6655E-01 9.7084E-01 9.8678E-01 9.8182E-01 9.8059E-01 9.7874E-01 9.6592E-01 9.5401E-01 9.4632E-01 9.4960E-01 9.5398E-01 9.4803E-01 9.0820E-01 8.7932E-01 8.5266E-01 8.6283E-01 7.9537E-01 7.3757E-01 6.8868E-01 1.3076E+00 1.2858E+00 1.2373E+00 1.2098E+00 1.1835E+00 1.1628E+00 1.1642E+00 1.1090E+00 1.0740E+00 1.0222E+00 9.8878E-01 9.9209E-01 9.3850E-01 8.8728E-01 9.1508E-01 8.9674E-01 8.7571E-01 8.6071E-01 8.3970E-01 8.2235E-01 8.8378E-01 8.8026E-01 8.6619E-01 8.8493E-01 8.8189E-01 8.6652E-01 8.7358E-01 8.7070E-01 8.6169E-01 8.5555E-01 8.6059E-01 8.5871E-01 8.3487E-01 8.3882E-01 8.2077E-01 7.9024E-01 7.5210E-01 7.0088E-01 6.6391E-01 6.4512E-01 1.1931E+00 1.1749E+00 1.1711E+00 1.1259E+00 1.0999E+00 1.0295E+00 1.0784E+00 1.0130E+00 9.9259E-01 9.6094E-01 9.3163E-01 9.2330E-01 8.8714E-01 8.8293E-01 8.3708E-01 8.6108E-01 8.4607E-01 8.0628E-01 8.0119E-01 8.0345E-01 7.8553E-01 7.8661E-01 7.7904E-01 7.8155E-01 7.9682E-01 7.8092E-01 7.8820E-01 7.8320E-01 7.7516E-01 7.7212E-01 7.6952E-01 7.6155E-01 7.7033E-01 7.5267E-01 7.3192E-01 7.1749E-01 6.8001E-01 6.5281E-01 6.1977E-01 5.6793E-01 1.0790E+00 1.0672E+00 1.0783E+00 1.0528E+00 1.0127E+00 1.0163E+00 9.4146E-01 9.3910E-01 9.1052E-01 8.8878E-01 9.0447E-01 8.4826E-01 8.1868E-01 8.3064E-01 8.0110E-01 8.0422E-01 7.8086E-01 7.5908E-01 7.7014E-01 7.6925E-01 6.9852E-01 7.0042E-01 7.1061E-01 7.0840E-01 7.1255E-01 7.0605E-01 7.0606E-01 7.0504E-01 7.1180E-01 7.0345E-01 6.9485E-01 6.9859E-01 7.0690E-01 7.0108E-01 6.5780E-01 6.3330E-01 6.1287E-01 5.8704E-01 5.6270E-01 5.3811E-01 9.9312E-01 9.5628E-01 9.5367E-01 9.5048E-01 9.2142E-01 9.5139E-01 9.1865E-01 8.7802E-01 8.2124E-01 8.1866E-01 8.4912E-01 8.1743E-01 8.0547E-01 7.8259E-01 7.5129E-01 7.3167E-01 7.5342E-01 7.2070E-01 7.3100E-01 7.2000E-01 6.2793E-01 6.3130E-01 6.4241E-01 6.3721E-01 6.3572E-01 6.3017E-01 6.4604E-01 6.5014E-01 6.4188E-01 6.2896E-01 6.2253E-01 6.5200E-01 6.0492E-01 6.2282E-01 6.1455E-01 5.8091E-01 5.3239E-01 5.5092E-01 5.0460E-01 4.8490E-01 9.2104E-01 9.0215E-01 8.9772E-01 8.7490E-01 8.5113E-01 8.5053E-01 8.4185E-01 8.0838E-01 7.7554E-01 8.2087E-01 7.7154E-01 7.6526E-01 7.4639E-01 7.2938E-01 7.5572E-01 6.8488E-01 6.6837E-01 6.6650E-01 6.8911E-01 6.7796E-01 5.5644E-01 5.6895E-01 5.7983E-01 5.7082E-01 5.7556E-01 5.6906E-01 5.7558E-01 5.7668E-01 5.6513E-01 5.6780E-01 5.8224E-01 5.8286E-01 5.6018E-01 5.7118E-01 5.4459E-01 5.4363E-01 5.1884E-01 5.0884E-01 4.6147E-01 4.4610E-01 8.7015E-01 8.2078E-01 8.4014E-01 7.8954E-01 8.2303E-01 7.8961E-01 7.8648E-01 7.7380E-01 7.4541E-01 7.3772E-01 7.2885E-01 7.2475E-01 6.6522E-01 6.7419E-01 6.8238E-01 6.6410E-01 6.5399E-01 6.4403E-01 6.4874E-01 6.2729E-01 5.0943E-01 5.0963E-01 5.1031E-01 5.2526E-01 5.2100E-01 5.1810E-01 5.2357E-01 5.1802E-01 5.1339E-01 5.1676E-01 5.1947E-01 5.3179E-01 5.0923E-01 5.1169E-01 5.0501E-01 4.9471E-01 4.6949E-01 4.4864E-01 4.4612E-01 4.0766E-01 7.7757E-01 7.6714E-01 7.6915E-01 7.9140E-01 7.5316E-01 7.4023E-01 7.1869E-01 6.8782E-01 7.1272E-01 6.9100E-01 6.6077E-01 6.7682E-01 6.5487E-01 6.5588E-01 6.3526E-01 6.2053E-01 6.3206E-01 5.9696E-01 6.0290E-01 6.0288E-01 4.6312E-01 4.5592E-01 4.5988E-01 4.5658E-01 4.7361E-01 4.6532E-01 4.7667E-01 4.6669E-01 4.7354E-01 4.7021E-01 4.6650E-01 4.6286E-01 4.6629E-01 4.5910E-01 4.6490E-01 4.4009E-01 4.3222E-01 4.1135E-01 3.8905E-01 3.8488E-01 7.3407E-01 7.4116E-01 7.3564E-01 7.0703E-01 7.1733E-01 6.6825E-01 6.7135E-01 6.4658E-01 6.4286E-01 6.3597E-01 6.3532E-01 6.2851E-01 6.1427E-01 6.0889E-01 5.7856E-01 5.6138E-01 5.5628E-01 5.7625E-01 5.5880E-01 5.7713E-01 4.1546E-01 4.2165E-01 4.1831E-01 4.2485E-01 4.2571E-01 4.2743E-01 4.2204E-01 4.3188E-01 4.2459E-01 4.3150E-01 4.2033E-01 4.3020E-01 4.4004E-01 4.3132E-01 4.0025E-01 4.1214E-01 3.8434E-01 3.8101E-01 3.7252E-01 3.5271E-01 7.0871E-01 6.7876E-01 6.6187E-01 6.5129E-01 6.3112E-01 6.3452E-01 6.2176E-01 6.3575E-01 6.0580E-01 5.6856E-01 5.7384E-01 5.8265E-01 5.7399E-01 5.6861E-01 5.7658E-01 5.3300E-01 5.4568E-01 5.4097E-01 5.5075E-01 5.4747E-01 3.8400E-01 3.7124E-01 3.7390E-01 3.8287E-01 3.8729E-01 3.9144E-01 3.8609E-01 3.8912E-01 3.8752E-01 3.9085E-01 3.9089E-01 3.8951E-01 3.9314E-01 3.7205E-01 3.9235E-01 3.8496E-01 3.6305E-01 3.5103E-01 3.3310E-01 3.3303E-01 6.2538E-01 6.3090E-01 6.1281E-01 6.0221E-01 5.8409E-01 5.6667E-01 5.8976E-01 5.8116E-01 5.8206E-01 5.6291E-01 5.5049E-01 5.2263E-01 5.2076E-01 5.2987E-01 5.2568E-01 5.1520E-01 5.1229E-01 5.1449E-01 5.1163E-01 5.1276E-01 3.4384E-01 3.4108E-01 3.4496E-01 3.4156E-01 3.4664E-01 3.5547E-01 3.4632E-01 3.5435E-01 3.5360E-01 3.4827E-01 3.5309E-01 3.5635E-01 3.5432E-01 3.5190E-01 3.5441E-01 3.4879E-01 3.2866E-01 3.2267E-01 3.0721E-01 2.9461E-01 5.6742E-01 5.7209E-01 5.6009E-01 5.6696E-01 5.6190E-01 5.3777E-01 5.5589E-01 5.2849E-01 5.2315E-01 5.1567E-01 5.2433E-01 5.1233E-01 4.9520E-01 4.8995E-01 4.9659E-01 4.8750E-01 4.8963E-01 4.8021E-01 4.8534E-01 4.6634E-01 3.0806E-01 3.0799E-01 3.1018E-01 3.1007E-01 3.1502E-01 3.1302E-01 3.1655E-01 3.1902E-01 3.2013E-01 3.2031E-01 3.3107E-01 3.4212E-01 3.2550E-01 3.2767E-01 3.2154E-01 3.1860E-01 2.9377E-01 2.8651E-01 2.9222E-01 2.7401E-01 5.1787E-01 5.3139E-01 5.3066E-01 5.3871E-01 5.4626E-01 4.9851E-01 5.1879E-01 4.8874E-01 4.7820E-01 5.0408E-01 4.9476E-01 4.7911E-01 4.6734E-01 4.6498E-01 4.6426E-01 4.4991E-01 4.7411E-01 4.5274E-01 4.4948E-01 4.6219E-01 2.8115E-01 2.7728E-01 2.7933E-01 2.8172E-01 2.8445E-01 2.8792E-01 2.8777E-01 2.8300E-01 2.8853E-01 2.8488E-01 2.9352E-01 3.1281E-01 2.9936E-01 2.9045E-01 2.9236E-01 2.9018E-01 2.9105E-01 2.6581E-01 2.4987E-01 2.4940E-01 5.0305E-01 4.8414E-01 4.8958E-01 4.7494E-01 4.7117E-01 4.9020E-01 4.5751E-01 4.6645E-01 4.5986E-01 4.7193E-01 4.4706E-01 4.5461E-01 4.4148E-01 4.5422E-01 4.2462E-01 4.3328E-01 4.3993E-01 4.2648E-01 4.3482E-01 4.1478E-01 2.5375E-01 2.6027E-01 2.5627E-01 2.5602E-01 2.6133E-01 2.6177E-01 2.6272E-01 2.6656E-01 2.6417E-01 2.6116E-01 2.6939E-01 2.7249E-01 2.6650E-01 2.7403E-01 2.7419E-01 2.6390E-01 2.4596E-01 2.5381E-01 2.5141E-01 2.3328E-01 4.7649E-01 4.5556E-01 4.5460E-01 4.5843E-01 4.4834E-01 4.3513E-01 4.5599E-01 4.3869E-01 4.3234E-01 4.2183E-01 4.2155E-01 4.2712E-01 4.2003E-01 4.3044E-01 3.9761E-01 4.2145E-01 3.9134E-01 3.9165E-01 3.9650E-01 4.1218E-01 2.3098E-01 2.3293E-01 2.3061E-01 2.3307E-01 2.3408E-01 2.3672E-01 2.3346E-01 2.4185E-01 2.3908E-01 2.3487E-01 2.4654E-01 2.4802E-01 2.5061E-01 2.5363E-01 2.4016E-01 2.3884E-01 2.3307E-01 2.2469E-01 2.2653E-01 2.1979E-01 4.0449E-01 4.0526E-01 4.3260E-01 4.1752E-01 4.0555E-01 4.2062E-01 4.2188E-01 4.1986E-01 4.0992E-01 3.9229E-01 4.0806E-01 3.9155E-01 3.8434E-01 3.9899E-01 3.9377E-01 3.5792E-01 3.7871E-01 3.8230E-01 3.8629E-01 3.9637E-01 2.0447E-01 2.0643E-01 2.1242E-01 2.1244E-01 2.1758E-01 2.1741E-01 2.1842E-01 2.2022E-01 2.1847E-01 2.2531E-01 2.2922E-01 2.3357E-01 2.2653E-01 2.2553E-01 2.3334E-01 2.2149E-01 2.1898E-01 2.1404E-01 2.0687E-01 2.0502E-01 3.9051E-01 4.0412E-01 3.9635E-01 3.8839E-01 4.1284E-01 4.0607E-01 3.8462E-01 3.6642E-01 3.7136E-01 3.7867E-01 3.6574E-01 3.6415E-01 3.7138E-01 3.6498E-01 3.7432E-01 3.4829E-01 3.6016E-01 3.5197E-01 3.5562E-01 3.4679E-01 1.9172E-01 1.8714E-01 1.9234E-01 1.9352E-01 1.9191E-01 1.9617E-01 2.0010E-01 1.9858E-01 2.0123E-01 2.0206E-01 2.0635E-01 2.1111E-01 2.0766E-01 2.0181E-01 2.0822E-01 2.0551E-01 2.0924E-01 1.9478E-01 1.9724E-01 1.8091E-01 3.5742E-01 3.5532E-01 3.6807E-01 3.6497E-01 3.7623E-01 3.6948E-01 3.7050E-01 3.6630E-01 3.5067E-01 3.5491E-01 3.4747E-01 3.5843E-01 3.3884E-01 3.5252E-01 3.3743E-01 3.3847E-01 3.4211E-01 3.3848E-01 3.4094E-01 3.3532E-01 1.7413E-01 1.7360E-01 1.7755E-01 1.7530E-01 1.7582E-01 1.7831E-01 1.8249E-01 1.7984E-01 1.8538E-01 1.8713E-01 1.8532E-01 2.0044E-01 1.9360E-01 1.9451E-01 1.9506E-01 1.8139E-01 1.8210E-01 1.7556E-01 1.8051E-01 1.7436E-01 3.3760E-01 3.2984E-01 3.2644E-01 3.3663E-01 3.2413E-01 3.3688E-01 3.2775E-01 3.4090E-01 3.3087E-01 3.3502E-01 3.2118E-01 3.2805E-01 3.3188E-01 3.1202E-01 3.2223E-01 3.0492E-01 3.2158E-01 3.2175E-01 3.2504E-01 3.1561E-01 1.5933E-01 1.5807E-01 1.5628E-01 1.5802E-01 1.6220E-01 1.6400E-01 1.6418E-01 1.6522E-01 1.6499E-01 1.7237E-01 1.7975E-01 1.7732E-01 1.7384E-01 1.7000E-01 1.7503E-01 1.7246E-01 1.7114E-01 1.6874E-01 1.6747E-01 1.5771E-01 3.2620E-01 3.3008E-01 3.1914E-01 3.2056E-01 2.9256E-01 3.2274E-01 3.2099E-01 3.0202E-01 2.9919E-01 3.1900E-01 3.0415E-01 3.0039E-01 3.1161E-01 3.0892E-01 2.9439E-01 3.0298E-01 2.9774E-01 2.8946E-01 2.9899E-01 2.9950E-01 2.2439E+00 2.2397E+00 2.2657E+00 2.3151E+00 2.3519E+00 2.3934E+00 2.4476E+00 2.4972E+00 2.5533E+00 2.6296E+00 2.7373E+00 2.7554E+00 2.8085E+00 2.8325E+00 2.8624E+00 2.8862E+00 2.8557E+00 2.9162E+00 2.8932E+00 2.9101E+00 5.8102E+00 5.8506E+00 5.8606E+00 5.9540E+00 5.9586E+00 5.9470E+00 5.9451E+00 6.0028E+00 6.0099E+00 6.0044E+00 6.0596E+00 6.0260E+00 6.0479E+00 6.0029E+00 6.0446E+00 6.0148E+00 5.9731E+00 5.9715E+00 5.9642E+00 6.0094E+00 # Rd[r][angle]. [1/(cm2sr)]. # Rd[0][0], [0][1],..[0][na-1] # Rd[1][0], [1][1],..[1][na-1] # ... # Rd[nr-1][0], [nr-1][1],..[nr-1][na-1] Rd_ra 6.2665E+00 2.0408E+00 1.2399E+00 8.9192E-01 7.1204E-01 5.7809E-01 4.9331E-01 4.1930E-01 3.5898E-01 3.1224E-01 2.7205E-01 2.4329E-01 2.0911E-01 1.8121E-01 1.6070E-01 1.4222E-01 1.2461E-01 1.1051E-01 9.8317E-02 8.6277E-02 7.6516E-02 6.6669E-02 6.0786E-02 5.3541E-02 4.7814E-02 4.2100E-02 3.7807E-02 3.3432E-02 3.0113E-02 2.8499E-02 2.5054E-02 2.2198E-02 2.0252E-02 1.8092E-02 1.7005E-02 1.4580E-02 1.3261E-02 1.1831E-02 1.0867E-02 9.8794E-03 9.2208E-03 7.8531E-03 7.4345E-03 6.4558E-03 6.3524E-03 5.4264E-03 5.1475E-03 4.6185E-03 4.2175E-03 6.3678E-02 # Tt[r][angle]. [1/(cm2sr)]. # Tt[0][0], [0][1],..[0][na-1] # Tt[1][0], [1][1],..[1][na-1] # ... # Tt[nr-1][0], [nr-1][1],..[nr-1][na-1] Tt_ra 1.1944E-01 1.2176E-01 1.1440E-01 1.1172E-01 1.0873E-01 1.0862E-01 1.0543E-01 9.9685E-02 9.4394E-02 8.8334E-02 8.3305E-02 8.0762E-02 7.4240E-02 6.8787E-02 6.5094E-02 6.0172E-02 5.6823E-02 5.3056E-02 4.8362E-02 4.4868E-02 4.2564E-02 3.8598E-02 3.4775E-02 3.2608E-02 3.0565E-02 2.6829E-02 2.5429E-02 2.3391E-02 2.0222E-02 1.9086E-02 1.7185E-02 1.5949E-02 1.5039E-02 1.3471E-02 1.2305E-02 1.1357E-02 1.0496E-02 9.4820E-03 8.8253E-03 8.2067E-03 7.3625E-03 6.9774E-03 6.2567E-03 6.0037E-03 5.4003E-03 4.8383E-03 4.5766E-03 4.3200E-03 3.8424E-03 6.7666E-02

__MACOSX/FTP-Uploads/ch04Convolution/._SAMPLE.MCO

__MACOSX/FTP-Uploads/._ch04Convolution

FTP-Uploads/ch08Ballistic/Ch08Ex03PSF.m

% Example 8.3 u = linspace(-1,1)*3*4*pi; subplot(2, 1, 1) plot(u, (sinc(u/4/pi)).^2, 'k--', u, (sinc(u/4/pi)).^4, 'k-') grid xlabel('u') ylabel('Axial PSF') legend('Conventional', 'Confocal') v = linspace(0,10); subplot(2, 1, 2) plot(v, (2*besselj(1,v)./v).^2, 'k--', v, (2*besselj(1,v)./v).^4, 'k-') grid xlabel('v') ylabel('Lateral PSF') legend('Conventional', 'Confocal')

__MACOSX/FTP-Uploads/ch08Ballistic/._Ch08Ex03PSF.m

__MACOSX/FTP-Uploads/._ch08Ballistic

FTP-Uploads/ch09OCT/Ch09Ex03Demodulation.m

% Example 9.3 % Use SI units throughout lambda0 = 830E-9; % center wavelength dlambda = 60E-9; % bandwidth (delta lambda) c = 3E8; % speed of light lc = 4*log(2)/pi*lambda0^2/dlambda % coherence length Number_of_periods = 0.5*lc/(lambda0/2) % # of periods in FWHM figure(1); N = 2^12; % number of sampling points dl = lc*linspace(-2,2, N); % array for Delta_l k0 = 2*pi/lambda0; % propagation constant subplot(4, 1, 1) % interferogram Iac = exp(-16*log(2)*(dl/lc).^2) .* cos(2*k0 * dl); plot(dl/lc, Iac, 'k') title('(a) Interferogram') xlabel('\Deltal/l_c') ylabel('Signal') axis([-0.6, 0.6, -1, 1]) subplot(4, 1, 2) % rectified interferogram Irec = abs(Iac); plot(dl/lc, Irec, 'k') title('(b) Rectified interferogram') xlabel('\Deltal/l_c') ylabel('Signal') axis([-0.6, 0.6, -1, 1]) subplot(4, 1, 3) % spectrum of the rectified interferogram Frec1 = fft(Irec)/sqrt(N); % order of frequencies: 0,1...(N/2-1),-N/2,-(N/2-1)...-1 Frec2 = fftshift(Frec1); % shifted order of frequencies: -N/2,-(N/2-1)...-1, 0,1...(N/2-1) dfreq = 1/(4*lc); % freq bin size = 1/sampling range freq = dfreq*(-N/2:N/2-1); % frequency array plot(freq*lambda0, abs(Frec2), 'k') title('(c) Spectrum of the rectified interferogram') xlabel('Frequency (1/\lambda_0)') ylabel('Amplitude') axis([-10, 10, 0, 5]) subplot(4, 1, 4) % envelope freq_cut = 1/lambda0/2; % cut-off frequency for filtering i_cut = round(freq_cut/dfreq); % convert freq_cut to an array index Ffilt = Frec1; % initialize array Ffilt(i_cut:N-i_cut+1) = 0; % filter Ifilt = abs(ifft(Ffilt))*sqrt(N); % amplitude of inverse FFT plot(dl/lc, Ifilt/max(Ifilt), 'k') Iac_en = exp(-16*log(2)*(dl/lc).^2); % envelope hold on; plot(dl(1:N/32:N)/lc, Iac_en(1:N/32:N), 'ko') hold off; title('(d) Envelopes') xlabel('\Deltal/l_c') ylabel('Signals') axis([-0.6, 0.6, -1, 1]) legend('Demodulated','Original')

__MACOSX/FTP-Uploads/ch09OCT/._Ch09Ex03Demodulation.m

FTP-Uploads/ch09OCT/Ch09Ex04FDOCT.m

% Example 9.4 % Use SI units throughout lambda0 = 830E-9; % center wavelength of source dlambda = 20E-9; % FWHM wavelength bandwidth of source ns=1.38; % refractive index of sample ls1 = 100E-6; % location of backscatterer 1 ls2 = 150E-6; % location of backscatterer 2 rs1 = 0.5; % reflectivity of backscatterer 1 rs2 = 0.25; % reflectivity of backscatterer 2 k0=2*pi/lambda0; % center propagation constant delta_k=2*pi*dlambda/lambda0^2; % FWHM bandwidth of k sigma_k = delta_k/sqrt(2*log(2)); % standard deviation of k N=2^10; % number of sampling points nsigma = 5; % number of standard deviations to plot on each side of k0 subplot(4,1,1); % Generate the interferogram k = k0 + sigma_k*linspace(-nsigma,nsigma, N); % array for k S_k = exp(-(1/2)*(k-k0).^2/sigma_k^2); % Gaussian source PSD E_s1 = rs1*exp(i*2*k*ns*ls1); % sample electric field from scatter 1 E_s2 = rs2*exp(i*2*k*ns*ls2); % sample electric field from scatter 2 I_k1 = S_k .* abs(1 + E_s1 + E_s2).^2; % interferogram (r_R = 1) plot(k/k0,I_k1/max(I_k1), 'k'); title('Interferogram'); xlabel('Propagation constant k/k_0'); ylabel('Normalized intensity'); axis([0.9 1.1 0 1]); subplot(4,1,2); % Inverse Fourier transform (IFT) of the interferogram spec1=abs(fftshift(ifft(I_k1)))/sqrt(N); dls_prime = 1/(2*nsigma*sigma_k/(2*pi)); % freq bin size = 1/sampling range ls_prime = dls_prime*(-N/2:N/2-1); % frequency array plot(ls_prime/(2*ns),spec1/max(spec1), 'k'); % scale the frequency title('IFT of the interferogram'); xlabel('Depth ls (m)'); ylabel('Relative reflectivity'); axis([-2*ls2 2*ls2 0 1]); subplot(4,1,3); % IFT of the deconvolved interferogram spec1_norm =abs(fftshift(ifft(I_k1./S_k)))/sqrt(N); dls_prime = 1/(2*nsigma*sigma_k/(2*pi)); % bin size = 1/sampling range ls_prime = dls_prime*(-N/2:N/2-1); % frequency array plot(ls_prime/(2*ns),spec1_norm/max(spec1_norm), 'k'); title('IFT of the deconvolved interferogram'); xlabel('Depth ls (m)'); ylabel('Relative reflectivity'); axis([-2*ls2 2*ls2 0 1]); subplot(4,1,4); % IFT of the deconvolved differential interferogram I_k2 = S_k .* abs(-1 + E_s1 + E_s2).^2; % interferogram delta_I_k = I_k1 - I_k2; spec2=abs(fftshift(ifft(delta_I_k./S_k)))/sqrt(N); plot(ls_prime/(2*ns),spec2/max(spec2), 'k'); title('IFT of the deconvolved differential interferogram'); xlabel('Depth ls (m)'); ylabel('Relative reflectivity'); axis([-2*ls2 2*ls2 0 1]);

__MACOSX/FTP-Uploads/ch09OCT/._Ch09Ex04FDOCT.m

__MACOSX/FTP-Uploads/._ch09OCT

FTP-Uploads/ch11DOT/Ch11Ex01NullPlane.m

% Example 11.1 c = 3e8/1.37; %[m/s] mua = 0.1E2; %[/m] mus = 10E2; %[/m] mus = mus', g = 0 f = 200E6; %[Hz] D = 1/(3*(mua+mus)); %[m] k = sqrt((-c*mua + i*2*pi*f)/(c*D)); %wave vector disp(['Absorption coeff. mua [/cm] = ', num2str(mua*1E-2)]) disp(['Reduced scattering coeff. mus'' [/cm] = ', num2str(mus*1E-2)]) disp(['Frequency f [MHz] = ', num2str(f*1E-6)]) disp(['Wavelength [cm] = ', num2str(2*pi/real(k)*1E2)]) disp(['Decay const [cm] = ', num2str(1/imag(k)*1E2)]) xs = 1E-2; %[m] sources at (xs, 0) & (-xs, 0) yd = 3E-2; %[m] detector at (xd, yd) xd = (-3:0.02:3)*1E-2; %[m] r1 = sqrt((xd - xs).^2 + yd.^2); % distance b/t -src & detector U1 = -exp(i*k*r1)./(4*pi*c*D*r1); % negative src r2 = sqrt((xd + xs).^2 + yd.^2); % distance b/t +src & detector U2 = exp(i*k*r2)./(4*pi*c*D*r2); figure(1) subplot(3,1,1) plot([-xs xs]*1E2, [0, 0], '*', [0], [3], 'o', [0 0], [0 3]) text(-xs*1E2, 0.5, '+Source') text(+xs*1E2, 0.5, '-Source') text(0, 2.5, 'Scanning Detector') axis([-3 3 0 3]) xlabel('Source & Detector Positions [cm]') ylabel('y [cm]') title('Null Line') subplot(3,1,2) plot(xd*1E2, abs(U1+U2)) xlabel('Detector Position [cm]') ylabel('Amplitude') subplot(3,1,3) plot(xd*1E2, unwrap(angle(U1+U2))*180/pi) xlabel('Detector Position [cm]') ylabel('Angle [deg]') grid figure(2) r = (1:1:20)*1E-2; U = exp(i*k*r)./(4*pi*c*D*r); subplot(2,1,1) semilogy(r*1E2, abs(U)) xlabel('Source-Detector Distance [cm]') ylabel('Amplitude') title('Propagation of PDW') subplot(2,1,2) plot(r*1E2, unwrap(angle(U))*180/pi) xlabel('Source-Detector Distance [cm]') ylabel('Angle [deg]') grid

__MACOSX/FTP-Uploads/ch11DOT/._Ch11Ex01NullPlane.m

FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/bp.c

/* Backprojection for imaging with photon density wave. 2/23/1998, LW. */ #include <stdio.h> #include <math.h> #include <float.h> #include "nrutil.h" void svdcmp(float **a, int m, int n, float w[], float **v); #define SQ(x) ((x)*(x)) #ifndef PI #define PI 3.141592653589793 #endif typedef struct { double r, i; } complex; typedef struct { float f; /* frequency [Hz]. */ float c; /* speed of light in medium [cm/s]. */ float h; /* grid size [cm], dx = dy = dz = h. */ int N; /* NxN grid. */ int gap; /* gap between src/det & boundary [grid]. */ float mua0; /* mua of background [/cm]. */ float mus0; /* mus' of background [/cm]. */ int obj_x, obj_y, obj_size; /* object location and size in grid. */ float dmua; /* delta mua of object [/cm]. */ } ParamStru; typedef struct { float *VecUsc; /* vector U. Real part 1st. */ float **MatW; /* matrix W: weight. Real part 1st. */ float *VecMua; /* vector mua. */ } ArrayStru; complex C(double rl, double im) { complex c; c.r = rl; c.i = im; return (c); } double Re(complex C) { return (C.r); } double Im(complex C) { return (C.i); } double CAbs(complex C) { return (sqrt(C.r * C.r + C.i * C.i)); } complex CAdd(complex C1, complex C2) { complex c; c.r = C1.r + C2.r; c.i = C1.i + C2.i; return (c); } complex CSub(complex C1, complex C2) { complex c; c.r = C1.r - C2.r; c.i = C1.i - C2.i; return (c); } /* (a + ib)(c + id) = ac-bd + i(bc+ad) */ complex CMul(complex C1, complex C2) { complex c; c.r = C1.r * C2.r - C1.i * C2.i; c.i = C1.r * C2.i + C1.i * C2.r; return (c); } /* (a + ib)/(c + id) = (a+ib)(c-id)/(c^2+d^2) */ complex CDiv(complex C1, complex C2) { double temp; complex c; temp = 1 / (C2.r * C2.r + C2.i * C2.i); c.r = (C1.r * C2.r + C1.i * C2.i) * temp; c.i = (C1.i * C2.r - C1.r * C2.i) * temp; return (c); } complex CC(complex C) { complex ctemp; ctemp.r = C.r; ctemp.i = -C.i; return (ctemp); } complex CSqrt(complex C) { double amp, ang; complex ctemp; amp = sqrt(CAbs(C)); ang = 0.5 * atan2(Im(C), Re(C)); ctemp.r = amp * cos(ang); ctemp.i = amp * sin(ang); return (ctemp); } /* Calculate exp(C). */ complex CExp(complex C) { double amp, ang; complex ctemp; amp = exp(Re(C)); ang = Im(C); ctemp.r = amp * cos(ang); ctemp.i = amp * sin(ang); return (ctemp); } /**************************************************************** * Report error message to stderr, then exit the program * with signal 1. ****/ void RepError(char *ErrorText) { fprintf(stderr, "%s\n", ErrorText); fprintf(stderr, "...now exiting to system...\n"); exit(1); } void ShowProg(void) { puts(""); puts("Back Projection of Photon Density Wave"); puts("Lihong Wang, Ph.D."); puts("Texas A&M University"); puts("Version date: 2/23/1998"); puts(""); } void SetParam(ParamStru *par) { par->f = 200E6; par->c = 3E10 / 1.37; par->h = 0.2; par->N = 20; /* number of voxels in each direction. */ par->gap = 3; par->mua0 = 0.1; par->mus0 = 10; par->obj_x = 8; par->obj_y = 10; par->obj_size = 5; par->dmua = 0.01; } void AllocArrays(ParamStru par, ArrayStru *arr) { int num_rows = 2 *par.N *par.N; /* NxN measurements. */ int num_cols = par.N *par.N; arr->VecUsc = vector(1, num_rows); arr->VecMua = vector(1, num_cols); arr->MatW = matrix(1, num_rows, 1, num_cols); } complex Green(ParamStru par, int ix, int iy) { static int once = 1; complex k0, G; float D = 1 / (3 *(par.mua0 + par.mus0)); float r = sqrt(SQ(ix *par.h) + SQ(iy *par.h)); k0 = CSqrt(C(-par.mua0 / D, 2 * PI * par.f / (par.c * D))); k0 = CMul(k0, C(0, -r)); /* -ikr = k*(-r). */ G = CExp(k0); G = CDiv(G, C(4 * PI * r, 0)); if (once == 1) { /* printf("G = %e\n", G); */ once = 2; } return (G); } complex U0(ParamStru par, int ix, int iy) { complex U0; float D = 1 / (3 *(par.mua0 + par.mus0)); U0 = Green(par, ix, iy); U0 = CDiv(U0, C(par.c * D, 0)); return (U0); } void CalcUsc(ParamStru par, int ixs, int iys, int ixd, int iyd, float *Usc_re, float *Usc_im) { complex kernal, Usc; int ixo, iyo; float D = 1 / (3 *(par.mua0 + par.mus0)); Usc = C(0, 0); for (ixo = par.obj_x; ixo <= par.obj_x + par.obj_size; ixo++) for (iyo = par.obj_y; iyo <= par.obj_y + par.obj_size; iyo++) { kernal = CMul(U0(par, ixo - ixs, iyo - iys), Green(par, ixd - ixo, iyd - iyo)); kernal = CMul(C(-par.dmua / D, 0), kernal); Usc = CAdd(Usc, kernal); } *Usc_re = Re(Usc) * pow(par.h, 3); *Usc_im = Im(Usc) * pow(par.h, 3); } void CalcVectorUsc(ParamStru par, float *Usc) { int iys, iyd, isd, i_shift; i_shift = par.N * par.N; /* printf("Usc:\n"); */ for (iyd = 1; iyd <= par.N; iyd++) { for (iys = 1; iys <= par.N; iys++) { isd = iys + (iyd - 1) * par.N; CalcUsc(par, -par.gap, iys, par.N + par.gap, iyd, &Usc[isd], &Usc[isd + i_shift]); /* printf("%E\t%E\t", Usc[isd], Usc[isd + i_shift]); */ } } } void CalcW(ParamStru par, int ixs, int iys, int ixd, int iyd, int ixo, int iyo, float *W_re, float *W_im) { complex W; float D = 1 / (3 *(par.mua0 + par.mus0)); W = CMul(U0(par, ixo - ixs, iyo - iys), Green(par, ixd - ixo, iyd - iyo)); *W_re = -Re(W) * pow(par.h, 3) / D; *W_im = -Im(W) * pow(par.h, 3) / D; } void CalcMatrixW(ParamStru par, float **W) { int iys, iyd, ixo, iyo; /* indices to src, det, obj. */ int isd, iob; /* combined indices. */ int i_shift; i_shift = par.N * par.N; /* printf("\nW:\n"); */ for (iyd = 1; iyd <= par.N; iyd++) for (iys = 1; iys <= par.N; iys++) for (iyo = 1; iyo <= par.N; iyo++) for (ixo = 1; ixo <= par.N; ixo++) { isd = iys + (iyd - 1) * par.N; iob = ixo + (iyo - 1) * par.N; CalcW(par, -par.gap, iys, par.N + par.gap, iyd, ixo, iyo, &W[isd][iob], &W[isd + i_shift][iob]); /* printf("%e\t%e\t", W[isd][iob], W[isd+i_shift][iob]); */ } } void CalcVectorMua(ParamStru par, ArrayStru *arr) { int m, n; float *w, **v; int i, j, k; m = 2 * par.N * par.N; n = par.N * par.N; w = vector(1, n); v = matrix(1, n, 1, n); svdcmp(arr->MatW, m, n, w, v); for (i = 1; i <= n; i++) { arr->VecMua[i] = 0; for (j = 1; j <= n; j++) for (k = 1; k <= m; k++) arr->VecMua[i] += v[i][j] / w[j] * arr->MatW[k][j] * arr->VecUsc[k]; } free_vector(w, 1, n); free_matrix(v, 1, n, 1, n); } void FPrintMu(ParamStru par, float *mua) { FILE * fp; int ix, iy, ixy; fp = fopen("bp.im", "w"); for (iy = 1; iy <= par.N; iy++) { for (ix = 1; ix <= par.N; ix++) { ixy = ix + (iy - 1) * par.N; if (ix < par.N) fprintf(fp, "%.5E\t", par.mua0 + mua[ixy]); else fprintf(fp, "%.5E\n", par.mua0 + mua[ixy]); } } fclose(fp); } void main(int argc, char *argv[]) { ParamStru params; ArrayStru arrays; ShowProg(); SetParam(&params); AllocArrays(params, &arrays); CalcVectorUsc(params, arrays.VecUsc); printf("Done Calc Usc\n"); CalcMatrixW(params, arrays.MatW); printf("Done Calc W\n"); CalcVectorMua(params, &arrays); printf("Done Calc Mua\n"); FPrintMu(params, arrays.VecMua); }

__MACOSX/FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/._bp.c

FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/Makefile

# Turn on -g in CFLAGS for dbx; -O in CFLAGS for optimized object code. # Do not turn on -g & -O simultaneously. CFLAGS = -g CC=cc RM=/bin/rm -f LIBS= -lm OBJS = bp.o nrutil.o nrfunc.o EXEC = bp INC = nrutil.h BINDIR = ~/bin all: $(EXEC) $(EXEC): $(OBJS) $(RM) $@; $(CC) -o $@ $(OBJS) $(LIBS) #implicit rule .c dependence, .o target. $@ means the matched target. .c.o: $(RM) $@; $(CC) -c $(CFLAGS) $*.c bp.o: $(INC) nrutil.o: $(INC) nrfunc.o: $(INC) clean: $(RM) $(EXEC) $(RM) $(OBJS) install: mv $(EXEC) $(BINDIR)

__MACOSX/FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/._Makefile

FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/nrfunc.c

/* Selected functions from Numerical Recipes. */ #include <math.h> #define NRANSI #include "nrutil.h" float pythag(float a, float b) { float absa, absb; absa = fabs(a); absb = fabs(b); if (absa > absb) return absa * sqrt(1.0 + SQR(absb / absa)); else return (absb == 0.0 ? 0.0 : absb * sqrt(1.0 + SQR(absa / absb))); } #undef NRANSI #define NRANSI #include "nrutil.h" void svdcmp(float **a, int m, int n, float w[], float **v) { float pythag(float a, float b); int flag, i, its, j, jj, k, l, nm; float anorm, c, f, g, h, s, scale, x, y, z, *rv1; rv1 = vector(1, n); g = scale = anorm = 0.0; for (i = 1; i <= n; i++) { l = i + 1; rv1[i] = scale * g; g = s = scale = 0.0; if (i <= m) { for (k = i; k <= m; k++) scale += fabs(a[k][i]); if (scale) { for (k = i; k <= m; k++) { a[k][i] /= scale; s += a[k][i] * a[k][i]; } f = a[i][i]; g = -SIGN(sqrt(s), f); h = f * g - s; a[i][i] = f - g; for (j = l; j <= n; j++) { for (s = 0.0, k = i; k <= m; k++) s += a[k][i] * a[k][j]; f = s / h; for (k = i; k <= m; k++) a[k][j] += f * a[k][i]; } for (k = i; k <= m; k++) a[k][i] *= scale; } } w[i] = scale * g; g = s = scale = 0.0; if (i <= m && i != n) { for (k = l; k <= n; k++) scale += fabs(a[i][k]); if (scale) { for (k = l; k <= n; k++) { a[i][k] /= scale; s += a[i][k] * a[i][k]; } f = a[i][l]; g = -SIGN(sqrt(s), f); h = f * g - s; a[i][l] = f - g; for (k = l; k <= n; k++) rv1[k] = a[i][k] / h; for (j = l; j <= m; j++) { for (s = 0.0, k = l; k <= n; k++) s += a[j][k] * a[i][k]; for (k = l; k <= n; k++) a[j][k] += s * rv1[k]; } for (k = l; k <= n; k++) a[i][k] *= scale; } } anorm = FMAX(anorm, (fabs(w[i]) + fabs(rv1[i]))); } for (i = n; i >= 1; i--) { if (i < n) { if (g) { for (j = l; j <= n; j++) v[j][i] = (a[i][j] / a[i][l]) / g; for (j = l; j <= n; j++) { for (s = 0.0, k = l; k <= n; k++) s += a[i][k] * v[k][j]; for (k = l; k <= n; k++) v[k][j] += s * v[k][i]; } } for (j = l; j <= n; j++) v[i][j] = v[j][i] = 0.0; } v[i][i] = 1.0; g = rv1[i]; l = i; } for (i = IMIN(m, n); i >= 1; i--) { l = i + 1; g = w[i]; for (j = l; j <= n; j++) a[i][j] = 0.0; if (g) { g = 1.0 / g; for (j = l; j <= n; j++) { for (s = 0.0, k = l; k <= m; k++) s += a[k][i] * a[k][j]; f = (s / a[i][i]) * g; for (k = i; k <= m; k++) a[k][j] += f * a[k][i]; } for (j = i; j <= m; j++) a[j][i] *= g; } else for (j = i; j <= m; j++) a[j][i] = 0.0; ++a[i][i]; } for (k = n; k >= 1; k--) { for (its = 1; its <= 30; its++) { flag = 1; for (l = k; l >= 1; l--) { nm = l - 1; if ((float) (fabs(rv1[l]) + anorm) == anorm) { flag = 0; break; } if ((float) (fabs(w[nm]) + anorm) == anorm) break; } if (flag) { c = 0.0; s = 1.0; for (i = l; i <= k; i++) { f = s * rv1[i]; rv1[i] = c * rv1[i]; if ((float) (fabs(f) + anorm) == anorm) break; g = w[i]; h = pythag(f, g); w[i] = h; h = 1.0 / h; c = g * h; s = -f * h; for (j = 1; j <= m; j++) { y = a[j][nm]; z = a[j][i]; a[j][nm] = y * c + z * s; a[j][i] = z * c - y * s; } } } z = w[k]; if (l == k) { if (z < 0.0) { w[k] = -z; for (j = 1; j <= n; j++) v[j][k] = -v[j][k]; } break; } if (its == 30) nrerror("no convergence in 30 svdcmp iterations"); x = w[l]; nm = k - 1; y = w[nm]; g = rv1[nm]; h = rv1[k]; f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y); g = pythag(f, 1.0); f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x; c = s = 1.0; for (j = l; j <= nm; j++) { i = j + 1; g = rv1[i]; y = w[i]; h = s * g; g = c * g; z = pythag(f, h); rv1[j] = z; c = f / z; s = h / z; f = x * c + g * s; g = g * c - x * s; h = y * s; y *= c; for (jj = 1; jj <= n; jj++) { x = v[jj][j]; z = v[jj][i]; v[jj][j] = x * c + z * s; v[jj][i] = z * c - x * s; } z = pythag(f, h); w[j] = z; if (z) { z = 1.0 / z; c = f * z; s = h * z; } f = c * g + s * y; x = c * y - s * g; for (jj = 1; jj <= m; jj++) { y = a[jj][j]; z = a[jj][i]; a[jj][j] = y * c + z * s; a[jj][i] = z * c - y * s; } } rv1[l] = 0.0; rv1[k] = f; w[k] = x; } } free_vector(rv1, 1, n); } #undef NRANSI

__MACOSX/FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/._nrfunc.c

FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/nrutil.c

/* CAUTION: This is the ANSI C (only) version of the Numerical Recipes utility file nrutil.c. Do not confuse this file with the same-named file nrutil.c that is supplied in the same subdirectory or archive as the header file nrutil.h. *That* file contains both ANSI and traditional K&R versions, along with #ifdef macros to select the correct version. *This* file contains only ANSI C. */ #include <stdio.h> #include <stddef.h> #include <stdlib.h> #include "nrutil.h" #define NR_END 1 #define FREE_ARG char* void nrerror(char error_text[]) /* Numerical Recipes standard error handler */ { fprintf(stderr, "Numerical Recipes run-time error...\n"); fprintf(stderr, "%s\n", error_text); fprintf(stderr, "...now exiting to system...\n"); exit(1); } float *vector(long nl, long nh) /* allocate a float vector with subscript range v[nl..nh] */ { float *v; v = (float *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(float))); if (!v) nrerror("allocation failure in vector()"); return v - nl + NR_END; } int *ivector(long nl, long nh) /* allocate an int vector with subscript range v[nl..nh] */ { int *v; v = (int *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(int))); if (!v) nrerror("allocation failure in ivector()"); return v - nl + NR_END; } unsigned char *cvector(long nl, long nh) /* allocate an unsigned char vector with subscript range v[nl..nh] */ { unsigned char *v; v = (unsigned char *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(unsigned char))); if (!v) nrerror("allocation failure in cvector()"); return v - nl + NR_END; } unsigned long *lvector(long nl, long nh) /* allocate an unsigned long vector with subscript range v[nl..nh] */ { unsigned long *v; v = (unsigned long *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(long))); if (!v) nrerror("allocation failure in lvector()"); return v - nl + NR_END; } double *dvector(long nl, long nh) /* allocate a double vector with subscript range v[nl..nh] */ { double *v; v = (double *) malloc((size_t) ((nh - nl + 1 + NR_END) * sizeof(double))); if (!v) nrerror("allocation failure in dvector()"); return v - nl + NR_END; } float **matrix(long nrl, long nrh, long ncl, long nch) /* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */ { long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1; float **m; /* allocate pointers to rows */ m = (float **) malloc((size_t) ((nrow + NR_END) * sizeof(float *))); if (!m) nrerror("allocation failure 1 in matrix()"); m += NR_END; m -= nrl; /* allocate rows and set pointers to them */ m[nrl] = (float *) malloc((size_t) ((nrow * ncol + NR_END) * sizeof(float))); if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); m[nrl] += NR_END; m[nrl] -= ncl; for (i = nrl + 1; i <= nrh; i++) m[i] = m[i - 1] + ncol; /* return pointer to array of pointers to rows */ return m; } double **dmatrix(long nrl, long nrh, long ncl, long nch) /* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */ { long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1; double **m; /* allocate pointers to rows */ m = (double **) malloc((size_t) ((nrow + NR_END) * sizeof(double *))); if (!m) nrerror("allocation failure 1 in matrix()"); m += NR_END; m -= nrl; /* allocate rows and set pointers to them */ m[nrl] = (double *) malloc((size_t) ((nrow * ncol + NR_END) * sizeof(double))); if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); m[nrl] += NR_END; m[nrl] -= ncl; for (i = nrl + 1; i <= nrh; i++) m[i] = m[i - 1] + ncol; /* return pointer to array of pointers to rows */ return m; } int **imatrix(long nrl, long nrh, long ncl, long nch) /* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */ { long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1; int **m; /* allocate pointers to rows */ m = (int **) malloc((size_t) ((nrow + NR_END) * sizeof(int *))); if (!m) nrerror("allocation failure 1 in matrix()"); m += NR_END; m -= nrl; /* allocate rows and set pointers to them */ m[nrl] = (int *) malloc((size_t) ((nrow * ncol + NR_END) * sizeof(int))); if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); m[nrl] += NR_END; m[nrl] -= ncl; for (i = nrl + 1; i <= nrh; i++) m[i] = m[i - 1] + ncol; /* return pointer to array of pointers to rows */ return m; } float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long oldch, long newrl, long newcl) /* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */ { long i, j, nrow = oldrh - oldrl + 1, ncol = oldcl - newcl; float **m; /* allocate array of pointers to rows */ m = (float **) malloc((size_t) ((nrow + NR_END) * sizeof(float *))); if (!m) nrerror("allocation failure in submatrix()"); m += NR_END; m -= newrl; /* set pointers to rows */ for (i = oldrl, j = newrl; i <= oldrh; i++, j++) m[j] = a[i] + ncol; /* return pointer to array of pointers to rows */ return m; } float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch) /* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1 and ncol=nch-ncl+1. The routine should be called with the address &a[0][0] as the first argument. */ { long i, j, nrow = nrh - nrl + 1, ncol = nch - ncl + 1; float **m; /* allocate pointers to rows */ m = (float **) malloc((size_t) ((nrow + NR_END) * sizeof(float *))); if (!m) nrerror("allocation failure in convert_matrix()"); m += NR_END; m -= nrl; /* set pointers to rows */ m[nrl] = a - ncl; for (i = 1, j = nrl + 1; i < nrow; i++, j++) m[j] = m[j - 1] + ncol; /* return pointer to array of pointers to rows */ return m; } float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh) /* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */ { long i, j, nrow = nrh - nrl + 1, ncol = nch - ncl + 1, ndep = ndh - ndl + 1; float ***t; /* allocate pointers to pointers to rows */ t = (float ***) malloc((size_t) ((nrow + NR_END) * sizeof(float **))); if (!t) nrerror("allocation failure 1 in f3tensor()"); t += NR_END; t -= nrl; /* allocate pointers to rows and set pointers to them */ t[nrl] = (float **) malloc((size_t) ((nrow * ncol + NR_END) * sizeof(float *))); if (!t[nrl]) nrerror("allocation failure 2 in f3tensor()"); t[nrl] += NR_END; t[nrl] -= ncl; /* allocate rows and set pointers to them */ t[nrl][ncl] = (float *) malloc((size_t) ((nrow * ncol * ndep + NR_END) * sizeof(float))); if (!t[nrl][ncl]) nrerror("allocation failure 3 in f3tensor()"); t[nrl][ncl] += NR_END; t[nrl][ncl] -= ndl; for (j = ncl + 1; j <= nch; j++) t[nrl][j] = t[nrl][j - 1] + ndep; for (i = nrl + 1; i <= nrh; i++) { t[i] = t[i - 1] + ncol; t[i][ncl] = t[i - 1][ncl] + ncol * ndep; for (j = ncl + 1; j <= nch; j++) t[i][j] = t[i][j - 1] + ndep; } /* return pointer to array of pointers to rows */ return t; } void free_vector(float *v, long nl, long nh) /* free a float vector allocated with vector() */ { free((FREE_ARG) (v + nl - NR_END)); } void free_ivector(int *v, long nl, long nh) /* free an int vector allocated with ivector() */ { free((FREE_ARG) (v + nl - NR_END)); } void free_cvector(unsigned char *v, long nl, long nh) /* free an unsigned char vector allocated with cvector() */ { free((FREE_ARG) (v + nl - NR_END)); } void free_lvector(unsigned long *v, long nl, long nh) /* free an unsigned long vector allocated with lvector() */ { free((FREE_ARG) (v + nl - NR_END)); } void free_dvector(double *v, long nl, long nh) /* free a double vector allocated with dvector() */ { free((FREE_ARG) (v + nl - NR_END)); } void free_matrix(float **m, long nrl, long nrh, long ncl, long nch) /* free a float matrix allocated by matrix() */ { free((FREE_ARG) (m[nrl] + ncl - NR_END)); free((FREE_ARG) (m + nrl - NR_END)); } void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch) /* free a double matrix allocated by dmatrix() */ { free((FREE_ARG) (m[nrl] + ncl - NR_END)); free((FREE_ARG) (m + nrl - NR_END)); } void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch) /* free an int matrix allocated by imatrix() */ { free((FREE_ARG) (m[nrl] + ncl - NR_END)); free((FREE_ARG) (m + nrl - NR_END)); } void free_submatrix(float **b, long nrl, long nrh, long ncl, long nch) /* free a submatrix allocated by submatrix() */ { free((FREE_ARG) (b + nrl - NR_END)); } void free_convert_matrix(float **b, long nrl, long nrh, long ncl, long nch) /* free a matrix allocated by convert_matrix() */ { free((FREE_ARG) (b + nrl - NR_END)); } void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch, long ndl, long ndh) /* free a float f3tensor allocated by f3tensor() */ { free((FREE_ARG) (t[nrl][ncl] + ndl - NR_END)); free((FREE_ARG) (t[nrl] + ncl - NR_END)); free((FREE_ARG) (t + nrl - NR_END)); } /* (C) Copr. 1986-92 Numerical Recipes Software s%{,*;15M. */

__MACOSX/FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/._nrutil.c

FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/nrutil.h

#ifndef _NR_UTILS_H_ #define _NR_UTILS_H_ static float sqrarg; #define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg) static double dsqrarg; #define DSQR(a) ((dsqrarg=(a)) == 0.0 ? 0.0 : dsqrarg*dsqrarg) static double dmaxarg1, dmaxarg2; #define DMAX(a,b) (dmaxarg1=(a),dmaxarg2=(b),(dmaxarg1) > (dmaxarg2) ?\ (dmaxarg1) : (dmaxarg2)) static double dminarg1, dminarg2; #define DMIN(a,b) (dminarg1=(a),dminarg2=(b),(dminarg1) < (dminarg2) ?\ (dminarg1) : (dminarg2)) static float maxarg1, maxarg2; #define FMAX(a,b) (maxarg1=(a),maxarg2=(b),(maxarg1) > (maxarg2) ?\ (maxarg1) : (maxarg2)) static float minarg1, minarg2; #define FMIN(a,b) (minarg1=(a),minarg2=(b),(minarg1) < (minarg2) ?\ (minarg1) : (minarg2)) static long lmaxarg1, lmaxarg2; #define LMAX(a,b) (lmaxarg1=(a),lmaxarg2=(b),(lmaxarg1) > (lmaxarg2) ?\ (lmaxarg1) : (lmaxarg2)) static long lminarg1, lminarg2; #define LMIN(a,b) (lminarg1=(a),lminarg2=(b),(lminarg1) < (lminarg2) ?\ (lminarg1) : (lminarg2)) static int imaxarg1, imaxarg2; #define IMAX(a,b) (imaxarg1=(a),imaxarg2=(b),(imaxarg1) > (imaxarg2) ?\ (imaxarg1) : (imaxarg2)) static int iminarg1, iminarg2; #define IMIN(a,b) (iminarg1=(a),iminarg2=(b),(iminarg1) < (iminarg2) ?\ (iminarg1) : (iminarg2)) #define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a)) #if defined(__STDC__) || defined(ANSI) || defined(NRANSI) /* ANSI */ void nrerror(char error_text[]); float *vector(long nl, long nh); int *ivector(long nl, long nh); unsigned char *cvector(long nl, long nh); unsigned long *lvector(long nl, long nh); double *dvector(long nl, long nh); float **matrix(long nrl, long nrh, long ncl, long nch); double **dmatrix(long nrl, long nrh, long ncl, long nch); int **imatrix(long nrl, long nrh, long ncl, long nch); float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long oldch, long newrl, long newcl); float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch); float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh); void free_vector(float *v, long nl, long nh); void free_ivector(int *v, long nl, long nh); void free_cvector(unsigned char *v, long nl, long nh); void free_lvector(unsigned long *v, long nl, long nh); void free_dvector(double *v, long nl, long nh); void free_matrix(float **m, long nrl, long nrh, long ncl, long nch); void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch); void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch); void free_submatrix(float **b, long nrl, long nrh, long ncl, long nch); void free_convert_matrix(float **b, long nrl, long nrh, long ncl, long nch); void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch, long ndl, long ndh); #else /* ANSI */ /* traditional - K&R */ void nrerror(); float *vector(); float **matrix(); float **submatrix(); float **convert_matrix(); float ***f3tensor(); double *dvector(); double **dmatrix(); int *ivector(); int **imatrix(); unsigned char *cvector(); unsigned long *lvector(); void free_vector(); void free_dvector(); void free_ivector(); void free_cvector(); void free_lvector(); void free_matrix(); void free_submatrix(); void free_convert_matrix(); void free_dmatrix(); void free_imatrix(); void free_f3tensor(); #endif /* ANSI */ #endif /* _NR_UTILS_H_ */

__MACOSX/FTP-Uploads/ch11DOT/Ch11Ex03-Backprojection/._nrutil.h

__MACOSX/FTP-Uploads/ch11DOT/._Ch11Ex03-Backprojection

__MACOSX/FTP-Uploads/._ch11DOT

FTP-Uploads/ch12PAT/Ch12Ex07.m

% Example 12.7 % Photoacoustic signal from a homogeneously heated slab % Use SI units clear all vs = 1500; p0 = 1; L = 1E-3; Lhalf = L/2; zmax = 2; z = linspace(-zmax, zmax, 1000)*L; figure(1) clf i_axis = 1; for t = [0:1/2:1, 2]*Lhalf/vs ppos = p0/2.*heaviside(z-(-Lhalf+vs*t)).*heaviside(Lhalf+vs*t-z); pneg = p0/2.*heaviside(z-(-Lhalf-vs*t)).*heaviside(Lhalf-vs*t-z); p = ppos + pneg; subplot(4, 2, i_axis, 'align') plot(z/L, ppos/p0, 'k-', z/L, pneg/p0, 'k--') tick = [.015 .025]; set(0,'DefaultAxesTickLength',tick) title(['\itt\rm = ', num2str(vs*t/L), '\itxL\rm/\itv_s']) axis([-zmax, zmax, 0, 1.1]) if (i_axis == 7) xlabel('\itz\rm/\itL') end ylabel('Partial pressures/\it{p}\rm_0') if (i_axis == 1) legend('\itp\rm_+', '\itp\rm_-') end i_axis = i_axis + 1; subplot(4, 2, i_axis, 'align') plot(z/L, p/p0, 'k-') tick = [.015 .025]; set(0,'DefaultAxesTickLength',tick) title(['\itt\rm = ', num2str(vs*t/L), '\itxL\rm/\itv_s']) axis([-zmax, zmax, 0, 1.1]) if (i_axis == 8) xlabel('\itz\rm/\itL') end ylabel('Total pressure/\it{p}\rm_0') i_axis = i_axis + 1; end

__MACOSX/FTP-Uploads/ch12PAT/._Ch12Ex07.m

FTP-Uploads/ch12PAT/Ch12Ex07Movie.m

% Example 12.7 movie % Photoacoustic signal from a homogeneously heated slab % Use SI units fig=figure(1); set(fig,'DoubleBuffer','on'); % Flash-free rendering for animations clear all vs = 1500; p0 = 1; L = 1E-3; Lhalf = L/2; zmax = 2; z = linspace(-zmax, zmax, 1000)*L; mov = avifile('Example07_PA_Slab.avi') for t = [0:0.1:2]*Lhalf/vs ppos = p0/2.*heaviside(z-(-Lhalf+vs*t)).*heaviside(Lhalf+vs*t-z); pneg = p0/2.*heaviside(z-(-Lhalf-vs*t)).*heaviside(Lhalf-vs*t-z); p = ppos + pneg; subplot(1, 2, 1) hold off; plot(z/L, ppos/p0, 'k-', z/L, pneg/p0, 'k--') grid axis([-zmax, zmax, 0, 1.1]) xlabel('\itz/L') ylabel('Partial pressures/\itp\rm_0') legend('\itp\rm_+', '\itp\rm_-') subplot(1, 2, 2) hold off; plot(z/L, p/p0, 'k-') grid axis([-zmax, zmax, 0, 1.1]) xlabel('z/L') ylabel('Total pressure/\itp\rm_0') pause(0.01) mov = addframe(mov,getframe(gcf)); end mov = close(mov);

__MACOSX/FTP-Uploads/ch12PAT/._Ch12Ex07Movie.m

FTP-Uploads/ch12PAT/Ch12Ex08.m

% Example 12.8 % Photoacoustic signal from a homogeneously heated sphere % Use SI units clear all vs = 1500; p0 = 1; Rs = 0.5E-3; rd = 2*Rs; % Location of detector t = linspace(0, (rd + 2*Rs)/vs, 1000); figure(1) clf pin1 = p0/2*(1+vs*t./rd).*heaviside(rd+vs*t).*heaviside(Rs-rd-vs*t); pinr = p0/2*(1-vs*t./rd).*heaviside(-rd+vs*t).*heaviside(Rs+rd-vs*t); pout = p0/2*(1-vs*t./rd).*heaviside(rd-vs*t).*heaviside(Rs-rd+vs*t); p = pin1 + pinr + pout; plot(vs*t/Rs, p, 'k') tick = [.015 .025]; set(0,'DefaultAxesTickLength',tick) xlabel('Normalized time: \itv_s t\rm/\itR_s') ylabel('Normalized pressure: \itp\rm/\itp\rm_0') title('Pressure at \itr \rm= 2\itR_s')

__MACOSX/FTP-Uploads/ch12PAT/._Ch12Ex08.m

FTP-Uploads/ch12PAT/Ch12Ex09.m

% Example 12.9 % Photoacoustic signal from a homogeneously heated sphere % Use SI units clear all vs = 1500; p0 = 1; Rs = 0.5E-3; rmax = 4; rmin = 1E-3*Rs; r = linspace(0, rmax*Rs, 1000) + rmin; dr = rmax*Rs/(1000-1); theta = linspace(-pi/2, pi/2); figure(1) clf i_axis = 1; for t = [0:1/2:1, 2]*Rs/vs pin1 = p0/2*(1+vs*t./r).*heaviside(r+vs*t).*heaviside(Rs-r-vs*t); pinr = p0/2*(1-vs*t./r).*heaviside(-r+vs*t).*heaviside(Rs+r-vs*t); pout = p0/2*(1-vs*t./r).*heaviside(r-vs*t).*heaviside(Rs-r+vs*t); p = pin1 + pinr + pout; subplot(4, 2, i_axis, 'align') hold off; plot(r/Rs, pin1/p0, 'k--', ... r/Rs, pinr/p0, 'k-.', ... r/Rs, pout/p0, 'k-', ... cos(theta), sin(theta), 'k-') tick = [.015 .025]; set(0,'DefaultAxesTickLength',tick) title(['t = ', num2str(vs*t/Rs), 'x\itR_s/v_s']) axis equal; axis([0, rmax, -2, 2]) ylabel('Partial pressures/\itp\rm_0') if (i_axis == 1) legend('p_{in1}', 'p_{inr}', 'p_{out}') end if (i_axis == 7) xlabel('\itr/R_s') end i_axis = i_axis + 1; subplot(4, 2, i_axis, 'align') hold off; plot(r/Rs, p/p0, 'k-', ... cos(theta), sin(theta), 'k-') tick = [.015 .025]; set(0,'DefaultAxesTickLength',tick) title(['t = ', num2str(vs*t/Rs), 'x\itR_s/v_s']) axis equal; axis([0, rmax, -2, 2]) ylabel('Total pressure/\itp\rm_0') if (i_axis == 8) xlabel('\itr/R_s') end i_axis = i_axis + 1; end

__MACOSX/FTP-Uploads/ch12PAT/._Ch12Ex09.m

FTP-Uploads/ch12PAT/Example07_PA_Slab.avi

__MACOSX/FTP-Uploads/ch12PAT/._Example07_PA_Slab.avi

__MACOSX/FTP-Uploads/._ch12PAT

__MACOSX/._FTP-Uploads