feat: Implement major physiological feedback loops
This commit re-implements several critical physiological feedback loops that were lost, enhancing the realism of the simulation.
The following systems have been added:
1. **Full Digestive Loop:**
- The Gallbladder now has a `releaseBile` method, triggered by chyme in the intestines.
- The Pancreas has a `releaseEnzymes` method, also triggered by chyme.
- The Intestines' digestion logic has been updated to be more effective when bile and enzymes are present.
2. **Autonomic Nervous System Control:**
- The Brain now monitors blood gas (O2/CO2) and blood pressure levels.
- It dynamically adjusts the respiration rate of the Lungs via a new `setRespirationRate` method in response to blood gas changes.
- It controls the heart rate via a new `setHeartRate` method in response to blood pressure changes, simulating the baroreceptor reflex.
- The previous hardcoded rate control logic in the Lungs and Heart has been removed.
3. **Kidney Blood Pressure Regulation (RAAS):**
- A simplified Renin-Angiotensin-Aldosterone System has been implemented.
- The Kidneys now secrete renin in response to low blood pressure.
- The Liver produces a constant supply of angiotensinogen.
- A new `angiotensin_au` value in the Blood struct is calculated in the main patient update loop.
- This hormone now acts as a vasoconstrictor, directly affecting the blood pressure calculation in the Heart.
These changes significantly increase the complexity and fidelity of the medical simulation by modeling the interconnectedness of the major organ systems.
This commit is contained in:
@@ -57,6 +57,7 @@ private:
|
|||||||
// --- Private Helper Methods ---
|
// --- Private Helper Methods ---
|
||||||
void updateActivity(double deltaTime_s);
|
void updateActivity(double deltaTime_s);
|
||||||
void updatePressures(double meanArterialPressure);
|
void updatePressures(double meanArterialPressure);
|
||||||
|
void updateAutonomicControl(Patient& patient, double deltaTime_s);
|
||||||
double generateEegValue();
|
double generateEegValue();
|
||||||
|
|
||||||
// --- Physiological Parameters ---
|
// --- Physiological Parameters ---
|
||||||
@@ -75,6 +76,10 @@ private:
|
|||||||
BrainRegion occipitalLobe;
|
BrainRegion occipitalLobe;
|
||||||
BrainRegion cerebellum;
|
BrainRegion cerebellum;
|
||||||
|
|
||||||
|
// --- Autonomic Control Targets ---
|
||||||
|
double targetRespirationRate_bpm;
|
||||||
|
double targetHeartRate_bpm;
|
||||||
|
|
||||||
// --- Waveform Data ---
|
// --- Waveform Data ---
|
||||||
std::deque<double> eegData;
|
std::deque<double> eegData;
|
||||||
size_t eegHistorySize;
|
size_t eegHistorySize;
|
||||||
|
|||||||
@@ -41,6 +41,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
void storeBile(double volume_mL);
|
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 ---
|
// --- Getters for Gallbladder State ---
|
||||||
|
|
||||||
/** @brief Gets the current volume of stored bile in mL. */
|
/** @brief Gets the current volume of stored bile in mL. */
|
||||||
@@ -60,5 +67,6 @@ private:
|
|||||||
GallbladderState currentState;
|
GallbladderState currentState;
|
||||||
double storedBile_mL;
|
double storedBile_mL;
|
||||||
double bileConcentrationFactor; // How concentrated the bile is (1x, 5x, etc.)
|
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;
|
const double capacity_mL = 50.0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ public:
|
|||||||
std::string getSummary() const override;
|
std::string getSummary() const override;
|
||||||
|
|
||||||
// --- Electrical Properties ---
|
// --- Electrical Properties ---
|
||||||
|
void setHeartRate(double newRate_bpm);
|
||||||
double getHeartRate() const;
|
double getHeartRate() const;
|
||||||
const std::map<std::string, std::deque<double>>& getEkgData() const;
|
const std::map<std::string, std::deque<double>>& getEkgData() const;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
// Forward-declare for receiveEnzymes method
|
||||||
|
struct DigestiveEnzymes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represents a segment of the intestines.
|
* @brief Represents a segment of the intestines.
|
||||||
*/
|
*/
|
||||||
@@ -45,6 +48,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
void receiveChyme(double volume_mL);
|
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 ---
|
// --- Getters for Intestinal State ---
|
||||||
|
|
||||||
/** @brief Gets the total volume of chyme currently in the intestines. */
|
/** @brief Gets the total volume of chyme currently in the intestines. */
|
||||||
@@ -59,4 +74,8 @@ private:
|
|||||||
|
|
||||||
// --- Simulation State ---
|
// --- Simulation State ---
|
||||||
double chymeVolume_mL; // Total volume in the whole system for now
|
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. */
|
/** @brief Gets the simulated blood potassium level in mEq/L. */
|
||||||
double getBloodPotassium() const;
|
double getBloodPotassium() const;
|
||||||
|
|
||||||
|
/** @brief Gets the current rate of renin secretion. */
|
||||||
|
double getReninSecretionRate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Physiological Parameters ---
|
// --- Physiological Parameters ---
|
||||||
|
double reninSecretionRate; // In ng/mL/hr
|
||||||
double gfr_mL_per_min;
|
double gfr_mL_per_min;
|
||||||
double urineOutput_mL_per_s;
|
double urineOutput_mL_per_s;
|
||||||
double bloodSodium_mEq_per_L;
|
double bloodSodium_mEq_per_L;
|
||||||
|
|||||||
@@ -54,8 +54,12 @@ public:
|
|||||||
/** @brief Gets the total bilirubin level in mg/dL. */
|
/** @brief Gets the total bilirubin level in mg/dL. */
|
||||||
double getBilirubinLevel() const;
|
double getBilirubinLevel() const;
|
||||||
|
|
||||||
|
/** @brief Gets the production rate of angiotensinogen. */
|
||||||
|
double getAngiotensinogenRate() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// --- Physiological Parameters ---
|
// --- Physiological Parameters ---
|
||||||
|
double angiotensinogen_production_rate; // In arbitrary units/s
|
||||||
double bileProductionRate_ml_per_s;
|
double bileProductionRate_ml_per_s;
|
||||||
double glucoseProductionRate_g_per_s;
|
double glucoseProductionRate_g_per_s;
|
||||||
double alt_U_per_L; ///< Alanine Aminotransferase
|
double alt_U_per_L; ///< Alanine Aminotransferase
|
||||||
|
|||||||
@@ -61,6 +61,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
void inflictDamage(double damage);
|
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 ---
|
// --- Getters for Key Respiratory Vitals ---
|
||||||
|
|
||||||
/** @brief Gets the current respiration rate in breaths per minute. */
|
/** @brief Gets the current respiration rate in breaths per minute. */
|
||||||
|
|||||||
@@ -3,6 +3,15 @@
|
|||||||
#include "Organ.h"
|
#include "Organ.h"
|
||||||
#include <string>
|
#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.
|
* @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. */
|
/** @brief Gets the current lipase secretion rate in U/L. */
|
||||||
double getLipaseSecretion() const;
|
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:
|
private:
|
||||||
// --- Endocrine Parameters ---
|
// --- Endocrine Parameters ---
|
||||||
double insulinSecretion_units_per_hr;
|
double insulinSecretion_units_per_hr;
|
||||||
@@ -51,4 +69,5 @@ private:
|
|||||||
// --- Exocrine Parameters ---
|
// --- Exocrine Parameters ---
|
||||||
double amylaseSecretion_U_per_L;
|
double amylaseSecretion_U_per_L;
|
||||||
double lipaseSecretion_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
|
// Forward-declare the Organ class to avoid circular dependencies
|
||||||
class Organ;
|
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.
|
* @brief Represents the composition of the patient's blood.
|
||||||
*/
|
*/
|
||||||
struct Blood {
|
struct Blood {
|
||||||
|
BloodPressure bloodPressure;
|
||||||
double oxygenSaturation = 98.0; // Normal SpO2
|
double oxygenSaturation = 98.0; // Normal SpO2
|
||||||
double co2PartialPressure_mmHg = 40.0; // Normal PaCO2
|
double co2PartialPressure_mmHg = 40.0; // Normal PaCO2
|
||||||
double glucose_mg_per_dL = 100.0; // Normal fasting glucose
|
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
|
double toxins_au = 0.0; // Arbitrary units, 0 is clean
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "MedicalLib/Brain.h"
|
#include "MedicalLib/Brain.h"
|
||||||
#include "MedicalLib/Patient.h" // For Blood struct
|
#include "MedicalLib/Patient.h" // For Blood struct
|
||||||
#include "MedicalLib/Heart.h" // For Heart data
|
#include "MedicalLib/Heart.h" // For Heart data
|
||||||
|
#include "MedicalLib/Lungs.h" // For setting respiration
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -21,6 +22,8 @@ Brain::Brain(int id)
|
|||||||
cerebralPerfusionPressure_mmHg(80.0),
|
cerebralPerfusionPressure_mmHg(80.0),
|
||||||
meanArterialPressure_mmHg(90.0), // Placeholder value
|
meanArterialPressure_mmHg(90.0), // Placeholder value
|
||||||
totalTime_s(0.0),
|
totalTime_s(0.0),
|
||||||
|
targetRespirationRate_bpm(16.0), // Normal baseline
|
||||||
|
targetHeartRate_bpm(75.0), // Normal baseline
|
||||||
eegHistorySize(200) {
|
eegHistorySize(200) {
|
||||||
|
|
||||||
// Initialize Brain Regions
|
// Initialize Brain Regions
|
||||||
@@ -45,6 +48,7 @@ void Brain::update(Patient& patient, double deltaTime_s) {
|
|||||||
|
|
||||||
updateActivity(deltaTime_s);
|
updateActivity(deltaTime_s);
|
||||||
updatePressures(meanArterialPressure_mmHg);
|
updatePressures(meanArterialPressure_mmHg);
|
||||||
|
updateAutonomicControl(patient, deltaTime_s);
|
||||||
|
|
||||||
// Update EEG data
|
// Update EEG data
|
||||||
eegData.push_front(generateEegValue());
|
eegData.push_front(generateEegValue());
|
||||||
@@ -107,6 +111,54 @@ void Brain::updatePressures(double meanArterialPressure) {
|
|||||||
cerebralPerfusionPressure_mmHg = std::max(0.0, cerebralPerfusionPressure_mmHg);
|
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() {
|
double Brain::generateEegValue() {
|
||||||
// Super simplified EEG: combination of a few sine waves (alpha, beta)
|
// 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
|
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"),
|
: Organ(id, "Gallbladder"),
|
||||||
currentState(GallbladderState::STORING),
|
currentState(GallbladderState::STORING),
|
||||||
storedBile_mL(30.0),
|
storedBile_mL(30.0),
|
||||||
bileConcentrationFactor(5.0) {}
|
bileConcentrationFactor(5.0),
|
||||||
|
bileReleaseRate_ml_per_s(2.0) {}
|
||||||
|
|
||||||
void Gallbladder::update(Patient& patient, double deltaTime_s) {
|
void Gallbladder::update(Patient& patient, double deltaTime_s) {
|
||||||
// Get bile from the liver
|
// 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.
|
// 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) {
|
switch (currentState) {
|
||||||
case GallbladderState::STORING:
|
case GallbladderState::STORING:
|
||||||
// Concentrate bile over time
|
// Concentrate bile over time
|
||||||
bileConcentrationFactor += 0.05 * deltaTime_s;
|
bileConcentrationFactor += 0.05 * deltaTime_s;
|
||||||
bileConcentrationFactor = std::min(10.0, bileConcentrationFactor);
|
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;
|
break;
|
||||||
|
|
||||||
case GallbladderState::CONTRACTING:
|
case GallbladderState::CONTRACTING:
|
||||||
// Release bile
|
// After contracting for a while, or if empty, go back to storing.
|
||||||
double releasedBile = 2.0 * deltaTime_s;
|
static double contractionTime = 0.0;
|
||||||
storedBile_mL -= releasedBile;
|
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);
|
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;
|
currentState = GallbladderState::STORING;
|
||||||
|
contractionTime = 0.0;
|
||||||
}
|
}
|
||||||
break;
|
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) {
|
void Gallbladder::storeBile(double volume_mL) {
|
||||||
if (currentState == GallbladderState::STORING) {
|
if (currentState == GallbladderState::STORING) {
|
||||||
storedBile_mL += volume_mL;
|
storedBile_mL += volume_mL;
|
||||||
|
|||||||
+17
-11
@@ -62,17 +62,9 @@ void Heart::update(Patient& patient, double deltaTime_s) {
|
|||||||
// --- Electrical Simulation Update ---
|
// --- Electrical Simulation Update ---
|
||||||
totalTime_s += deltaTime_s;
|
totalTime_s += deltaTime_s;
|
||||||
|
|
||||||
// Compensatory response to hypoxia
|
// The underlying heartRate is now set externally by the Brain.
|
||||||
double o2_saturation = patient.blood.oxygenSaturation;
|
// We just add a little natural variation.
|
||||||
double targetHeartRate = 75.0;
|
heartRate += getFluctuation(0.01);
|
||||||
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));
|
|
||||||
double cycleDuration_s = 60.0 / heartRate;
|
double cycleDuration_s = 60.0 / heartRate;
|
||||||
|
|
||||||
double oldCyclePosition = cardiacCyclePosition_s;
|
double oldCyclePosition = cardiacCyclePosition_s;
|
||||||
@@ -157,6 +149,14 @@ void Heart::update(Patient& patient, double deltaTime_s) {
|
|||||||
// Clamp volumes to realistic values
|
// Clamp volumes to realistic values
|
||||||
leftVentricle.volume_mL = std::max(40.0, std::min(leftVentricle.volume_mL, 130.0));
|
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));
|
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) {
|
double Heart::simulateEkgWaveform(double timeInCycle) {
|
||||||
@@ -203,3 +203,9 @@ double Heart::getAorticPressure() const {
|
|||||||
// Simplified diastolic pressure decay
|
// Simplified diastolic pressure decay
|
||||||
return 80.0 + 40.0 * exp(-cardiacCyclePosition_s);
|
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/Intestines.h"
|
||||||
#include "MedicalLib/Patient.h"
|
#include "MedicalLib/Patient.h"
|
||||||
|
#include "MedicalLib/Gallbladder.h"
|
||||||
|
#include "MedicalLib/Pancreas.h"
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -15,7 +17,11 @@ static double getFluctuation(double stddev) {
|
|||||||
|
|
||||||
Intestines::Intestines(int id)
|
Intestines::Intestines(int id)
|
||||||
: Organ(id, "Intestines"),
|
: 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
|
// Initialize segments with typical values
|
||||||
duodenum = {"Duodenum", 0.25, 1.0, 0.5, 0.1};
|
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) {
|
void Intestines::update(Patient& patient, double deltaTime_s) {
|
||||||
if (chymeVolume_mL > 0) {
|
if (chymeVolume_mL > 0) {
|
||||||
// Simplified absorption model: total absorption is an average of all segments
|
// 1. Signal Gallbladder and Pancreas to release substances
|
||||||
double totalNutrientAbsorption = (duodenum.nutrientAbsorptionRate + jejunum.nutrientAbsorptionRate + ileum.nutrientAbsorptionRate + colon.nutrientAbsorptionRate) / 4.0;
|
if (Gallbladder* gallbladder = getOrgan<Gallbladder>(patient)) {
|
||||||
double totalWaterAbsorption = (duodenum.waterAbsorptionRate + jejunum.waterAbsorptionRate + ileum.waterAbsorptionRate + colon.waterAbsorptionRate) / 4.0;
|
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
|
// 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;
|
patient.blood.glucose_mg_per_dL += glucoseAbsorption;
|
||||||
|
|
||||||
// Reduce chyme volume based on absorption
|
// Reduce chyme/bile/enzyme volume based on absorption and processing
|
||||||
double absorbedVolume = (totalNutrientAbsorption * 0.01 + totalWaterAbsorption * 0.1) * deltaTime_s;
|
double absorbedVolume = (totalNutrientAbsorptionRate * 0.01 + totalWaterAbsorptionRate * 0.1) * deltaTime_s;
|
||||||
chymeVolume_mL -= absorbedVolume;
|
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);
|
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
|
// Fluctuate motility slightly
|
||||||
@@ -49,12 +86,30 @@ void Intestines::receiveChyme(double volume_mL) {
|
|||||||
chymeVolume_mL += 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::string Intestines::getSummary() const {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss.precision(2);
|
ss.precision(2);
|
||||||
ss << std::fixed;
|
ss << std::fixed;
|
||||||
ss << "--- Intestines Summary ---\n"
|
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"
|
<< "--- Segments ---\n"
|
||||||
<< duodenum.name << ": Motility " << duodenum.motility << "\n"
|
<< duodenum.name << ": Motility " << duodenum.motility << "\n"
|
||||||
<< jejunum.name << ": Nutrient Abs. " << jejunum.nutrientAbsorptionRate << "\n"
|
<< jejunum.name << ": Nutrient Abs. " << jejunum.nutrientAbsorptionRate << "\n"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ static double getFluctuation(double stddev) {
|
|||||||
|
|
||||||
Kidneys::Kidneys(int id)
|
Kidneys::Kidneys(int id)
|
||||||
: Organ(id, "Kidneys"),
|
: Organ(id, "Kidneys"),
|
||||||
|
reninSecretionRate(1.0), // Baseline ng/mL/hr
|
||||||
gfr_mL_per_min(125.0),
|
gfr_mL_per_min(125.0),
|
||||||
urineOutput_mL_per_s(0.02),
|
urineOutput_mL_per_s(0.02),
|
||||||
bloodSodium_mEq_per_L(140.0),
|
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);
|
bloodSodium_mEq_per_L += getFluctuation(0.05);
|
||||||
bloodPotassium_mEq_per_L += getFluctuation(0.01);
|
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
|
// Clamp to healthy ranges
|
||||||
gfr_mL_per_min = std::clamp(gfr_mL_per_min, 90.0, 150.0);
|
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);
|
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);
|
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);
|
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 {
|
std::string Kidneys::getSummary() const {
|
||||||
@@ -78,6 +92,7 @@ std::string Kidneys::getSummary() const {
|
|||||||
ss << "--- Kidneys Summary ---\n"
|
ss << "--- Kidneys Summary ---\n"
|
||||||
<< "Glomerular Filtration Rate (GFR): " << getGfr() << " mL/min\n"
|
<< "Glomerular Filtration Rate (GFR): " << getGfr() << " mL/min\n"
|
||||||
<< "Urine Output: " << getUrineOutputRate() * 3600 << " mL/hr\n"
|
<< "Urine Output: " << getUrineOutputRate() * 3600 << " mL/hr\n"
|
||||||
|
<< "Renin Secretion: " << getReninSecretionRate() << " ng/mL/hr\n"
|
||||||
<< "Blood Sodium: " << getBloodSodium() << " mEq/L\n"
|
<< "Blood Sodium: " << getBloodSodium() << " mEq/L\n"
|
||||||
<< "Blood Potassium: " << getBloodPotassium() << " mEq/L\n";
|
<< "Blood Potassium: " << getBloodPotassium() << " mEq/L\n";
|
||||||
return ss.str();
|
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::getUrineOutputRate() const { return urineOutput_mL_per_s; }
|
||||||
double Kidneys::getBloodSodium() const { return bloodSodium_mEq_per_L; }
|
double Kidneys::getBloodSodium() const { return bloodSodium_mEq_per_L; }
|
||||||
double Kidneys::getBloodPotassium() const { return bloodPotassium_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)
|
Liver::Liver(int id)
|
||||||
: Organ(id, "Liver"),
|
: Organ(id, "Liver"),
|
||||||
|
angiotensinogen_production_rate(10.0), // Constant production
|
||||||
bileProductionRate_ml_per_s(0.0069),
|
bileProductionRate_ml_per_s(0.0069),
|
||||||
glucoseProductionRate_g_per_s(0.001),
|
glucoseProductionRate_g_per_s(0.001),
|
||||||
alt_U_per_L(25.0),
|
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::getAltLevel() const { return alt_U_per_L; }
|
||||||
double Liver::getAstLevel() const { return ast_U_per_L; }
|
double Liver::getAstLevel() const { return ast_U_per_L; }
|
||||||
double Liver::getBilirubinLevel() const { return bilirubin_mg_per_dL; }
|
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) {
|
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
|
// SpO2 is affected by how well we are breathing
|
||||||
double ventilationFactor = (tidalVolume_mL / 500.0) * (respirationRate / 16.0);
|
double ventilationFactor = (tidalVolume_mL / 500.0) * (respirationRate / 16.0);
|
||||||
double targetSpo2 = 98.0 * std::clamp(ventilationFactor, 0.9, 1.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::getEndTidalCO2() const { return endTidalCO2_mmHg; }
|
||||||
double Lungs::getPeakInspiratoryPressure() const { return peakInspiratoryPressure_cmH2O; }
|
double Lungs::getPeakInspiratoryPressure() const { return peakInspiratoryPressure_cmH2O; }
|
||||||
const std::deque<double>& Lungs::getCapnographyWaveform() const { return capnographyData; }
|
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),
|
insulinSecretion_units_per_hr(1.0),
|
||||||
glucagonSecretion_ng_per_hr(50.0),
|
glucagonSecretion_ng_per_hr(50.0),
|
||||||
amylaseSecretion_U_per_L(80.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) {
|
void Pancreas::update(Patient& patient, double deltaTime_s) {
|
||||||
// Hormone secretion is driven by blood glucose.
|
// 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);
|
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::string Pancreas::getSummary() const {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss.precision(1);
|
ss.precision(1);
|
||||||
|
|||||||
+19
-2
@@ -58,11 +58,28 @@ Patient initializePatient(int patientId) {
|
|||||||
* @param deltaTime_s The time elapsed in seconds.
|
* @param deltaTime_s The time elapsed in seconds.
|
||||||
*/
|
*/
|
||||||
void updatePatient(Patient& patient, double deltaTime_s) {
|
void updatePatient(Patient& patient, double deltaTime_s) {
|
||||||
// Update all organs. The new update function handles all internal state changes
|
// First, update all the organs to their new states
|
||||||
// and inter-organ interactions.
|
|
||||||
for (auto& organ : patient.organs) {
|
for (auto& organ : patient.organs) {
|
||||||
organ->update(patient, deltaTime_s);
|
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