oblique_mc

Monte Carlo in Single Layer Biological Tissue
git clone anongit@rnpnr.xyz:oblique_mc.git
Log | Files | Refs | Feed | README | LICENSE

Commit: d4de4a4808aa835e8bcb04a4e989b0070bc4417f
Parent: e682745deac999f08021a0aa1cf02a8ec71ac438
Author: Randy Palamar
Date:   Tue,  2 Apr 2024 22:50:38 -0600

allow incidence location to be specified

Diffstat:
Mconfig.def.h | 10++++++----
Mmc.c | 88++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
2 files changed, 64 insertions(+), 34 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -1,14 +1,15 @@ /* global data that will not be modified after startup */ static struct { - Rect extent; /* extent [cm] */ + Rect extent; /* extent [cm] */ + Vec3Pol incidence_location; /* in polar coordinates */ u32 Nx, Ny; u32 N_photons; - f64 mu_a, mu_s; /* cm^-1 */ + f64 mu_a, mu_s; /* cm^-1 */ f64 g, d; f64 n, n0; - f64 theta_i; /* degrees */ + f64 theta_i; /* radians */ f64 mu_t; f64 xoff, yoff; @@ -20,6 +21,7 @@ static struct { .left = -1.5, .right = 1.5 }, + .incidence_location = (Vec3Pol){ 1.0, DEG2RAD(30) }, .Nx = 61, .Ny = 61, .N_photons = 10e6, @@ -27,5 +29,5 @@ static struct { .g = 0.9, .d = 1e6, .n = 1.0, .n0 = 1.0, - .theta_i = 45, + .theta_i = DEG2RAD(45), }; diff --git a/mc.c b/mc.c @@ -1,12 +1,14 @@ /* Oblique Monte Carlo - * Written by Randy Palamar - March 2024 + * Written by Randy Palamar - March-April 2024 * Based on: Hybrid-MC by Li Li - * Refrence: Wang, "Biomedical optics", chpater 3 & 6 + * Reference: Wang, "Biomedical optics", chapter 3 & 6 */ /* Details: - * - Uses Cartesian coordinates. - * - Beam is incident in x-z plane. + * - Mostly uses Cartesian coordinates. Polar coordinates are used for + * specifying incident location. + * - Beam faces in positive z direction incident from anywhere in r-theta + * plane. Initial launch direction is always towards origin. */ #include <math.h> @@ -18,9 +20,10 @@ #include <string.h> #include <time.h> -#define SGN(x) ((x) >= 0 ? 1 : -1) -#define ABS(x) ((x) >= 0 ? x : -x) -#define LEN(a) (sizeof(a) / sizeof(*a)) +#define SGN(x) ((x) >= 0 ? 1 : -1) +#define ABS(x) ((x) >= 0 ? x : -x) +#define LEN(a) (sizeof(a) / sizeof(*a)) +#define DEG2RAD(a) ((a) * M_PI / 180.0) #define ZERO 1.0e-6 #define BIGVAL 1.0e6 @@ -35,18 +38,10 @@ typedef ptrdiff_t size; typedef struct { u8 *data; size len; } s8; #define s8(s) (s8){(u8 *)s, (LEN(s) - 1)} -typedef struct { - f64 x, y ,z; -} Vec3; - -typedef struct { - f64 top, bot, left, right; -} Rect; - -typedef struct { - u32 Nx, Ny; - f64 *b; -} Mat2; +typedef struct { f64 x, y ,z; } Vec3; +typedef struct { f64 r, theta, z; } Vec3Pol; +typedef struct { f64 top, bot, left, right; } Rect; +typedef struct { u32 Nx, Ny; f64 *b; } Mat2; typedef struct { Vec3 pos; @@ -74,6 +69,26 @@ die(const char *fmt, ...) exit(1); } +static Vec3 +polar_to_cartesian(Vec3Pol v) +{ + return (Vec3){ + .x = v.r * cos(v.theta), + .y = v.r * sin(v.theta), + .z = v.z + }; +} + +static Vec3Pol +cartesian_to_polar(Vec3 v) +{ + return (Vec3Pol){ + .r = sqrt(v.x * v.x + v.y * v.y), + .theta = atan2(v.y, v.x), + .z = v.z + }; +} + /* concatenates nstrs together. 0 terminates output for use with bad APIs */ static s8 s8concat(s8 *strs, size nstrs) @@ -146,7 +161,6 @@ init(void) gctx.xoff = w / 2; gctx.yoff = h / 2; gctx.mu_t = gctx.mu_a + gctx.mu_s; - gctx.theta_i = gctx.theta_i * M_PI / 180; } static void @@ -188,20 +202,30 @@ alloc_mat2(Mat2 *m, u32 x, u32 y) m->Ny = y; } +static Vec3 +launch_direction_from_polar_angle(f64 theta) +{ + f64 rdir = sin(gctx.theta_i) * gctx.n0 / gctx.n; + return (Vec3){ + .x = -rdir * cos(theta), + .y = -rdir * sin(theta), + .z = sqrt(1 - rdir * rdir) + }; +} + static void -launch_photon(Photon *p) +launch_photon(Photon *p, Vec3Pol pos) { - p->pos = (Vec3){0, 0, 0}; - p->dir.x = sin(gctx.theta_i) * gctx.n0 / gctx.n; - p->dir.y = 0; - p->dir.z = sqrt(1 - p->dir.x * p->dir.x); + p->pos = polar_to_cartesian(pos); + p->dir = launch_direction_from_polar_angle(pos.theta); p->n_scatters = 0; p->dead = 0; + Vec3Pol dir = cartesian_to_polar(p->dir); f64 sin_ti = sin(gctx.theta_i); f64 cos_ti = cos(gctx.theta_i); - f64 sin_tt = p->dir.x; - f64 cos_tt = p->dir.z; + f64 sin_tt = dir.r; + f64 cos_tt = dir.z; f64 raux1 = sin_ti * cos_tt - cos_ti * sin_tt; raux1 *= raux1; f64 raux2 = sin_ti * cos_tt + cos_ti * sin_tt; @@ -364,8 +388,6 @@ static void simulate_photon(Photon *p) { f64 step = 0, boundary_dist = 0; - - launch_photon(p); do { step = next_step(step); boundary_dist = step_towards_boundary(p); @@ -397,8 +419,14 @@ main(int argc, char *argv[]) /* Propagate Photons */ time_t tstart, tend; time(&tstart); - Photon p; + + /* cache starting photon; nothing at launch changes between runs */ + Photon p_start; + launch_photon(&p_start, gctx.incidence_location); for (u32 i = 1; i <= gctx.N_photons; i++) { + /* Photon is 64 bytes. this will use SIMD if available. + * otherwise compiler will just insert a memcpy call here */ + Photon p = p_start; simulate_photon(&p); if (i % (gctx.N_photons / 10) == 0) printf("[%u/%u] photons done!\n", i, gctx.N_photons);