feat(patient): integrate brain/bladder/esophagus/stomach coupling
Introduce richer neuro-visceral coupling and signal integration across organs to improve physiological realism: - Add Brain, Esophagus, Bladder, Stomach, Spinal signal structs - Couple bladder with spinal autonomics and brain state to drive parasympathetic/sympathetic/somatic outputs and thresholds - Deliver esophageal bolus into stomach; update hiatal pressure and LES tone from stomach distension/acid/motility - Move stomach emptying into intestines within patient update loop - Replace spleen state penalty with immune activity-based adjustment - Simplify gallbladder control using intestinal nutrient energy - Refine heart autonomic tone using brain/spinal outputs - Re-export BladderPhase, SleepStage, EsophagealStage No breaking API changes.
This commit is contained in:
+3
-3
@@ -55,9 +55,9 @@ mod spinal_cord;
|
||||
mod spleen;
|
||||
mod stomach;
|
||||
|
||||
pub use bladder::Bladder;
|
||||
pub use brain::Brain;
|
||||
pub use esophagus::Esophagus;
|
||||
pub use bladder::{Bladder, BladderPhase};
|
||||
pub use brain::{Brain, SleepStage};
|
||||
pub use esophagus::{EsophagealStage, Esophagus};
|
||||
pub use gallbladder::Gallbladder;
|
||||
pub use heart::Heart;
|
||||
pub use intestines::Intestines;
|
||||
|
||||
+266
-72
@@ -2,10 +2,10 @@
|
||||
|
||||
use crate::error::MedicalError;
|
||||
use crate::organs::{
|
||||
Bladder, Brain, Gallbladder, Heart, IntestinalPhase, Intestines, Kidneys, Liver, Lungs, Organ,
|
||||
Pancreas, SpinalCord, Spleen, SplenicState, Stomach,
|
||||
Bladder, BladderPhase, Brain, EsophagealStage, Esophagus, Gallbladder, Heart, Intestines,
|
||||
Kidneys, Liver, Lungs, Organ, Pancreas, SleepStage, SpinalCord, Spleen, Stomach,
|
||||
};
|
||||
use crate::organs::intestines::IntestinalPhase;\r\nuse crate::organs::spleen::SplenicState;\r\n\r\nuse crate::types::{Blood, BloodPressure, OrganType};
|
||||
use crate::types::{Blood, BloodPressure, OrganType};
|
||||
|
||||
/// Patient container and simulation entry.
|
||||
#[derive(Debug)]
|
||||
@@ -20,21 +20,48 @@ pub struct Patient {
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct HeartSignals {
|
||||
systolic: f32,
|
||||
diastolic: f32,
|
||||
map: f32,
|
||||
cardiac_output: f32,
|
||||
heart_rate: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct LungSignals {
|
||||
spo2_pct: f32,
|
||||
alveolar_po2_mm_hg: f32,
|
||||
alveolar_pco2_mm_hg: f32,
|
||||
minute_ventilation_l_min: f32,
|
||||
oxygen_delivery_ml_min: f32,
|
||||
shunt_fraction: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct BrainSignals {
|
||||
brainstem_drive: f32,
|
||||
autonomic_variability: f32,
|
||||
consciousness: f32,
|
||||
sleep_depth: f32,
|
||||
rem_tone: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct EsophagusSignals {
|
||||
stage: EsophagealStage,
|
||||
bolus_volume_ml: f32,
|
||||
peristaltic_progress_cm: f32,
|
||||
lower_sphincter_tone: f32,
|
||||
hiatal_pressure_cm_h2o: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct BladderSignals {
|
||||
afferent_signal: f32,
|
||||
urgency: f32,
|
||||
phase: BladderPhase,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct StomachSignals {
|
||||
emptying_rate_ml_min: f32,
|
||||
nutrient_load_kcal: f32,
|
||||
volume_ml: f32,
|
||||
acid_level: u8,
|
||||
motility_index: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -52,7 +79,6 @@ struct PancreasSignals {
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct IntestineSignals {
|
||||
phase: IntestinalPhase,
|
||||
nutrient_energy_kcal: f32,
|
||||
}
|
||||
|
||||
@@ -65,6 +91,7 @@ struct GallbladderSignals {
|
||||
struct SpinalSignals {
|
||||
sympathetic_outflow: f32,
|
||||
parasympathetic_outflow: f32,
|
||||
reflex_gain: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -77,7 +104,6 @@ struct SpleenSignals {
|
||||
immune_activity: u8,
|
||||
red_pulp_volume_ml: f32,
|
||||
platelet_reservoir: f32,
|
||||
state: SplenicState,
|
||||
}
|
||||
|
||||
impl Patient {
|
||||
@@ -191,7 +217,7 @@ impl Patient {
|
||||
}
|
||||
OrganType::Esophagus => {
|
||||
let id = format!("{}-eso", self.id);
|
||||
self.add_organ(crate::organs::Esophagus::new(id));
|
||||
self.add_organ(Esophagus::new(id));
|
||||
}
|
||||
OrganType::Kidneys => {
|
||||
let id = format!("{}-kidneys", self.id);
|
||||
@@ -219,21 +245,22 @@ impl Patient {
|
||||
let systolic = h.arterial_bp.systolic as f32;
|
||||
let diastolic = h.arterial_bp.diastolic as f32;
|
||||
HeartSignals {
|
||||
systolic,
|
||||
diastolic,
|
||||
map: diastolic + (systolic - diastolic) / 3.0,
|
||||
cardiac_output: h.cardiac_output_l_min,
|
||||
heart_rate: h.heart_rate_bpm,
|
||||
}
|
||||
});
|
||||
|
||||
let lungs_signals = self.find_organ_typed::<Lungs>().map(|l| LungSignals {
|
||||
spo2_pct: l.spo2_pct,
|
||||
alveolar_po2_mm_hg: l.alveolar_po2_mm_hg,
|
||||
alveolar_pco2_mm_hg: l.alveolar_pco2_mm_hg,
|
||||
minute_ventilation_l_min: l.minute_ventilation_l_min,
|
||||
oxygen_delivery_ml_min: l.oxygen_delivery_ml_min,
|
||||
shunt_fraction: l.shunt_fraction,
|
||||
});
|
||||
|
||||
let stomach_signals = self.find_organ_typed::<Stomach>().map(|s| StomachSignals {
|
||||
emptying_rate_ml_min: s.emptying_rate_ml_min,
|
||||
nutrient_load_kcal: s.nutrient_load_kcal,
|
||||
volume_ml: s.volume_ml,
|
||||
acid_level: s.acid_level,
|
||||
motility_index: s.motility_index,
|
||||
});
|
||||
|
||||
let liver_signals = self.find_organ_typed::<Liver>().map(|l| LiverSignals {
|
||||
@@ -243,7 +270,6 @@ impl Patient {
|
||||
let intestine_signals = self
|
||||
.find_organ_typed::<Intestines>()
|
||||
.map(|i| IntestineSignals {
|
||||
phase: i.phase,
|
||||
nutrient_energy_kcal: i.nutrient_energy_kcal,
|
||||
});
|
||||
|
||||
@@ -253,6 +279,16 @@ impl Patient {
|
||||
bile_acid_concentration_mmol_l: g.bile_acid_concentration_mmol_l,
|
||||
});
|
||||
|
||||
let esophagus_before = self
|
||||
.find_organ_typed::<Esophagus>()
|
||||
.map(|e| EsophagusSignals {
|
||||
stage: e.stage,
|
||||
bolus_volume_ml: e.bolus_volume_ml,
|
||||
peristaltic_progress_cm: e.peristaltic_progress_cm,
|
||||
lower_sphincter_tone: e.lower_sphincter_tone,
|
||||
hiatal_pressure_cm_h2o: e.hiatal_pressure_gradient_cm_h2o,
|
||||
});
|
||||
|
||||
let kidney_signals = self.find_organ_typed::<Kidneys>().map(|k| KidneySignals {
|
||||
urine_flow_ml_min: k.urine_flow_ml_min,
|
||||
});
|
||||
@@ -262,10 +298,12 @@ impl Patient {
|
||||
.map(|s| SpinalSignals {
|
||||
sympathetic_outflow: s.sympathetic_outflow,
|
||||
parasympathetic_outflow: s.parasympathetic_outflow,
|
||||
reflex_gain: s.reflex_gain,
|
||||
});
|
||||
|
||||
let blood_glucose = self.blood.glucose_mg_dl;
|
||||
if let Some(pancreas) = self.find_organ_typed_mut::<Pancreas>() {
|
||||
pancreas.blood_glucose_mg_dl = self.blood.glucose_mg_dl;
|
||||
pancreas.blood_glucose_mg_dl = blood_glucose;
|
||||
if let Some(intestines) = intestine_signals {
|
||||
let incretin_target = (intestines.nutrient_energy_kcal / 400.0).clamp(0.05, 1.0);
|
||||
pancreas.incretin_signal =
|
||||
@@ -302,8 +340,9 @@ impl Patient {
|
||||
}
|
||||
}
|
||||
|
||||
let blood_glucose_for_kidneys = self.blood.glucose_mg_dl;
|
||||
if let Some(kidneys) = self.find_organ_typed_mut::<Kidneys>() {
|
||||
let osm_target = 285.0 + (self.blood.glucose_mg_dl - 95.0) * 0.06;
|
||||
let osm_target = 285.0 + (blood_glucose_for_kidneys - 95.0) * 0.06;
|
||||
kidneys.serum_osmolality_mosm =
|
||||
Self::relax_value(kidneys.serum_osmolality_mosm, osm_target, dt_seconds, 120.0);
|
||||
if let Some(heart) = heart_signals {
|
||||
@@ -324,10 +363,7 @@ impl Patient {
|
||||
);
|
||||
}
|
||||
if let Some(intestines) = intestine_signals {
|
||||
if matches!(
|
||||
intestines.phase,
|
||||
IntestinalPhase::IlealBrake | IntestinalPhase::Dysmotility
|
||||
) {
|
||||
if intestines.nutrient_energy_kcal < 80.0 {
|
||||
gallbladder.sphincter_of_oddi_tone = (gallbladder.sphincter_of_oddi_tone
|
||||
+ 0.05 * dt_seconds / 60.0)
|
||||
.clamp(0.2, 0.95);
|
||||
@@ -372,6 +408,16 @@ impl Patient {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(stomach) = stomach_signals {
|
||||
let delivered_ml = stomach.emptying_rate_ml_min * dt_seconds / 60.0;
|
||||
let delivered_kcal = (delivered_ml * 0.8).min(stomach.nutrient_load_kcal);
|
||||
if delivered_kcal > 0.0 {
|
||||
if let Some(intestines) = self.find_organ_typed_mut::<Intestines>() {
|
||||
intestines.nutrient_energy_kcal += delivered_kcal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for organ in &mut self.organs {
|
||||
organ.update(dt_seconds);
|
||||
}
|
||||
@@ -417,21 +463,14 @@ impl Patient {
|
||||
immune_activity: s.immune_activity,
|
||||
red_pulp_volume_ml: s.red_pulp_volume_ml,
|
||||
platelet_reservoir: s.platelet_reservoir,
|
||||
state: s.state,
|
||||
});
|
||||
|
||||
if let Some(spleen) = spleen_after {
|
||||
let state_penalty = match spleen.state {
|
||||
SplenicState::SympatheticContraction => -2.0,
|
||||
SplenicState::HyperimmuneActivation => 2.5,
|
||||
SplenicState::Sequestration => 3.0,
|
||||
SplenicState::Hypofunction => -1.5,
|
||||
SplenicState::Homeostatic => 0.0,
|
||||
};
|
||||
let immune_penalty = (spleen.immune_activity as f32 - 80.0) / 120.0;
|
||||
let hematocrit_target = 42.0
|
||||
- (spleen.red_pulp_volume_ml - 180.0) / 8.0
|
||||
- (spleen.platelet_reservoir - 70.0) / 30.0
|
||||
+ state_penalty;
|
||||
+ immune_penalty * 2.0;
|
||||
self.blood.hematocrit_pct = Self::relax_value(
|
||||
self.blood.hematocrit_pct,
|
||||
hematocrit_target.clamp(30.0, 55.0),
|
||||
@@ -440,8 +479,9 @@ impl Patient {
|
||||
);
|
||||
}
|
||||
|
||||
let blood_glucose_for_pancreas = self.blood.glucose_mg_dl;
|
||||
if let Some(pancreas) = self.find_organ_typed_mut::<Pancreas>() {
|
||||
pancreas.blood_glucose_mg_dl = self.blood.glucose_mg_dl;
|
||||
pancreas.blood_glucose_mg_dl = blood_glucose_for_pancreas;
|
||||
}
|
||||
|
||||
let pancreas_after = self
|
||||
@@ -453,6 +493,188 @@ impl Patient {
|
||||
somatostatin: p.somatostatin,
|
||||
});
|
||||
|
||||
let brain_after = self.find_organ_typed::<Brain>().map(|b| BrainSignals {
|
||||
brainstem_drive: b.brainstem_autonomic_drive,
|
||||
autonomic_variability: b.autonomic_variability,
|
||||
consciousness: b.consciousness as f32 / 100.0,
|
||||
sleep_depth: match b.sleep_stage {
|
||||
SleepStage::Wake => 0.0,
|
||||
SleepStage::N1 => 0.25,
|
||||
SleepStage::N2 => 0.55,
|
||||
SleepStage::N3 => 0.9,
|
||||
SleepStage::Rem => 0.4,
|
||||
},
|
||||
rem_tone: matches!(b.sleep_stage, SleepStage::Rem) as i32 as f32,
|
||||
});
|
||||
let spinal_after = self
|
||||
.find_organ_typed::<SpinalCord>()
|
||||
.map(|s| SpinalSignals {
|
||||
sympathetic_outflow: s.sympathetic_outflow,
|
||||
parasympathetic_outflow: s.parasympathetic_outflow,
|
||||
reflex_gain: s.reflex_gain,
|
||||
});
|
||||
let bladder_after = self.find_organ_typed::<Bladder>().map(|b| BladderSignals {
|
||||
afferent_signal: b.afferent_signal,
|
||||
urgency: b.urgency,
|
||||
phase: b.phase,
|
||||
});
|
||||
let esophagus_after = self
|
||||
.find_organ_typed::<Esophagus>()
|
||||
.map(|e| EsophagusSignals {
|
||||
stage: e.stage,
|
||||
bolus_volume_ml: e.bolus_volume_ml,
|
||||
peristaltic_progress_cm: e.peristaltic_progress_cm,
|
||||
lower_sphincter_tone: e.lower_sphincter_tone,
|
||||
hiatal_pressure_cm_h2o: e.hiatal_pressure_gradient_cm_h2o,
|
||||
});
|
||||
|
||||
if let (Some(before), Some(after)) = (esophagus_before, esophagus_after) {
|
||||
let mut delivered_ml = 0.0;
|
||||
if matches!(
|
||||
after.stage,
|
||||
EsophagealStage::Clearing | EsophagealStage::Idle
|
||||
) {
|
||||
delivered_ml = (before.bolus_volume_ml - after.bolus_volume_ml).max(0.0);
|
||||
} else if after.peristaltic_progress_cm > 22.0 && before.peristaltic_progress_cm <= 22.0
|
||||
{
|
||||
delivered_ml = before.bolus_volume_ml * 0.6;
|
||||
}
|
||||
let delivered_ml = delivered_ml.clamp(0.0, 40.0);
|
||||
if delivered_ml > 0.0 {
|
||||
if let Some(stomach) = self.find_organ_typed_mut::<Stomach>() {
|
||||
stomach.volume_ml = (stomach.volume_ml + delivered_ml).clamp(80.0, 1600.0);
|
||||
stomach.nutrient_load_kcal += delivered_ml * 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(stomach), Some(esophagus_state)) = (stomach_signals, esophagus_after) {
|
||||
if let Some(esophagus) = self.find_organ_typed_mut::<Esophagus>() {
|
||||
let distension = ((stomach.volume_ml - 250.0) / 160.0).clamp(-0.3, 2.2);
|
||||
let acid_factor = (stomach.acid_level as f32 / 100.0 - 0.65).max(0.0);
|
||||
let motility_relief = (stomach.motility_index - 0.5).min(0.0).abs();
|
||||
let gradient_target =
|
||||
(esophagus_state.hiatal_pressure_cm_h2o + distension * 6.0 + acid_factor * 3.0
|
||||
- motility_relief * 2.0)
|
||||
.clamp(4.0, 22.0);
|
||||
esophagus.hiatal_pressure_gradient_cm_h2o = Self::relax_value(
|
||||
esophagus.hiatal_pressure_gradient_cm_h2o,
|
||||
gradient_target,
|
||||
dt_seconds,
|
||||
90.0,
|
||||
);
|
||||
let les_target = (esophagus_state.lower_sphincter_tone
|
||||
+ 0.15 * (0.6 - distension).clamp(-0.4, 0.4)
|
||||
- 0.1 * acid_factor)
|
||||
.clamp(0.2, 0.95);
|
||||
esophagus.lower_sphincter_tone = Self::relax_value(
|
||||
esophagus.lower_sphincter_tone,
|
||||
les_target,
|
||||
dt_seconds,
|
||||
120.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(bladder_state) = bladder_after {
|
||||
if let Some(spinal) = self.find_organ_typed_mut::<SpinalCord>() {
|
||||
let stretch = bladder_state.afferent_signal;
|
||||
let urgency = bladder_state.urgency;
|
||||
let voiding_signal =
|
||||
matches!(bladder_state.phase, BladderPhase::Voiding) as i32 as f32;
|
||||
let sleep_bias = brain_after.map(|b| b.sleep_depth).unwrap_or(0.0);
|
||||
let para_target = (0.42 + 0.5 * stretch + 0.25 * voiding_signal
|
||||
- 0.15 * sleep_bias)
|
||||
.clamp(0.2, 0.95);
|
||||
let sym_target = (0.65 - 0.4 * stretch - 0.25 * voiding_signal + 0.2 * sleep_bias)
|
||||
.clamp(0.15, 0.9);
|
||||
spinal.parasympathetic_outflow = Self::relax_value(
|
||||
spinal.parasympathetic_outflow,
|
||||
para_target,
|
||||
dt_seconds,
|
||||
90.0,
|
||||
);
|
||||
spinal.sympathetic_outflow =
|
||||
Self::relax_value(spinal.sympathetic_outflow, sym_target, dt_seconds, 90.0);
|
||||
let sedation = brain_after.map(|b| 1.0 - b.consciousness).unwrap_or(0.0);
|
||||
let reflex_target =
|
||||
(1.0 + 0.55 * stretch + 0.3 * voiding_signal - 0.3 * sedation).clamp(0.6, 1.6);
|
||||
spinal.reflex_gain =
|
||||
Self::relax_value(spinal.reflex_gain, reflex_target, dt_seconds, 120.0);
|
||||
spinal.nociceptive_facilitation = Self::relax_value(
|
||||
spinal.nociceptive_facilitation,
|
||||
(0.2 + 0.5 * urgency + 0.1 * voiding_signal).clamp(0.15, 1.0),
|
||||
dt_seconds,
|
||||
120.0,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(bladder) = self.find_organ_typed_mut::<Bladder>() {
|
||||
if let Some(brain) = brain_after {
|
||||
let sedation = (1.0 - brain.consciousness).clamp(0.0, 1.0);
|
||||
let sleep_bonus = brain.sleep_depth * 0.25 + brain.rem_tone * 0.1;
|
||||
let capacity = bladder.capacity_ml.max(1.0);
|
||||
let micturition_target = (capacity
|
||||
* (0.65 + sleep_bonus + sedation * 0.2 - bladder_state.urgency * 0.05))
|
||||
.clamp(capacity * 0.5, capacity * 0.9);
|
||||
bladder.micturition_threshold_ml = Self::relax_value(
|
||||
bladder.micturition_threshold_ml,
|
||||
micturition_target,
|
||||
dt_seconds,
|
||||
900.0,
|
||||
);
|
||||
let urge_target = (capacity * (0.4 + sleep_bonus * 0.5 + sedation * 0.15))
|
||||
.clamp(capacity * 0.3, capacity * 0.7);
|
||||
bladder.urge_threshold_ml = Self::relax_value(
|
||||
bladder.urge_threshold_ml,
|
||||
urge_target,
|
||||
dt_seconds,
|
||||
600.0,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(brain) = brain_after {
|
||||
let voiding_signal =
|
||||
matches!(bladder_state.phase, BladderPhase::Voiding) as i32 as f32;
|
||||
let spinal_sym = spinal_after.map(|s| s.sympathetic_outflow).unwrap_or(0.6);
|
||||
let spinal_para = spinal_after
|
||||
.map(|s| s.parasympathetic_outflow)
|
||||
.unwrap_or(0.5);
|
||||
let reflex_gain = spinal_after.map(|s| s.reflex_gain).unwrap_or(1.0);
|
||||
let para_target = (0.3
|
||||
+ brain.brainstem_drive * 0.4
|
||||
+ spinal_para * 0.3
|
||||
+ bladder_state.urgency * 0.5
|
||||
+ voiding_signal * 0.3)
|
||||
.clamp(0.05, 1.1);
|
||||
bladder.parasympathetic_drive = Self::relax_value(
|
||||
bladder.parasympathetic_drive,
|
||||
para_target,
|
||||
dt_seconds,
|
||||
120.0,
|
||||
)
|
||||
.clamp(0.0, 1.0);
|
||||
let sym_target =
|
||||
(0.55 + (1.0 - brain.brainstem_drive) * 0.35 + spinal_sym * 0.4
|
||||
- bladder_state.urgency * 0.4
|
||||
- voiding_signal * 0.4)
|
||||
.clamp(0.1, 1.0);
|
||||
bladder.sympathetic_drive =
|
||||
Self::relax_value(bladder.sympathetic_drive, sym_target, dt_seconds, 160.0)
|
||||
.clamp(0.0, 1.0);
|
||||
let voluntary = brain.consciousness;
|
||||
let somatic_target = ((0.55 + voluntary * 0.35 - brain.sleep_depth * 0.3)
|
||||
* (1.0 - bladder_state.urgency * 0.55)
|
||||
+ reflex_gain * 0.1
|
||||
- voiding_signal * 0.5)
|
||||
.clamp(0.1, 0.95);
|
||||
bladder.somatic_drive =
|
||||
Self::relax_value(bladder.somatic_drive, somatic_target, dt_seconds, 110.0)
|
||||
.clamp(0.0, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pancreas) = pancreas_after {
|
||||
if let Some(liver) = self.find_organ_typed_mut::<Liver>() {
|
||||
let insulin_target = (pancreas.insulin / 60.0).clamp(0.1, 1.0);
|
||||
@@ -464,28 +686,6 @@ impl Patient {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(liver) = self.find_organ_typed::<Liver>() {
|
||||
if let Some(gallbladder) = self.find_organ_typed_mut::<Gallbladder>() {
|
||||
let inflow_target = (liver.bile_secretion_ml_min * 0.8).clamp(0.05, 2.4);
|
||||
gallbladder.hepatic_bile_flow_ml_per_min = Self::relax_value(
|
||||
gallbladder.hepatic_bile_flow_ml_per_min,
|
||||
inflow_target,
|
||||
dt_seconds,
|
||||
80.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(stomach) = self.find_organ_typed::<Stomach>() {
|
||||
let delivered_ml = stomach.emptying_rate_ml_min * dt_seconds / 60.0;
|
||||
let delivered_kcal = (delivered_ml * 0.8).min(stomach.nutrient_load_kcal);
|
||||
if delivered_kcal > 0.0 {
|
||||
if let Some(intestines) = self.find_organ_typed_mut::<Intestines>() {
|
||||
intestines.nutrient_energy_kcal += delivered_kcal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_, urine_flow)) = kidneys_after {
|
||||
let produced = (urine_flow * dt_seconds / 60.0).max(0.0);
|
||||
if produced > 0.0 {
|
||||
@@ -495,17 +695,13 @@ impl Patient {
|
||||
}
|
||||
}
|
||||
|
||||
let brain_after = self
|
||||
.find_organ_typed::<Brain>()
|
||||
.map(|b| (b.brainstem_autonomic_drive, b.autonomic_variability));
|
||||
let spinal_after = self
|
||||
.find_organ_typed::<SpinalCord>()
|
||||
.map(|s| (s.sympathetic_outflow, s.parasympathetic_outflow));
|
||||
if let Some(heart) = self.find_organ_typed_mut::<Heart>() {
|
||||
if let Some((brain_drive, brain_sympathetic)) = brain_after {
|
||||
let mut tone_target = (brain_drive - 0.5) * 1.2 + (brain_sympathetic - 0.5) * 0.8;
|
||||
if let Some((sym, para)) = spinal_after {
|
||||
tone_target += (sym - para) * 0.6;
|
||||
if let Some(brain) = brain_after {
|
||||
let mut tone_target =
|
||||
(brain.brainstem_drive - 0.5) * 1.2 + (brain.autonomic_variability - 0.5) * 0.8;
|
||||
if let Some(spinal) = spinal_after {
|
||||
tone_target +=
|
||||
(spinal.sympathetic_outflow - spinal.parasympathetic_outflow) * 0.6;
|
||||
}
|
||||
heart.autonomic_tone = Self::relax_value(
|
||||
heart.autonomic_tone,
|
||||
@@ -584,7 +780,7 @@ fn is_valid_id(id: &str) -> bool {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::organs::intestines::IntestinalPhase;\r\nuse crate::organs::spleen::SplenicState;\r\n\r\nuse crate::types::OrganType;
|
||||
use crate::types::OrganType;
|
||||
|
||||
#[test]
|
||||
fn patient_lifecycle() {
|
||||
@@ -603,5 +799,3 @@ mod tests {
|
||||
assert!(Patient::new("bad id").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user