-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsource_hook.h
More file actions
132 lines (116 loc) · 4.99 KB
/
source_hook.h
File metadata and controls
132 lines (116 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#ifndef PHOTON_SOURCE_HOOK_H_
#define PHOTON_SOURCE_HOOK_H_
#include "core/hook_api.h"
namespace bdm {
namespace skibidy {
// Photon transport source hook: diffusion approximation for light in tissue.
//
// Models SLM-shaped light delivery for optogenetic stimulation or phototherapy.
// The fluence rate field is driven by a surface irradiance boundary condition
// (beam profile) and attenuated by tissue absorption and scattering via the
// diffusion approximation: D = 1 / (3 * (mu_a + mu_s')).
//
// Produces:
// - Fluence field: photon density (light intensity) throughout tissue
// - Opsin activation field: channelrhodopsin open-state fraction
// Modifies:
// - ROS: phototoxicity from excessive light dose
// - Temperature: absorbed photon energy converted to heat
//
// Jacques 2013 (doi:10.1088/0031-9155/58/11/R37)
// Mardinly et al. 2018 (doi:10.1038/s41593-018-0139-8)
// Fenno et al. 2011 (doi:10.1146/annurev-neuro-061010-113817)
struct PhotonSourceHook {
DiffusionGrid* fluence_grid = nullptr;
DiffusionGrid* opsin_grid = nullptr;
DiffusionGrid* ros_grid = nullptr;
DiffusionGrid* temp_grid = nullptr;
const SimParam* sp_ = nullptr;
bool active = false;
inline void Init(const GridRegistry& reg, SignalBoard& sig) {
sp_ = reg.Params();
active = sp_->photon.enabled;
if (!active) return;
fluence_grid = reg.Get(fields::kFluenceId);
opsin_grid = reg.Get(fields::kOpsinId);
if (sp_->ros.enabled)
ros_grid = reg.Get(fields::kROSId);
if (sp_->temperature.enabled)
temp_grid = reg.Get(fields::kTemperatureId);
}
// Dermal: light attenuation and opsin activation in tissue volume.
inline void ApplyDermal(const VoxelSnapshot& snap, SignalBoard& sig) {
if (!snap.in_wound) return;
real_t dt = snap.dt;
// Beer-Lambert source term: inject photons at surface, attenuate with depth.
// Surface voxels (z near wound surface) receive irradiance scaled by beam
// profile. Deeper voxels rely on diffusion grid transport.
if (fluence_grid) {
real_t z_surface = sp_->volume_z_cornified;
real_t depth = z_surface - snap.z; // distance from surface into tissue
if (depth >= 0 && depth < 1.0) {
// Surface layer: apply beam profile as source term
real_t dx = snap.x - sp_->photon.beam_center_x;
real_t dy = snap.y - sp_->photon.beam_center_y;
real_t r2 = dx * dx + dy * dy;
real_t beam_r2 = sp_->photon.beam_radius * sp_->photon.beam_radius;
if (beam_r2 > 0 && r2 < beam_r2 * 4.0) {
// Gaussian beam profile
real_t intensity = sp_->photon.irradiance *
std::exp(-2.0 * r2 / beam_r2);
fluence_grid->ChangeConcentrationBy(snap.idx, intensity * dt);
}
}
// Absorption sink: mu_a removes photons from fluence field.
// Uses per-voxel material absorption (tissue-appropriate attenuation).
real_t fluence = fluence_grid->GetConcentration(snap.idx);
if (fluence > 1e-10) {
real_t mu_a = snap.mat ? snap.mat->absorption
: sp_->photon.absorption_coeff;
real_t absorbed = mu_a * fluence * dt;
fluence_grid->ChangeConcentrationBy(snap.idx, -absorbed);
// Thermal coupling: absorbed photons heat tissue.
// Scaled by material conductivity for tissue-appropriate heating.
if (temp_grid) {
real_t coupling = sp_->photon.thermal_coupling;
if (snap.mat) {
// Lower conductivity = more local heating (inverse scaling)
coupling *= 0.5 / std::max(snap.mat->conductivity, 0.01);
}
temp_grid->ChangeConcentrationBy(snap.idx, coupling * absorbed);
}
// Phototoxicity: high fluence generates ROS
if (ros_grid && fluence > sp_->photon.phototoxicity_threshold) {
real_t excess = fluence - sp_->photon.phototoxicity_threshold;
ros_grid->ChangeConcentrationBy(snap.idx,
sp_->photon.phototoxicity_rate * excess * dt);
}
}
}
UpdateOpsin(snap.idx, dt);
}
// Two-state opsin kinetics: dO/dt = k_on * L * (sat - O) - k_off * O.
// Clamped to [0, saturation].
inline void UpdateOpsin(size_t idx, real_t dt) {
if (!opsin_grid || !fluence_grid) return;
real_t fluence = fluence_grid->GetConcentration(idx);
real_t opsin = opsin_grid->GetConcentration(idx);
real_t k_on = sp_->photon.opsin_activation_rate;
real_t k_off = sp_->photon.opsin_deactivation_rate;
real_t sat = sp_->photon.opsin_saturation;
real_t delta = k_on * fluence * (sat - opsin) * dt
- k_off * opsin * dt;
if (std::abs(delta) > 1e-12) {
opsin_grid->ChangeConcentrationBy(idx, delta);
real_t new_val = opsin_grid->GetConcentration(idx);
if (new_val < 0) {
opsin_grid->ChangeConcentrationBy(idx, -new_val);
} else if (new_val > sat) {
opsin_grid->ChangeConcentrationBy(idx, sat - new_val);
}
}
}
};
} // namespace skibidy
} // namespace bdm
#endif // PHOTON_SOURCE_HOOK_H_