Merge remote-tracking branch 'git/feature/physiological-loops'
This commit is contained in:
@@ -57,6 +57,7 @@ private:
|
||||
// --- Private Helper Methods ---
|
||||
void updateActivity(double deltaTime_s);
|
||||
void updatePressures(double meanArterialPressure);
|
||||
void updateAutonomicControl(Patient& patient, double deltaTime_s);
|
||||
double generateEegValue();
|
||||
|
||||
// --- Physiological Parameters ---
|
||||
@@ -75,6 +76,10 @@ private:
|
||||
BrainRegion occipitalLobe;
|
||||
BrainRegion cerebellum;
|
||||
|
||||
// --- Autonomic Control Targets ---
|
||||
double targetRespirationRate_bpm;
|
||||
double targetHeartRate_bpm;
|
||||
|
||||
// --- Waveform Data ---
|
||||
std::deque<double> eegData;
|
||||
size_t eegHistorySize;
|
||||
|
||||
@@ -41,6 +41,13 @@ public:
|
||||
*/
|
||||
void storeBile(double volume_mL);
|
||||
|
||||
/**
|
||||
* @brief Releases bile when stimulated (e.g., by chyme in duodenum).
|
||||
* @param deltaTime_s The time step for this update.
|
||||
* @return The amount of bile released in mL.
|
||||
*/
|
||||
double releaseBile(double deltaTime_s);
|
||||
|
||||
// --- Getters for Gallbladder State ---
|
||||
|
||||
/** @brief Gets the current volume of stored bile in mL. */
|
||||
@@ -60,5 +67,6 @@ private:
|
||||
GallbladderState currentState;
|
||||
double storedBile_mL;
|
||||
double bileConcentrationFactor; // How concentrated the bile is (1x, 5x, etc.)
|
||||
double bileReleaseRate_ml_per_s; // Rate of bile release when contracting
|
||||
const double capacity_mL = 50.0;
|
||||
};
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
std::string getSummary() const override;
|
||||
|
||||
// --- Electrical Properties ---
|
||||
void setHeartRate(double newRate_bpm);
|
||||
double getHeartRate() const;
|
||||
const std::map<std::string, std::deque<double>>& getEkgData() const;
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Forward-declare for receiveEnzymes method
|
||||
struct DigestiveEnzymes;
|
||||
|
||||
/**
|
||||
* @brief Represents a segment of the intestines.
|
||||
*/
|
||||
@@ -45,6 +48,18 @@ public:
|
||||
*/
|
||||
void receiveChyme(double volume_mL);
|
||||
|
||||
/**
|
||||
* @brief Adds bile from the gallbladder.
|
||||
* @param volume_mL The volume of bile.
|
||||
*/
|
||||
void receiveBile(double volume_mL);
|
||||
|
||||
/**
|
||||
* @brief Adds digestive enzymes from the pancreas.
|
||||
* @param enzymes A struct containing the enzyme information.
|
||||
*/
|
||||
void receiveEnzymes(const DigestiveEnzymes& enzymes);
|
||||
|
||||
// --- Getters for Intestinal State ---
|
||||
|
||||
/** @brief Gets the total volume of chyme currently in the intestines. */
|
||||
@@ -59,4 +74,8 @@ private:
|
||||
|
||||
// --- Simulation State ---
|
||||
double chymeVolume_mL; // Total volume in the whole system for now
|
||||
double bileVolume_mL;
|
||||
double enzymeVolume_mL;
|
||||
double amylase_U_per_L;
|
||||
double lipase_U_per_L;
|
||||
};
|
||||
|
||||
@@ -51,8 +51,12 @@ public:
|
||||
/** @brief Gets the simulated blood potassium level in mEq/L. */
|
||||
double getBloodPotassium() const;
|
||||
|
||||
/** @brief Gets the current rate of renin secretion. */
|
||||
double getReninSecretionRate() const;
|
||||
|
||||
private:
|
||||
// --- Physiological Parameters ---
|
||||
double reninSecretionRate; // In ng/mL/hr
|
||||
double gfr_mL_per_min;
|
||||
double urineOutput_mL_per_s;
|
||||
double bloodSodium_mEq_per_L;
|
||||
|
||||
@@ -54,8 +54,12 @@ public:
|
||||
/** @brief Gets the total bilirubin level in mg/dL. */
|
||||
double getBilirubinLevel() const;
|
||||
|
||||
/** @brief Gets the production rate of angiotensinogen. */
|
||||
double getAngiotensinogenRate() const;
|
||||
|
||||
private:
|
||||
// --- Physiological Parameters ---
|
||||
double angiotensinogen_production_rate; // In arbitrary units/s
|
||||
double bileProductionRate_ml_per_s;
|
||||
double glucoseProductionRate_g_per_s;
|
||||
double alt_U_per_L; ///< Alanine Aminotransferase
|
||||
|
||||
@@ -61,6 +61,14 @@ public:
|
||||
*/
|
||||
void inflictDamage(double damage);
|
||||
|
||||
// --- Setters for External Control ---
|
||||
|
||||
/**
|
||||
* @brief Sets the respiration rate.
|
||||
* @param newRate_bpm The new rate in breaths per minute.
|
||||
*/
|
||||
void setRespirationRate(double newRate_bpm);
|
||||
|
||||
// --- Getters for Key Respiratory Vitals ---
|
||||
|
||||
/** @brief Gets the current respiration rate in breaths per minute. */
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
#include "Organ.h"
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Represents the mix of enzymes released by the pancreas for digestion.
|
||||
*/
|
||||
struct DigestiveEnzymes {
|
||||
double volume_mL = 0.0;
|
||||
double amylase_U_per_L = 0.0;
|
||||
double lipase_U_per_L = 0.0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents the Pancreas, with both endocrine and exocrine functions.
|
||||
*/
|
||||
@@ -43,6 +52,15 @@ public:
|
||||
/** @brief Gets the current lipase secretion rate in U/L. */
|
||||
double getLipaseSecretion() const;
|
||||
|
||||
// --- Exocrine Functions (Enzymes) ---
|
||||
|
||||
/**
|
||||
* @brief Releases digestive enzymes when stimulated.
|
||||
* @param deltaTime_s The time step for this update.
|
||||
* @return A struct containing the released enzymes.
|
||||
*/
|
||||
DigestiveEnzymes releaseEnzymes(double deltaTime_s);
|
||||
|
||||
private:
|
||||
// --- Endocrine Parameters ---
|
||||
double insulinSecretion_units_per_hr;
|
||||
@@ -51,4 +69,5 @@ private:
|
||||
// --- Exocrine Parameters ---
|
||||
double amylaseSecretion_U_per_L;
|
||||
double lipaseSecretion_U_per_L;
|
||||
double enzymeReleaseRate_ml_per_s;
|
||||
};
|
||||
|
||||
@@ -7,13 +7,23 @@
|
||||
// Forward-declare the Organ class to avoid circular dependencies
|
||||
class Organ;
|
||||
|
||||
/**
|
||||
* @brief Represents blood pressure values.
|
||||
*/
|
||||
struct BloodPressure {
|
||||
double systolic_mmHg = 120.0;
|
||||
double diastolic_mmHg = 80.0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents the composition of the patient's blood.
|
||||
*/
|
||||
struct Blood {
|
||||
BloodPressure bloodPressure;
|
||||
double oxygenSaturation = 98.0; // Normal SpO2
|
||||
double co2PartialPressure_mmHg = 40.0; // Normal PaCO2
|
||||
double glucose_mg_per_dL = 100.0; // Normal fasting glucose
|
||||
double angiotensin_au = 0.0; // Hormone for BP control
|
||||
double toxins_au = 0.0; // Arbitrary units, 0 is clean
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "MedicalLib/Brain.h"
|
||||
#include "MedicalLib/Patient.h" // For Blood struct
|
||||
#include "MedicalLib/Heart.h" // For Heart data
|
||||
#include "MedicalLib/Lungs.h" // For setting respiration
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
@@ -21,6 +22,8 @@ Brain::Brain(int id)
|
||||
cerebralPerfusionPressure_mmHg(80.0),
|
||||
meanArterialPressure_mmHg(90.0), // Placeholder value
|
||||
totalTime_s(0.0),
|
||||
targetRespirationRate_bpm(16.0), // Normal baseline
|
||||
targetHeartRate_bpm(75.0), // Normal baseline
|
||||
eegHistorySize(200) {
|
||||
|
||||
// Initialize Brain Regions
|
||||
@@ -45,6 +48,7 @@ void Brain::update(Patient& patient, double deltaTime_s) {
|
||||
|
||||
updateActivity(deltaTime_s);
|
||||
updatePressures(meanArterialPressure_mmHg);
|
||||
updateAutonomicControl(patient, deltaTime_s);
|
||||
|
||||
// Update EEG data
|
||||
eegData.push_front(generateEegValue());
|
||||
@@ -107,6 +111,54 @@ void Brain::updatePressures(double meanArterialPressure) {
|
||||
cerebralPerfusionPressure_mmHg = std::max(0.0, cerebralPerfusionPressure_mmHg);
|
||||
}
|
||||
|
||||
void Brain::updateAutonomicControl(Patient& patient, double deltaTime_s) {
|
||||
const double& co2 = patient.blood.co2PartialPressure_mmHg;
|
||||
const double& o2 = patient.blood.oxygenSaturation;
|
||||
|
||||
// Chemoreceptor reflex: Adjust respiration based on blood gases
|
||||
double co2_error = co2 - 40.0; // Normal PaCO2 is 40 mmHg
|
||||
double o2_error = 98.0 - o2; // Normal SpO2 is ~98%
|
||||
|
||||
// Increase rate for high CO2 or low O2
|
||||
double co2_drive = std::max(0.0, co2_error) * 0.5; // Strong response to high CO2
|
||||
double o2_drive = std::max(0.0, o2_error) * 0.8; // Stronger response to hypoxia
|
||||
|
||||
double targetRate = 16.0 + co2_drive + o2_drive;
|
||||
|
||||
// Smoothly adjust the current rate towards the target rate
|
||||
double adjustmentSpeed = 0.5; // How quickly the rate changes
|
||||
targetRespirationRate_bpm += (targetRate - targetRespirationRate_bpm) * adjustmentSpeed * deltaTime_s;
|
||||
|
||||
// Clamp to a physiological range
|
||||
targetRespirationRate_bpm = std::clamp(targetRespirationRate_bpm, 8.0, 35.0);
|
||||
|
||||
if (Lungs* lungs = getOrgan<Lungs>(patient)) {
|
||||
lungs->setRespirationRate(targetRespirationRate_bpm);
|
||||
}
|
||||
|
||||
// Baroreceptor reflex: Adjust heart rate based on blood pressure
|
||||
const auto& bp = patient.blood.bloodPressure;
|
||||
double meanArterialPressure = bp.diastolic_mmHg + (bp.systolic_mmHg - bp.diastolic_mmHg) / 3.0;
|
||||
|
||||
double bp_error = 90.0 - meanArterialPressure; // Target MAP is 90 mmHg
|
||||
|
||||
// Change HR to correct the error.
|
||||
double hr_adjustment = bp_error * 0.4; // Proportional response
|
||||
|
||||
double targetRate_hr = 75.0 + hr_adjustment;
|
||||
|
||||
// Smoothly adjust the current rate towards the target rate
|
||||
double hrAdjustmentSpeed = 0.4;
|
||||
targetHeartRate_bpm += (targetRate_hr - targetHeartRate_bpm) * hrAdjustmentSpeed * deltaTime_s;
|
||||
|
||||
// Clamp to a physiological range
|
||||
targetHeartRate_bpm = std::clamp(targetHeartRate_bpm, 50.0, 160.0);
|
||||
|
||||
if (Heart* heart = getOrgan<Heart>(patient)) {
|
||||
heart->setHeartRate(targetHeartRate_bpm);
|
||||
}
|
||||
}
|
||||
|
||||
double Brain::generateEegValue() {
|
||||
// Super simplified EEG: combination of a few sine waves (alpha, beta)
|
||||
double alpha_wave = 0.5 * sin(2 * M_PI * 10 * totalTime_s); // 10 Hz
|
||||
|
||||
+24
-15
@@ -10,7 +10,8 @@ Gallbladder::Gallbladder(int id)
|
||||
: Organ(id, "Gallbladder"),
|
||||
currentState(GallbladderState::STORING),
|
||||
storedBile_mL(30.0),
|
||||
bileConcentrationFactor(5.0) {}
|
||||
bileConcentrationFactor(5.0),
|
||||
bileReleaseRate_ml_per_s(2.0) {}
|
||||
|
||||
void Gallbladder::update(Patient& patient, double deltaTime_s) {
|
||||
// Get bile from the liver
|
||||
@@ -20,37 +21,45 @@ void Gallbladder::update(Patient& patient, double deltaTime_s) {
|
||||
}
|
||||
|
||||
// A real model would also be driven by CCK hormone for contraction.
|
||||
// For now, simulate slow storage and concentration, with a periodic contraction.
|
||||
// Contraction is now triggered externally by releaseBile().
|
||||
|
||||
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;
|
||||
// After contracting for a while, or if empty, go back to storing.
|
||||
static double contractionTime = 0.0;
|
||||
contractionTime += deltaTime_s;
|
||||
|
||||
if (storedBile_mL < 5.0) { // Stop contracting when near empty
|
||||
if (storedBile_mL < 5.0 || contractionTime > 15.0) { // Stop contracting when near empty or after 15s
|
||||
storedBile_mL = std::max(0.0, storedBile_mL);
|
||||
bileConcentrationFactor = 1.0; // Bile is fresh
|
||||
if (storedBile_mL == 0.0) {
|
||||
bileConcentrationFactor = 1.0; // Bile is fresh if we're totally empty
|
||||
}
|
||||
currentState = GallbladderState::STORING;
|
||||
contractionTime = 0.0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double Gallbladder::releaseBile(double deltaTime_s) {
|
||||
if (storedBile_mL <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
currentState = GallbladderState::CONTRACTING;
|
||||
double amountToRelease = bileReleaseRate_ml_per_s * deltaTime_s;
|
||||
amountToRelease = std::min(amountToRelease, storedBile_mL); // Don't release more than we have
|
||||
storedBile_mL -= amountToRelease;
|
||||
|
||||
return amountToRelease;
|
||||
}
|
||||
|
||||
void Gallbladder::storeBile(double volume_mL) {
|
||||
if (currentState == GallbladderState::STORING) {
|
||||
storedBile_mL += volume_mL;
|
||||
|
||||
+17
-11
@@ -62,17 +62,9 @@ void Heart::update(Patient& patient, double deltaTime_s) {
|
||||
// --- Electrical Simulation Update ---
|
||||
totalTime_s += deltaTime_s;
|
||||
|
||||
// Compensatory response to hypoxia
|
||||
double o2_saturation = patient.blood.oxygenSaturation;
|
||||
double targetHeartRate = 75.0;
|
||||
if (o2_saturation < 90.0) {
|
||||
targetHeartRate = 75.0 + (90.0 - o2_saturation) * 2.0; // Increase HR as SpO2 drops
|
||||
}
|
||||
|
||||
// Move current heart rate towards the target
|
||||
heartRate += (targetHeartRate - heartRate) * 0.1 * deltaTime_s;
|
||||
heartRate += getFluctuation(0.01); // Slow variation in underlying rate
|
||||
heartRate = std::max(60.0, std::min(heartRate, 140.0));
|
||||
// The underlying heartRate is now set externally by the Brain.
|
||||
// We just add a little natural variation.
|
||||
heartRate += getFluctuation(0.01);
|
||||
double cycleDuration_s = 60.0 / heartRate;
|
||||
|
||||
double oldCyclePosition = cardiacCyclePosition_s;
|
||||
@@ -157,6 +149,14 @@ void Heart::update(Patient& patient, double deltaTime_s) {
|
||||
// Clamp volumes to realistic values
|
||||
leftVentricle.volume_mL = std::max(40.0, std::min(leftVentricle.volume_mL, 130.0));
|
||||
rightVentricle.volume_mL = std::max(40.0, std::min(rightVentricle.volume_mL, 130.0));
|
||||
|
||||
// --- Update Blood Pressure ---
|
||||
// Simplified model: BP is influenced by heart rate and RAAS.
|
||||
double angiotensinEffect = patient.blood.angiotensin_au * 2.0; // Angiotensin is a potent vasoconstrictor
|
||||
double systolic = 110.0 + (heartRate - 75.0) * 0.5 + angiotensinEffect;
|
||||
double diastolic = 75.0 + (heartRate - 75.0) * 0.25 + angiotensinEffect;
|
||||
patient.blood.bloodPressure.systolic_mmHg = std::clamp(systolic, 80.0, 180.0);
|
||||
patient.blood.bloodPressure.diastolic_mmHg = std::clamp(diastolic, 50.0, 110.0);
|
||||
}
|
||||
|
||||
double Heart::simulateEkgWaveform(double timeInCycle) {
|
||||
@@ -203,3 +203,9 @@ double Heart::getAorticPressure() const {
|
||||
// Simplified diastolic pressure decay
|
||||
return 80.0 + 40.0 * exp(-cardiacCyclePosition_s);
|
||||
}
|
||||
|
||||
void Heart::setHeartRate(double newRate_bpm) {
|
||||
// Set the underlying target heart rate. The simulation will then use this
|
||||
// as the basis for its cycle timing.
|
||||
heartRate = newRate_bpm;
|
||||
}
|
||||
|
||||
+63
-8
@@ -1,5 +1,7 @@
|
||||
#include "MedicalLib/Intestines.h"
|
||||
#include "MedicalLib/Patient.h"
|
||||
#include "MedicalLib/Gallbladder.h"
|
||||
#include "MedicalLib/Pancreas.h"
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
@@ -15,7 +17,11 @@ static double getFluctuation(double stddev) {
|
||||
|
||||
Intestines::Intestines(int id)
|
||||
: Organ(id, "Intestines"),
|
||||
chymeVolume_mL(0.0) {
|
||||
chymeVolume_mL(0.0),
|
||||
bileVolume_mL(0.0),
|
||||
enzymeVolume_mL(0.0),
|
||||
amylase_U_per_L(0.0),
|
||||
lipase_U_per_L(0.0) {
|
||||
|
||||
// Initialize segments with typical values
|
||||
duodenum = {"Duodenum", 0.25, 1.0, 0.5, 0.1};
|
||||
@@ -26,18 +32,49 @@ Intestines::Intestines(int id)
|
||||
|
||||
void Intestines::update(Patient& patient, double 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;
|
||||
// 1. Signal Gallbladder and Pancreas to release substances
|
||||
if (Gallbladder* gallbladder = getOrgan<Gallbladder>(patient)) {
|
||||
double bileReleased = gallbladder->releaseBile(deltaTime_s);
|
||||
receiveBile(bileReleased);
|
||||
}
|
||||
if (Pancreas* pancreas = getOrgan<Pancreas>(patient)) {
|
||||
DigestiveEnzymes enzymesReleased = pancreas->releaseEnzymes(deltaTime_s);
|
||||
receiveEnzymes(enzymesReleased);
|
||||
}
|
||||
|
||||
// 2. Digestion and Absorption
|
||||
// Bile helps emulsify fats, enzymes break down nutrients.
|
||||
// We'll model this as a "digestion efficiency" multiplier.
|
||||
double digestionEfficiency = 1.0;
|
||||
if (bileVolume_mL > 0 && enzymeVolume_mL > 0) {
|
||||
digestionEfficiency = 5.0; // 5x more effective with bile and enzymes
|
||||
}
|
||||
|
||||
// Simplified absorption model
|
||||
double totalNutrientAbsorptionRate = (duodenum.nutrientAbsorptionRate + jejunum.nutrientAbsorptionRate + ileum.nutrientAbsorptionRate) * digestionEfficiency;
|
||||
double totalWaterAbsorptionRate = (duodenum.waterAbsorptionRate + jejunum.waterAbsorptionRate + ileum.waterAbsorptionRate + colon.waterAbsorptionRate);
|
||||
|
||||
// Absorb glucose into the blood
|
||||
double glucoseAbsorption = totalNutrientAbsorption * chymeVolume_mL * 0.001 * deltaTime_s;
|
||||
double glucoseAbsorption = totalNutrientAbsorptionRate * chymeVolume_mL * 0.001 * deltaTime_s;
|
||||
patient.blood.glucose_mg_per_dL += glucoseAbsorption;
|
||||
|
||||
// Reduce chyme volume based on absorption
|
||||
double absorbedVolume = (totalNutrientAbsorption * 0.01 + totalWaterAbsorption * 0.1) * deltaTime_s;
|
||||
// Reduce chyme/bile/enzyme volume based on absorption and processing
|
||||
double absorbedVolume = (totalNutrientAbsorptionRate * 0.01 + totalWaterAbsorptionRate * 0.1) * deltaTime_s;
|
||||
chymeVolume_mL -= absorbedVolume;
|
||||
|
||||
// As chyme is processed, bile and enzymes are used up
|
||||
bileVolume_mL -= 0.1 * bileVolume_mL * deltaTime_s;
|
||||
enzymeVolume_mL -= 0.1 * enzymeVolume_mL * deltaTime_s;
|
||||
|
||||
// Clamp volumes to zero
|
||||
chymeVolume_mL = std::max(0.0, chymeVolume_mL);
|
||||
bileVolume_mL = std::max(0.0, bileVolume_mL);
|
||||
enzymeVolume_mL = std::max(0.0, enzymeVolume_mL);
|
||||
|
||||
if (enzymeVolume_mL == 0.0) {
|
||||
amylase_U_per_L = 0.0;
|
||||
lipase_U_per_L = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Fluctuate motility slightly
|
||||
@@ -49,12 +86,30 @@ void Intestines::receiveChyme(double volume_mL) {
|
||||
chymeVolume_mL += volume_mL;
|
||||
}
|
||||
|
||||
void Intestines::receiveBile(double volume_mL) {
|
||||
bileVolume_mL += volume_mL;
|
||||
}
|
||||
|
||||
void Intestines::receiveEnzymes(const DigestiveEnzymes& enzymes) {
|
||||
if (enzymes.volume_mL <= 0) return;
|
||||
|
||||
// Calculate new weighted average of enzyme concentration
|
||||
double total_enzyme_vol = enzymeVolume_mL + enzymes.volume_mL;
|
||||
amylase_U_per_L = (amylase_U_per_L * enzymeVolume_mL + enzymes.amylase_U_per_L * enzymes.volume_mL) / total_enzyme_vol;
|
||||
lipase_U_per_L = (lipase_U_per_L * enzymeVolume_mL + enzymes.lipase_U_per_L * enzymes.volume_mL) / total_enzyme_vol;
|
||||
enzymeVolume_mL = total_enzyme_vol;
|
||||
}
|
||||
|
||||
std::string Intestines::getSummary() const {
|
||||
std::stringstream ss;
|
||||
ss.precision(2);
|
||||
ss << std::fixed;
|
||||
ss << "--- Intestines Summary ---\n"
|
||||
<< "Total Chyme Volume: " << getTotalChymeVolume() << " mL\n\n"
|
||||
<< "Chyme Volume: " << getTotalChymeVolume() << " mL\n"
|
||||
<< "Bile Volume: " << bileVolume_mL << " mL\n"
|
||||
<< "Enzyme Volume: " << enzymeVolume_mL << " mL\n"
|
||||
<< "Amylase: " << amylase_U_per_L << " U/L\n"
|
||||
<< "Lipase: " << lipase_U_per_L << " U/L\n\n"
|
||||
<< "--- Segments ---\n"
|
||||
<< duodenum.name << ": Motility " << duodenum.motility << "\n"
|
||||
<< jejunum.name << ": Nutrient Abs. " << jejunum.nutrientAbsorptionRate << "\n"
|
||||
|
||||
@@ -18,6 +18,7 @@ static double getFluctuation(double stddev) {
|
||||
|
||||
Kidneys::Kidneys(int id)
|
||||
: Organ(id, "Kidneys"),
|
||||
reninSecretionRate(1.0), // Baseline ng/mL/hr
|
||||
gfr_mL_per_min(125.0),
|
||||
urineOutput_mL_per_s(0.02),
|
||||
bloodSodium_mEq_per_L(140.0),
|
||||
@@ -64,11 +65,24 @@ void Kidneys::update(Patient& patient, double deltaTime_s) {
|
||||
bloodSodium_mEq_per_L += getFluctuation(0.05);
|
||||
bloodPotassium_mEq_per_L += getFluctuation(0.01);
|
||||
|
||||
// --- RAAS Regulation ---
|
||||
// Renin is released in response to low blood pressure.
|
||||
const auto& bp = patient.blood.bloodPressure;
|
||||
double meanArterialPressure = bp.diastolic_mmHg + (bp.systolic_mmHg - bp.diastolic_mmHg) / 3.0;
|
||||
|
||||
if (meanArterialPressure < 85.0) { // Low pressure threshold
|
||||
reninSecretionRate += (85.0 - meanArterialPressure) * 0.1 * deltaTime_s;
|
||||
} else {
|
||||
// If pressure is normal, renin decays back to baseline
|
||||
reninSecretionRate -= (reninSecretionRate - 1.0) * 0.05 * deltaTime_s;
|
||||
}
|
||||
|
||||
// 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);
|
||||
reninSecretionRate = std::clamp(reninSecretionRate, 0.5, 50.0);
|
||||
}
|
||||
|
||||
std::string Kidneys::getSummary() const {
|
||||
@@ -78,6 +92,7 @@ std::string Kidneys::getSummary() const {
|
||||
ss << "--- Kidneys Summary ---\n"
|
||||
<< "Glomerular Filtration Rate (GFR): " << getGfr() << " mL/min\n"
|
||||
<< "Urine Output: " << getUrineOutputRate() * 3600 << " mL/hr\n"
|
||||
<< "Renin Secretion: " << getReninSecretionRate() << " ng/mL/hr\n"
|
||||
<< "Blood Sodium: " << getBloodSodium() << " mEq/L\n"
|
||||
<< "Blood Potassium: " << getBloodPotassium() << " mEq/L\n";
|
||||
return ss.str();
|
||||
@@ -88,3 +103,4 @@ 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; }
|
||||
double Kidneys::getReninSecretionRate() const { return reninSecretionRate; }
|
||||
|
||||
@@ -15,6 +15,7 @@ static double getFluctuation(double stddev) {
|
||||
|
||||
Liver::Liver(int id)
|
||||
: Organ(id, "Liver"),
|
||||
angiotensinogen_production_rate(10.0), // Constant production
|
||||
bileProductionRate_ml_per_s(0.0069),
|
||||
glucoseProductionRate_g_per_s(0.001),
|
||||
alt_U_per_L(25.0),
|
||||
@@ -97,3 +98,4 @@ double Liver::getGlucoseProductionRate() const { return glucoseProductionRate_g_
|
||||
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; }
|
||||
double Liver::getAngiotensinogenRate() const { return angiotensinogen_production_rate; }
|
||||
|
||||
+5
-4
@@ -109,10 +109,6 @@ void Lungs::updateRespiratoryMechanics(double deltaTime_s) {
|
||||
}
|
||||
|
||||
void Lungs::updateGasLevels(double deltaTime_s) {
|
||||
// Fluctuate base vitals slightly
|
||||
respirationRate += getFluctuation(0.01);
|
||||
respirationRate = std::clamp(respirationRate, 12.0, 20.0);
|
||||
|
||||
// SpO2 is affected by how well we are breathing
|
||||
double ventilationFactor = (tidalVolume_mL / 500.0) * (respirationRate / 16.0);
|
||||
double targetSpo2 = 98.0 * std::clamp(ventilationFactor, 0.9, 1.0);
|
||||
@@ -174,3 +170,8 @@ double Lungs::getTidalVolume() const { return tidalVolume_mL; }
|
||||
double Lungs::getEndTidalCO2() const { return endTidalCO2_mmHg; }
|
||||
double Lungs::getPeakInspiratoryPressure() const { return peakInspiratoryPressure_cmH2O; }
|
||||
const std::deque<double>& Lungs::getCapnographyWaveform() const { return capnographyData; }
|
||||
|
||||
// --- Setters Implementation ---
|
||||
void Lungs::setRespirationRate(double newRate_bpm) {
|
||||
respirationRate = newRate_bpm;
|
||||
}
|
||||
|
||||
+15
-1
@@ -18,7 +18,8 @@ Pancreas::Pancreas(int id)
|
||||
insulinSecretion_units_per_hr(1.0),
|
||||
glucagonSecretion_ng_per_hr(50.0),
|
||||
amylaseSecretion_U_per_L(80.0),
|
||||
lipaseSecretion_U_per_L(40.0) {}
|
||||
lipaseSecretion_U_per_L(40.0),
|
||||
enzymeReleaseRate_ml_per_s(0.5) {}
|
||||
|
||||
void Pancreas::update(Patient& patient, double deltaTime_s) {
|
||||
// Hormone secretion is driven by blood glucose.
|
||||
@@ -52,6 +53,19 @@ void Pancreas::update(Patient& patient, double deltaTime_s) {
|
||||
lipaseSecretion_U_per_L = std::clamp(lipaseSecretion_U_per_L, 20.0, 60.0);
|
||||
}
|
||||
|
||||
DigestiveEnzymes Pancreas::releaseEnzymes(double deltaTime_s) {
|
||||
DigestiveEnzymes enzymes;
|
||||
enzymes.volume_mL = enzymeReleaseRate_ml_per_s * deltaTime_s;
|
||||
enzymes.amylase_U_per_L = getAmylaseSecretion();
|
||||
enzymes.lipase_U_per_L = getLipaseSecretion();
|
||||
|
||||
// When stimulated, enzyme production should ramp up
|
||||
amylaseSecretion_U_per_L += 2.0 * deltaTime_s;
|
||||
lipaseSecretion_U_per_L += 2.0 * deltaTime_s;
|
||||
|
||||
return enzymes;
|
||||
}
|
||||
|
||||
std::string Pancreas::getSummary() const {
|
||||
std::stringstream ss;
|
||||
ss.precision(1);
|
||||
|
||||
+19
-2
@@ -58,11 +58,28 @@ Patient initializePatient(int patientId) {
|
||||
* @param deltaTime_s The time elapsed in seconds.
|
||||
*/
|
||||
void updatePatient(Patient& patient, double deltaTime_s) {
|
||||
// Update all organs. The new update function handles all internal state changes
|
||||
// and inter-organ interactions.
|
||||
// First, update all the organs to their new states
|
||||
for (auto& organ : patient.organs) {
|
||||
organ->update(patient, deltaTime_s);
|
||||
}
|
||||
|
||||
// Next, handle systemic blood chemistry changes like RAAS
|
||||
const Liver* liver = getOrgan<const Liver>(patient);
|
||||
const Kidneys* kidneys = getOrgan<const Kidneys>(patient);
|
||||
|
||||
if (liver && kidneys) {
|
||||
double renin = kidneys->getReninSecretionRate();
|
||||
double angiotensinogen = liver->getAngiotensinogenRate();
|
||||
|
||||
// Simplified conversion: Renin acts as an enzyme to convert Angiotensinogen
|
||||
double production_rate = renin * angiotensinogen * 0.01;
|
||||
patient.blood.angiotensin_au += production_rate * deltaTime_s;
|
||||
}
|
||||
|
||||
// Angiotensin II has a half-life of ~15-30 seconds. We'll model a decay.
|
||||
double decay_rate = 0.04; // Corresponds to a half-life of ~17s
|
||||
patient.blood.angiotensin_au -= patient.blood.angiotensin_au * decay_rate * deltaTime_s;
|
||||
patient.blood.angiotensin_au = std::max(0.0, patient.blood.angiotensin_au);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+44507
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user