feat: Add detailed simulation to remaining simple organs

This commit applies a detailed physiological model to all remaining simple organ classes, bringing them to a level of complexity consistent with the Heart, Lungs, and Brain.

Updates include:
- Esophagus: Simulates peristalsis and bolus movement.
- Stomach: Implements a gastric state machine for digestion.
- Intestines: Adds segments (duodenum, jejunum, etc.) and simulates absorption.
- Pancreas: Differentiates endocrine and exocrine functions.
- Gallbladder: Simulates storing, concentrating, and releasing bile.
- Kidneys: Models nephrons, GFR, and electrolyte balance.
- Bladder: Implements a micturition cycle with pressure dynamics.
- Spleen: Models red and white pulp for blood filtering and immunity.
This commit is contained in:
google-labs-jules[bot]
2025-08-20 07:11:44 +00:00
parent 278ef9fe8e
commit 7459435e25
20 changed files with 1022 additions and 183 deletions
+69 -12
View File
@@ -2,28 +2,85 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
Bladder::Bladder(int id) : Organ(id, "Bladder"), currentVolume(50.0), capacity(500.0) {}
Bladder::Bladder(int id)
: Organ(id, "Bladder"),
currentState(MicturitionState::FILLING),
currentVolume_mL(50.0),
pressure_cmH2O(5.0),
internalSphincterClosed(true) {}
void Bladder::update(double deltaTime_s) {
// In a more complex model, this would be driven by kidney output.
// For now, simulate a constant fill rate.
const double fillRate_ml_per_s = 0.02;
currentVolume += fillRate_ml_per_s * deltaTime_s;
currentVolume = std::clamp(currentVolume, 0.0, capacity);
// In a connected model, this would be called by the Kidneys.
// For now, simulate a constant fill rate from the kidneys.
const double urineInflow_ml_per_s = 0.02;
if (currentState != MicturitionState::VOIDING) {
addUrine(urineInflow_ml_per_s * deltaTime_s);
}
// Simple pressure model: pressure increases with volume
pressure_cmH2O = (currentVolume_mL / capacity_mL) * 60.0;
// State machine
switch (currentState) {
case MicturitionState::FILLING:
if (currentVolume_mL > capacity_mL * 0.8 || pressure_cmH2O > pressureThreshold_cmH2O) {
currentState = MicturitionState::FULL;
}
break;
case MicturitionState::FULL:
// For demo, automatically start voiding after 10 seconds in FULL state
static double timeInFullState = 0.0;
timeInFullState += deltaTime_s;
if (timeInFullState > 10.0) {
currentState = MicturitionState::VOIDING;
internalSphincterClosed = false;
timeInFullState = 0.0;
}
break;
case MicturitionState::VOIDING:
double voidingRate_ml_per_s = 15.0;
currentVolume_mL -= voidingRate_ml_per_s * deltaTime_s;
if (currentVolume_mL <= 0) {
currentVolume_mL = 0;
currentState = MicturitionState::FILLING;
internalSphincterClosed = true;
}
break;
}
}
void Bladder::addUrine(double amount_ml) {
currentVolume += amount_ml;
currentVolume = std::clamp(currentVolume, 0.0, capacity);
if (currentState != MicturitionState::VOIDING) {
currentVolume_mL += amount_ml;
currentVolume_mL = std::clamp(currentVolume_mL, 0.0, capacity_mL);
}
}
std::string Bladder::stateToString(MicturitionState state) const {
switch (state) {
case MicturitionState::FILLING: return "Filling";
case MicturitionState::FULL: return "Full";
case MicturitionState::VOIDING: return "Voiding";
default: return "Unknown";
}
}
std::string Bladder::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " Volume: " << currentVolume << " / " << capacity << " ml";
ss.precision(1);
ss << std::fixed;
ss << "--- Bladder Summary ---\n"
<< "State: " << stateToString(currentState) << "\n"
<< "Volume: " << getVolume() << " / " << capacity_mL << " mL\n"
<< "Pressure: " << getPressure() << " cmH2O\n";
return ss.str();
}
double Bladder::getCurrentVolume() const { return currentVolume; }
double Bladder::getCapacity() const { return capacity; }
// --- Getters Implementation ---
double Bladder::getVolume() const { return currentVolume_mL; }
double Bladder::getPressure() const { return pressure_cmH2O; }
MicturitionState Bladder::getCurrentState() const { return currentState; }
+58 -9
View File
@@ -2,25 +2,74 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
Esophagus::Esophagus(int id) : Organ(id, "Esophagus"), motility(1.0) {}
Esophagus::Esophagus(int id)
: Organ(id, "Esophagus"),
currentState(PeristalsisState::IDLE),
lowerEsophagealSphincterTone(20.0) {}
void Esophagus::update(double deltaTime_s) {
// Placeholder, no real logic yet.
// Fluctuate motility slightly.
// Simulate a swallow every 15 seconds for demonstration
static double timeSinceLastSwallow = 0.0;
timeSinceLastSwallow += deltaTime_s;
if (timeSinceLastSwallow > 15.0) {
initiateSwallow(15.0); // Swallow a 15mL bolus
timeSinceLastSwallow = 0.0;
}
if (!activeBoli.empty()) {
currentState = PeristalsisState::CONTRACTING;
const double peristalsisSpeed_cm_per_s = 3.0;
for (auto& bolus : activeBoli) {
bolus.position_cm += peristalsisSpeed_cm_per_s * deltaTime_s;
}
// Remove boli that have passed into the stomach
activeBoli.erase(std::remove_if(activeBoli.begin(), activeBoli.end(),
[this](const Bolus& b) { return b.position_cm >= this->length_cm; }),
activeBoli.end());
if (activeBoli.empty()) {
currentState = PeristalsisState::IDLE;
}
} else {
currentState = PeristalsisState::IDLE;
}
// Fluctuate LES tone slightly
static std::random_device rd;
static std::mt19937 gen(rd());
std::normal_distribution<> d(0, 0.001);
std::normal_distribution<> d(0, 0.1);
lowerEsophagealSphincterTone += d(gen) * deltaTime_s;
lowerEsophagealSphincterTone = std::clamp(lowerEsophagealSphincterTone, 18.0, 25.0);
}
motility += d(gen) * deltaTime_s;
motility = std::clamp(motility, 0.95, 1.05);
void Esophagus::initiateSwallow(double bolusVolume_mL) {
activeBoli.push_back({bolusVolume_mL, 0.0});
}
std::string Esophagus::stateToString(PeristalsisState state) const {
switch (state) {
case PeristalsisState::IDLE: return "Idle";
case PeristalsisState::CONTRACTING: return "Contracting";
case PeristalsisState::RELAXING: return "Relaxing";
default: return "Unknown";
}
}
std::string Esophagus::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " Motility: " << motility * 100 << "%";
ss << "--- Esophagus Summary ---\n"
<< "State: " << stateToString(currentState) << "\n"
<< "LES Tone: " << std::fixed << std::setprecision(1) << lowerEsophagealSphincterTone << " mmHg\n"
<< "Boluses in transit: " << activeBoli.size() << "\n";
if (!activeBoli.empty()) {
ss << " - Top bolus position: " << activeBoli.front().position_cm << " / " << length_cm << " cm\n";
}
return ss.str();
}
double Esophagus::getMotility() const { return motility; }
PeristalsisState Esophagus::getCurrentState() const { return currentState; }
bool Esophagus::isSwallowing() const { return !activeBoli.empty(); }
+63 -11
View File
@@ -2,25 +2,77 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
Gallbladder::Gallbladder(int id) : Organ(id, "Gallbladder"), bileStored(30.0) {}
Gallbladder::Gallbladder(int id)
: Organ(id, "Gallbladder"),
currentState(GallbladderState::STORING),
storedBile_mL(30.0),
bileConcentrationFactor(5.0) {}
void Gallbladder::update(double deltaTime_s) {
// A real model would be affected by liver production and digestion.
// For now, it just sits there with a small fluctuation.
static std::random_device rd;
static std::mt19937 gen(rd());
std::normal_distribution<> d(0, 0.01);
// A real model would be driven by liver output and CCK hormone for contraction.
// For now, simulate slow storage and concentration, with a periodic contraction.
bileStored += d(gen) * deltaTime_s;
bileStored = std::clamp(bileStored, 10.0, 50.0);
// Simulate bile coming from the liver
storeBile(0.005 * deltaTime_s); // Slow constant trickle
switch (currentState) {
case GallbladderState::STORING:
// Concentrate bile over time
bileConcentrationFactor += 0.05 * deltaTime_s;
bileConcentrationFactor = std::min(10.0, bileConcentrationFactor);
// For demo, contract every 40 seconds
static double timeSinceContraction = 0.0;
timeSinceContraction += deltaTime_s;
if (timeSinceContraction > 40.0) {
currentState = GallbladderState::CONTRACTING;
timeSinceContraction = 0.0;
}
break;
case GallbladderState::CONTRACTING:
// Release bile
double releasedBile = 2.0 * deltaTime_s;
storedBile_mL -= releasedBile;
if (storedBile_mL < 5.0) { // Stop contracting when near empty
storedBile_mL = std::max(0.0, storedBile_mL);
bileConcentrationFactor = 1.0; // Bile is fresh
currentState = GallbladderState::STORING;
}
break;
}
}
void Gallbladder::storeBile(double volume_mL) {
if (currentState == GallbladderState::STORING) {
storedBile_mL += volume_mL;
storedBile_mL = std::clamp(storedBile_mL, 0.0, capacity_mL);
}
}
std::string Gallbladder::stateToString(GallbladderState state) const {
switch (state) {
case GallbladderState::STORING: return "Storing/Concentrating";
case GallbladderState::CONTRACTING: return "Contracting (Releasing)";
default: return "Unknown";
}
}
std::string Gallbladder::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " Bile Stored: " << bileStored << " ml";
ss.precision(1);
ss << std::fixed;
ss << "--- Gallbladder Summary ---\n"
<< "State: " << stateToString(currentState) << "\n"
<< "Volume: " << getStoredBileVolume() << " / " << capacity_mL << " mL\n"
<< "Concentration: " << getBileConcentration() << "x\n";
return ss.str();
}
double Gallbladder::getBileStored() const { return bileStored; }
// --- Getters Implementation ---
double Gallbladder::getStoredBileVolume() const { return storedBile_mL; }
double Gallbladder::getBileConcentration() const { return bileConcentrationFactor; }
GallbladderState Gallbladder::getCurrentState() const { return currentState; }
+46 -15
View File
@@ -2,6 +2,7 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
// Helper function for random fluctuations
static double getFluctuation(double stddev) {
@@ -11,29 +12,59 @@ static double getFluctuation(double stddev) {
return d(gen);
}
Intestines::Intestines(int id) : Organ(id, "Intestines"), nutrientAbsorptionRate(1.0), waterAbsorptionRate(0.1) {}
Intestines::Intestines(int id)
: Organ(id, "Intestines"),
chymeVolume_mL(0.0) {
// Initialize segments with typical values
duodenum = {"Duodenum", 0.25, 1.0, 0.5, 0.1};
jejunum = {"Jejunum", 2.5, 1.0, 1.0, 0.3};
ileum = {"Ileum", 3.0, 1.0, 0.8, 0.5};
colon = {"Colon", 1.5, 0.5, 0.1, 1.0}; // High water absorption
}
void Intestines::update(double deltaTime_s) {
const double baseline_nutrient_abs = 1.0;
const double baseline_water_abs = 0.1;
const double theta = 0.05;
const double nutrient_abs_stddev = 0.02;
const double water_abs_stddev = 0.005;
// For demonstration, simulate chyme arriving from the stomach
static double timeSinceChyme = 0.0;
timeSinceChyme += deltaTime_s;
if (timeSinceChyme > 25.0) {
receiveChyme(10.0); // Receive 10mL of chyme
timeSinceChyme = 0.0;
}
nutrientAbsorptionRate += theta * (baseline_nutrient_abs - nutrientAbsorptionRate) * deltaTime_s + getFluctuation(nutrient_abs_stddev * deltaTime_s);
waterAbsorptionRate += theta * (baseline_water_abs - waterAbsorptionRate) * deltaTime_s + getFluctuation(water_abs_stddev * deltaTime_s);
if (chymeVolume_mL > 0) {
// Simplified absorption model: total absorption is an average of all segments
double totalNutrientAbsorption = (duodenum.nutrientAbsorptionRate + jejunum.nutrientAbsorptionRate + ileum.nutrientAbsorptionRate + colon.nutrientAbsorptionRate) / 4.0;
double totalWaterAbsorption = (duodenum.waterAbsorptionRate + jejunum.waterAbsorptionRate + ileum.waterAbsorptionRate + colon.waterAbsorptionRate) / 4.0;
nutrientAbsorptionRate = std::clamp(nutrientAbsorptionRate, 0.8, 1.2);
waterAbsorptionRate = std::clamp(waterAbsorptionRate, 0.08, 0.12);
// Reduce chyme volume based on absorption
double absorbedVolume = (totalNutrientAbsorption * 0.01 + totalWaterAbsorption * 0.1) * deltaTime_s;
chymeVolume_mL -= absorbedVolume;
chymeVolume_mL = std::max(0.0, chymeVolume_mL);
}
// Fluctuate motility slightly
duodenum.motility += getFluctuation(0.01);
duodenum.motility = std::clamp(duodenum.motility, 0.9, 1.1);
}
void Intestines::receiveChyme(double volume_mL) {
chymeVolume_mL += volume_mL;
}
std::string Intestines::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " Nutrient Absorption: " << nutrientAbsorptionRate << "\n"
<< " Water Absorption: " << waterAbsorptionRate << " ml/s";
ss.precision(2);
ss << std::fixed;
ss << "--- Intestines Summary ---\n"
<< "Total Chyme Volume: " << getTotalChymeVolume() << " mL\n\n"
<< "--- Segments ---\n"
<< duodenum.name << ": Motility " << duodenum.motility << "\n"
<< jejunum.name << ": Nutrient Abs. " << jejunum.nutrientAbsorptionRate << "\n"
<< ileum.name << ": Water Abs. " << ileum.waterAbsorptionRate << "\n"
<< colon.name << ": Water Abs. " << colon.waterAbsorptionRate << "\n";
return ss.str();
}
double Intestines::getNutrientAbsorptionRate() const { return nutrientAbsorptionRate; }
double Intestines::getWaterAbsorptionRate() const { return waterAbsorptionRate; }
// --- Getters Implementation ---
double Intestines::getTotalChymeVolume() const { return chymeVolume_mL; }
+52 -15
View File
@@ -2,6 +2,8 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <numeric>
// Helper function for random fluctuations
static double getFluctuation(double stddev) {
@@ -11,29 +13,64 @@ static double getFluctuation(double stddev) {
return d(gen);
}
Kidneys::Kidneys(int id) : Organ(id, "Kidneys"), filtrationRate(125.0), urineProductionRate(0.02) {}
Kidneys::Kidneys(int id)
: Organ(id, "Kidneys"),
gfr_mL_per_min(125.0),
urineOutput_mL_per_s(0.02),
bloodSodium_mEq_per_L(140.0),
bloodPotassium_mEq_per_L(4.0),
totalFiltrationCapacity(1.0) {
// Create a simplified representation of nephrons
nephrons.resize(100); // 100 representative units
for (int i = 0; i < nephrons.size(); ++i) {
nephrons[i] = {"Nephron " + std::to_string(i), 1.0, false};
}
}
void Kidneys::update(double deltaTime_s) {
const double baseline_filtration = 125.0;
const double baseline_urine_prod = 0.02;
const double theta = 0.03;
const double filtration_stddev = 1.0;
const double urine_prod_stddev = 0.001;
// Recalculate total capacity based on nephron health
totalFiltrationCapacity = 0.0;
for (const auto& nephron : nephrons) {
if (!nephron.isDamaged) {
totalFiltrationCapacity += nephron.filtrationEfficiency;
}
}
totalFiltrationCapacity /= nephrons.size();
filtrationRate += theta * (baseline_filtration - filtrationRate) * deltaTime_s + getFluctuation(filtration_stddev * deltaTime_s);
urineProductionRate += theta * (baseline_urine_prod - urineProductionRate) * deltaTime_s + getFluctuation(urine_prod_stddev * deltaTime_s);
// GFR is dependent on overall health
const double baseline_gfr = 125.0 * totalFiltrationCapacity;
gfr_mL_per_min += 0.1 * (baseline_gfr - gfr_mL_per_min) * deltaTime_s + getFluctuation(0.5);
filtrationRate = std::clamp(filtrationRate, 100.0, 150.0);
urineProductionRate = std::clamp(urineProductionRate, 0.01, 0.03);
// Urine output is related to GFR but also hydration status (not modeled yet)
urineOutput_mL_per_s = gfr_mL_per_min / 60.0 * 0.01; // Simplified relationship
urineOutput_mL_per_s += getFluctuation(0.001);
// Simulate electrolyte balance
bloodSodium_mEq_per_L += getFluctuation(0.05);
bloodPotassium_mEq_per_L += getFluctuation(0.01);
// Clamp to healthy ranges
gfr_mL_per_min = std::clamp(gfr_mL_per_min, 90.0, 150.0);
urineOutput_mL_per_s = std::clamp(urineOutput_mL_per_s, 0.01, 0.03);
bloodSodium_mEq_per_L = std::clamp(bloodSodium_mEq_per_L, 135.0, 145.0);
bloodPotassium_mEq_per_L = std::clamp(bloodPotassium_mEq_per_L, 3.5, 5.0);
}
std::string Kidneys::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " Filtration Rate: " << filtrationRate << " ml/min\n"
<< " Urine Production: " << urineProductionRate << " ml/s";
ss.precision(1);
ss << std::fixed;
ss << "--- Kidneys Summary ---\n"
<< "Glomerular Filtration Rate (GFR): " << getGfr() << " mL/min\n"
<< "Urine Output: " << getUrineOutputRate() * 3600 << " mL/hr\n"
<< "Blood Sodium: " << getBloodSodium() << " mEq/L\n"
<< "Blood Potassium: " << getBloodPotassium() << " mEq/L\n";
return ss.str();
}
double Kidneys::getFiltrationRate() const { return filtrationRate; }
double Kidneys::getUrineProductionRate() const { return urineProductionRate; }
// --- Getters Implementation ---
double Kidneys::getGfr() const { return gfr_mL_per_min; }
double Kidneys::getUrineOutputRate() const { return urineOutput_mL_per_s; }
double Kidneys::getBloodSodium() const { return bloodSodium_mEq_per_L; }
double Kidneys::getBloodPotassium() const { return bloodPotassium_mEq_per_L; }
+57 -18
View File
@@ -2,6 +2,7 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <numeric>
// Helper function for random fluctuations
static double getFluctuation(double stddev) {
@@ -11,32 +12,70 @@ static double getFluctuation(double stddev) {
return d(gen);
}
// Healthy baseline rates converted to per-second
// Bile: 400-800 ml/day -> ~0.0069 ml/s
// Glucose: ~90 g/day -> ~0.001 g/s
Liver::Liver(int id) : Organ(id, "Liver"), bileProductionRate(0.0069), glucoseProductionRate(0.001) {}
Liver::Liver(int id)
: Organ(id, "Liver"),
bileProductionRate_ml_per_s(0.0069),
glucoseProductionRate_g_per_s(0.001),
alt_U_per_L(25.0),
ast_U_per_L(25.0),
bilirubin_mg_per_dL(0.8),
totalMetabolicCapacity(1.0) {
// Create a simplified representation of lobules
lobules.resize(100); // 100 representative units
for (int i = 0; i < lobules.size(); ++i) {
lobules[i] = {"Lobule " + std::to_string(i), 1.0, false};
}
}
void Liver::update(double deltaTime_s) {
const double baseline_bile_rate = 0.0069;
const double baseline_glucose_rate = 0.001;
const double theta = 0.02; // Slow reversion for metabolic rates
const double bile_stddev = 0.0001;
const double glucose_stddev = 0.00005;
// Recalculate total capacity based on lobule health (for future use)
totalMetabolicCapacity = 0.0;
for (const auto& lobule : lobules) {
if (!lobule.isDamaged) {
totalMetabolicCapacity += lobule.metabolicActivity;
}
}
totalMetabolicCapacity /= lobules.size();
bileProductionRate += theta * (baseline_bile_rate - bileProductionRate) * deltaTime_s + getFluctuation(bile_stddev * deltaTime_s);
glucoseProductionRate += theta * (baseline_glucose_rate - glucoseProductionRate) * deltaTime_s + getFluctuation(glucose_stddev * deltaTime_s);
// Baseline production rates are modulated by liver health
const double baseline_bile_rate = 0.0069 * totalMetabolicCapacity;
const double baseline_glucose_rate = 0.001 * totalMetabolicCapacity;
bileProductionRate = std::clamp(bileProductionRate, 0.005, 0.009);
glucoseProductionRate = std::clamp(glucoseProductionRate, 0.0008, 0.0012);
// Update production rates with minor fluctuations
bileProductionRate_ml_per_s += 0.02 * (baseline_bile_rate - bileProductionRate_ml_per_s) * deltaTime_s + getFluctuation(0.0001);
glucoseProductionRate_g_per_s += 0.02 * (baseline_glucose_rate - glucoseProductionRate_g_per_s) * deltaTime_s + getFluctuation(0.00005);
// Update enzyme and bilirubin levels
// In a healthy state, they hover around a normal baseline
alt_U_per_L += getFluctuation(0.1);
ast_U_per_L += getFluctuation(0.1);
bilirubin_mg_per_dL += getFluctuation(0.01);
// Clamp to normal healthy ranges
bileProductionRate_ml_per_s = std::clamp(bileProductionRate_ml_per_s, 0.005, 0.009);
glucoseProductionRate_g_per_s = std::clamp(glucoseProductionRate_g_per_s, 0.0008, 0.0012);
alt_U_per_L = std::clamp(alt_U_per_L, 10.0, 40.0);
ast_U_per_L = std::clamp(ast_U_per_L, 10.0, 40.0);
bilirubin_mg_per_dL = std::clamp(bilirubin_mg_per_dL, 0.3, 1.2);
}
std::string Liver::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " Bile Production Rate: " << bileProductionRate * 60 << " ml/min\n"
<< " Glucose Production Rate: " << glucoseProductionRate * 60 << " g/min";
ss.precision(3);
ss << std::fixed;
ss << "--- Liver Summary ---\n"
<< "Bile Production: " << getBileProductionRate() * 60.0 << " mL/min\n"
<< "Glucose Production: " << getGlucoseProductionRate() * 60.0 << " g/min\n"
<< "ALT Level: " << getAltLevel() << " U/L\n"
<< "AST Level: " << getAstLevel() << " U/L\n"
<< "Bilirubin: " << getBilirubinLevel() << " mg/dL\n";
return ss.str();
}
double Liver::getBileProductionRate() const { return bileProductionRate; }
double Liver::getGlucoseProductionRate() const { return glucoseProductionRate; }
// --- Getters Implementation ---
double Liver::getBileProductionRate() const { return bileProductionRate_ml_per_s; }
double Liver::getGlucoseProductionRate() const { return glucoseProductionRate_g_per_s; }
double Liver::getAltLevel() const { return alt_U_per_L; }
double Liver::getAstLevel() const { return ast_U_per_L; }
double Liver::getBilirubinLevel() const { return bilirubin_mg_per_dL; }
+36 -15
View File
@@ -2,6 +2,7 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
// Helper function for random fluctuations
static double getFluctuation(double stddev) {
@@ -11,29 +12,49 @@ static double getFluctuation(double stddev) {
return d(gen);
}
Pancreas::Pancreas(int id) : Organ(id, "Pancreas"), insulinProduction(1.0), glucagonProduction(50.0) {}
Pancreas::Pancreas(int id)
: Organ(id, "Pancreas"),
insulinSecretion_units_per_hr(1.0),
glucagonSecretion_ng_per_hr(50.0),
amylaseSecretion_U_per_L(80.0),
lipaseSecretion_U_per_L(40.0) {}
void Pancreas::update(double deltaTime_s) {
const double baseline_insulin = 1.0;
const double baseline_glucagon = 50.0;
const double theta = 0.05;
const double insulin_stddev = 0.01;
const double glucagon_stddev = 1.0;
// In a real model, hormone secretion would be driven by blood glucose.
// Enzyme secretion would be driven by food in the duodenum.
// For now, we just simulate minor fluctuations around a baseline.
insulinProduction += theta * (baseline_insulin - insulinProduction) * deltaTime_s + getFluctuation(insulin_stddev * deltaTime_s);
glucagonProduction += theta * (baseline_glucagon - glucagonProduction) * deltaTime_s + getFluctuation(glucagon_stddev * deltaTime_s);
// Endocrine fluctuations
insulinSecretion_units_per_hr += getFluctuation(0.05);
glucagonSecretion_ng_per_hr += getFluctuation(0.5);
insulinProduction = std::clamp(insulinProduction, 0.5, 2.0);
glucagonProduction = std::clamp(glucagonProduction, 40.0, 60.0);
// Exocrine fluctuations
amylaseSecretion_U_per_L += getFluctuation(0.2);
lipaseSecretion_U_per_L += getFluctuation(0.2);
// Clamp to healthy ranges
insulinSecretion_units_per_hr = std::clamp(insulinSecretion_units_per_hr, 0.5, 2.0);
glucagonSecretion_ng_per_hr = std::clamp(glucagonSecretion_ng_per_hr, 40.0, 60.0);
amylaseSecretion_U_per_L = std::clamp(amylaseSecretion_U_per_L, 60.0, 100.0);
lipaseSecretion_U_per_L = std::clamp(lipaseSecretion_U_per_L, 20.0, 60.0);
}
std::string Pancreas::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " Insulin Production: " << insulinProduction << " units/hr\n"
<< " Glucagon Production: " << glucagonProduction << " ng/hr";
ss.precision(1);
ss << std::fixed;
ss << "--- Pancreas Summary ---\n"
<< "--- Endocrine Function ---\n"
<< "Insulin Secretion: " << getInsulinSecretion() << " units/hr\n"
<< "Glucagon Secretion: " << getGlucagonSecretion() << " ng/hr\n"
<< "--- Exocrine Function ---\n"
<< "Amylase Secretion: " << getAmylaseSecretion() << " U/L\n"
<< "Lipase Secretion: " << getLipaseSecretion() << " U/L\n";
return ss.str();
}
double Pancreas::getInsulinProduction() const { return insulinProduction; }
double Pancreas::getGlucagonProduction() const { return glucagonProduction; }
// --- Getters Implementation ---
double Pancreas::getInsulinSecretion() const { return insulinSecretion_units_per_hr; }
double Pancreas::getGlucagonSecretion() const { return glucagonSecretion_ng_per_hr; }
double Pancreas::getAmylaseSecretion() const { return amylaseSecretion_U_per_L; }
double Pancreas::getLipaseSecretion() const { return lipaseSecretion_U_per_L; }
+39 -9
View File
@@ -2,6 +2,7 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
// Helper function for random fluctuations
static double getFluctuation(double stddev) {
@@ -11,23 +12,52 @@ static double getFluctuation(double stddev) {
return d(gen);
}
SpinalCord::SpinalCord(int id) : Organ(id, "SpinalCord"), signalConductionVelocity(70.0) {}
SpinalCord::SpinalCord(int id)
: Organ(id, "SpinalCord"),
reflexArcIntact(true) {
// Initialize major pathways
descendingMotorTract = {"Descending Motor Tract", SignalStatus::NORMAL, 75.0};
ascendingSensoryTract = {"Ascending Sensory Tract", SignalStatus::NORMAL, 65.0};
}
void SpinalCord::update(double deltaTime_s) {
const double baseline_velocity = 70.0;
const double theta = 0.01;
const double velocity_stddev = 0.1;
// In a healthy state, status doesn't change.
// Pathology models would alter these values.
// For now, we just simulate minor fluctuations in a healthy velocity.
descendingMotorTract.conductionVelocity_m_per_s += getFluctuation(0.1);
descendingMotorTract.conductionVelocity_m_per_s = std::clamp(descendingMotorTract.conductionVelocity_m_per_s, 70.0, 80.0);
signalConductionVelocity += theta * (baseline_velocity - signalConductionVelocity) * deltaTime_s + getFluctuation(velocity_stddev * deltaTime_s);
ascendingSensoryTract.conductionVelocity_m_per_s += getFluctuation(0.1);
ascendingSensoryTract.conductionVelocity_m_per_s = std::clamp(ascendingSensoryTract.conductionVelocity_m_per_s, 60.0, 70.0);
signalConductionVelocity = std::clamp(signalConductionVelocity, 60.0, 80.0);
// Reflex arc is a simple boolean for now.
reflexArcIntact = (descendingMotorTract.status == SignalStatus::NORMAL && ascendingSensoryTract.status == SignalStatus::NORMAL);
}
std::string SpinalCord::statusToString(SignalStatus status) const {
switch (status) {
case SignalStatus::NORMAL: return "Normal";
case SignalStatus::IMPAIRED: return "Impaired";
case SignalStatus::SEVERED: return "Severed";
default: return "Unknown";
}
}
std::string SpinalCord::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " Signal Velocity: " << signalConductionVelocity << " m/s";
ss << "--- Spinal Cord Summary ---\n"
<< "Motor Pathway (" << descendingMotorTract.name << "): "
<< statusToString(descendingMotorTract.status) << " ("
<< std::fixed << std::setprecision(1) << descendingMotorTract.conductionVelocity_m_per_s << " m/s)\n"
<< "Sensory Pathway (" << ascendingSensoryTract.name << "): "
<< statusToString(ascendingSensoryTract.status) << " ("
<< std::fixed << std::setprecision(1) << ascendingSensoryTract.conductionVelocity_m_per_s << " m/s)\n"
<< "Reflex Arc Intact: " << (isReflexArcIntact() ? "Yes" : "No") << "\n";
return ss.str();
}
double SpinalCord::getSignalConductionVelocity() const { return signalConductionVelocity; }
// --- Getters Implementation ---
SignalStatus SpinalCord::getMotorPathwayStatus() const { return descendingMotorTract.status; }
SignalStatus SpinalCord::getSensoryPathwayStatus() const { return ascendingSensoryTract.status; }
bool SpinalCord::isReflexArcIntact() const { return reflexArcIntact; }
+32 -15
View File
@@ -2,6 +2,7 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
// Helper function for random fluctuations
static double getFluctuation(double stddev) {
@@ -11,29 +12,45 @@ static double getFluctuation(double stddev) {
return d(gen);
}
Spleen::Spleen(int id) : Organ(id, "Spleen"), redBloodCellCount(4.5), whiteBloodCellCount(7.5) {}
Spleen::Spleen(int id) : Organ(id, "Spleen") {
// Initialize pulp components
redPulp = {1.0, 0.5};
whitePulp = {1500.0, 500.0};
}
void Spleen::update(double deltaTime_s) {
const double baseline_rbc = 4.5;
const double baseline_wbc = 7.5;
const double theta = 0.02;
const double rbc_stddev = 0.01;
const double wbc_stddev = 0.05;
// In a real model, these values would change in response to infection or disease.
// For now, we just simulate minor fluctuations around a healthy baseline.
redBloodCellCount += theta * (baseline_rbc - redBloodCellCount) * deltaTime_s + getFluctuation(rbc_stddev * deltaTime_s);
whiteBloodCellCount += theta * (baseline_wbc - whiteBloodCellCount) * deltaTime_s + getFluctuation(wbc_stddev * deltaTime_s);
// Red pulp fluctuations
redPulp.filtrationRate += getFluctuation(0.01);
redPulp.rbcBreakdownRate += getFluctuation(0.005);
redBloodCellCount = std::clamp(redBloodCellCount, 4.0, 5.0);
whiteBloodCellCount = std::clamp(whiteBloodCellCount, 5.0, 10.0);
// White pulp fluctuations
whitePulp.lymphocyteCount += getFluctuation(1.0);
whitePulp.macrophageCount += getFluctuation(0.5);
// Clamp to healthy ranges
redPulp.filtrationRate = std::clamp(redPulp.filtrationRate, 0.9, 1.1);
redPulp.rbcBreakdownRate = std::clamp(redPulp.rbcBreakdownRate, 0.45, 0.55);
whitePulp.lymphocyteCount = std::clamp(whitePulp.lymphocyteCount, 1400.0, 1600.0);
whitePulp.macrophageCount = std::clamp(whitePulp.macrophageCount, 450.0, 550.0);
}
std::string Spleen::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " RBC Count: " << redBloodCellCount << " million/uL\n"
<< " WBC Count: " << whiteBloodCellCount << " thousand/uL";
ss.precision(1);
ss << std::fixed;
ss << "--- Spleen Summary ---\n"
<< "--- Red Pulp ---\n"
<< "Filtration Rate: " << redPulp.filtrationRate << "\n"
<< "RBC Breakdown Rate: " << getRbcBreakdownRate() << "\n"
<< "--- White Pulp ---\n"
<< "Lymphocyte Count: " << getLymphocyteCount() << " million\n"
<< "Macrophage Count: " << whitePulp.macrophageCount << " million\n";
return ss.str();
}
double Spleen::getRedBloodCellCount() const { return redBloodCellCount; }
double Spleen::getWhiteBloodCellCount() const { return whiteBloodCellCount; }
// --- Getters Implementation ---
double Spleen::getRbcBreakdownRate() const { return redPulp.rbcBreakdownRate; }
double Spleen::getLymphocyteCount() const { return whitePulp.lymphocyteCount; }
+86 -16
View File
@@ -2,6 +2,7 @@
#include <random>
#include <algorithm>
#include <sstream>
#include <iomanip>
// Helper function for random fluctuations
static double getFluctuation(double stddev) {
@@ -11,30 +12,99 @@ static double getFluctuation(double stddev) {
return d(gen);
}
Stomach::Stomach(int id) : Organ(id, "Stomach"), phLevel(2.0), digestionRate(1.0) {}
Stomach::Stomach(int id)
: Organ(id, "Stomach"),
currentState(GastricState::EMPTY),
currentVolume_mL(0.0),
currentPh(4.5), // pH of an empty stomach is higher
gastricJuiceSecretionRate_ml_per_s(0.1),
emptyingRate_ml_per_s(0.5) {}
void Stomach::update(double deltaTime_s) {
// For now, these values just fluctuate around a baseline.
const double baseline_ph = 2.0;
const double baseline_digestion = 1.0;
const double theta = 0.1;
const double ph_stddev = 0.05;
const double digestion_stddev = 0.02;
// --- State Machine Logic ---
switch (currentState) {
case GastricState::EMPTY:
// For demo, simulate food arriving every 20 seconds
static double timeUntilFood = 0.0;
timeUntilFood += deltaTime_s;
if (timeUntilFood > 20.0) {
addSubstance(150.0); // Simulate arrival of a meal
timeUntilFood = 0.0;
}
break;
phLevel += theta * (baseline_ph - phLevel) * deltaTime_s + getFluctuation(ph_stddev * deltaTime_s);
digestionRate += theta * (baseline_digestion - digestionRate) * deltaTime_s + getFluctuation(digestion_stddev * deltaTime_s);
case GastricState::FILLING:
// Transition to DIGESTING after a short period
static double fillTime = 0.0;
fillTime += deltaTime_s;
if (fillTime > 2.0) {
currentState = GastricState::DIGESTING;
fillTime = 0.0;
}
break;
phLevel = std::clamp(phLevel, 1.5, 3.5);
digestionRate = std::clamp(digestionRate, 0.5, 1.5);
case GastricState::DIGESTING:
// Secrete acid, lowering pH
currentPh -= 0.5 * deltaTime_s;
currentPh = std::max(1.5, currentPh);
// After some time, start emptying
static double digestionTime = 0.0;
digestionTime += deltaTime_s;
if (digestionTime > 30.0) { // Digest for 30 seconds
currentState = GastricState::EMPTYING;
digestionTime = 0.0;
}
break;
case GastricState::EMPTYING:
// Empty chyme into intestines
double amountToEmpty = emptyingRate_ml_per_s * deltaTime_s;
currentVolume_mL -= amountToEmpty;
if (currentVolume_mL <= 0) {
currentVolume_mL = 0;
currentState = GastricState::EMPTY;
currentPh = 4.5; // pH returns to baseline
}
break;
}
// Secrete a baseline level of gastric juice, increasing during digestion
double currentSecretionRate = (currentState == GastricState::DIGESTING) ? 2.0 : 0.1;
currentVolume_mL += currentSecretionRate * deltaTime_s;
currentVolume_mL = std::clamp(currentVolume_mL, 0.0, capacity_mL);
}
void Stomach::addSubstance(double volume_mL) {
currentVolume_mL += volume_mL;
// Food buffers the acid initially
currentPh = std::min(4.0, currentPh + 0.5);
currentState = GastricState::FILLING;
}
std::string Stomach::stateToString(GastricState state) const {
switch (state) {
case GastricState::EMPTY: return "Empty";
case GastricState::FILLING: return "Filling";
case GastricState::DIGESTING: return "Digesting";
case GastricState::EMPTYING: return "Emptying";
default: return "Unknown";
}
}
std::string Stomach::getSummary() const {
std::stringstream ss;
ss << "Type: " << organType << " (ID: " << organId << ")\n"
<< " pH Level: " << phLevel << "\n"
<< " Digestion Rate: " << digestionRate;
ss.precision(1);
ss << std::fixed;
ss << "--- Stomach Summary ---\n"
<< "State: " << stateToString(currentState) << "\n"
<< "Volume: " << currentVolume_mL << " / " << capacity_mL << " mL\n"
<< "Acidity (pH): " << getAcidity() << "\n";
return ss.str();
}
double Stomach::getPhLevel() const { return phLevel; }
double Stomach::getDigestionRate() const { return digestionRate; }
// --- Getters Implementation ---
GastricState Stomach::getCurrentState() const { return currentState; }
double Stomach::getVolume() const { return currentVolume_mL; }
double Stomach::getAcidity() const { return currentPh; }