feat: Add detailed simulation to Lungs and Brain
Applied the level of detail from the Heart simulation to the Lungs and Brain classes. For the Lungs: - Added anatomical structures for lobes and bronchi. - Implemented a respiratory cycle with inspiration/expiration states. - Simulated tidal volume, airway pressures, and gas exchange. - Generated a capnography (etCO2) waveform. For the Brain: - Added structures for major brain regions. - Simulated intracranial pressure (ICP) and cerebral perfusion pressure (CPP). - Implemented a simplified Glasgow Coma Scale (GCS). - Generated a basic EEG waveform.
This commit is contained in:
@@ -1,9 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "Organ.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* @brief Represents the Brain organ.
|
||||
* @brief Represents a specific region of the brain.
|
||||
*/
|
||||
struct BrainRegion {
|
||||
std::string name;
|
||||
double activityLevel; // 0.0 to 1.0
|
||||
double bloodFlow_ml_100g_min;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents the Brain organ with a more detailed physiological model.
|
||||
*/
|
||||
class MEDICAL_LIB_API Brain : public Organ {
|
||||
public:
|
||||
@@ -14,7 +27,7 @@ public:
|
||||
Brain(int id);
|
||||
|
||||
/**
|
||||
* @brief Updates the brain's state over time.
|
||||
* @brief Updates the brain's state over a time interval.
|
||||
* @param deltaTime_s The time elapsed in seconds.
|
||||
*/
|
||||
void update(double deltaTime_s) override;
|
||||
@@ -25,11 +38,43 @@ public:
|
||||
*/
|
||||
std::string getSummary() const override;
|
||||
|
||||
// Specific getters for Brain properties
|
||||
double getConsciousnessLevel() const; // A simplified scale, e.g., 0.0 to 1.0
|
||||
double getCerebralBloodFlow() const; // in ml/100g/min
|
||||
// --- Getters for Key Neurological Vitals ---
|
||||
|
||||
/** @brief Gets the Glasgow Coma Scale score (simplified). */
|
||||
int getGCS() const;
|
||||
|
||||
/** @brief Gets the intracranial pressure in mmHg. */
|
||||
double getIntracranialPressure() const;
|
||||
|
||||
/** @brief Gets the cerebral perfusion pressure in mmHg. */
|
||||
double getCerebralPerfusionPressure() const;
|
||||
|
||||
/** @brief Gets the data for a simplified EEG waveform. */
|
||||
const std::deque<double>& getEegWaveform() const;
|
||||
|
||||
private:
|
||||
double consciousnessLevel;
|
||||
double cerebralBloodFlow;
|
||||
// --- Private Helper Methods ---
|
||||
void updateActivity(double deltaTime_s);
|
||||
void updatePressures(double meanArterialPressure);
|
||||
double generateEegValue();
|
||||
|
||||
// --- Physiological Parameters ---
|
||||
int gcsScore; ///< Glasgow Coma Scale (3-15)
|
||||
double intracranialPressure_mmHg; ///< ICP
|
||||
double cerebralPerfusionPressure_mmHg; ///< CPP
|
||||
double meanArterialPressure_mmHg; ///< MAP (placeholder, needs to be linked to Heart)
|
||||
|
||||
// --- Simulation State ---
|
||||
double totalTime_s;
|
||||
|
||||
// --- Anatomical Components ---
|
||||
BrainRegion frontalLobe;
|
||||
BrainRegion temporalLobe;
|
||||
BrainRegion parietalLobe;
|
||||
BrainRegion occipitalLobe;
|
||||
BrainRegion cerebellum;
|
||||
|
||||
// --- Waveform Data ---
|
||||
std::deque<double> eegData;
|
||||
size_t eegHistorySize;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "Organ.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
/**
|
||||
* @brief Represents the Lungs organ.
|
||||
* @brief Enum for the current state of the respiratory cycle.
|
||||
*/
|
||||
enum class RespiratoryState {
|
||||
INSPIRATION,
|
||||
EXPIRATION,
|
||||
PAUSE
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a single lobe of the lung.
|
||||
*/
|
||||
struct Lobe {
|
||||
std::string name;
|
||||
double volume_mL;
|
||||
double compliance; // How easily it inflates
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a major airway.
|
||||
*/
|
||||
struct Bronchus {
|
||||
std::string name;
|
||||
double resistance; // Airflow resistance
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents the Lungs organ with a more detailed physiological model.
|
||||
*/
|
||||
class MEDICAL_LIB_API Lungs : public Organ {
|
||||
public:
|
||||
@@ -14,7 +43,7 @@ public:
|
||||
Lungs(int id);
|
||||
|
||||
/**
|
||||
* @brief Updates the lungs' state over time.
|
||||
* @brief Updates the lungs' state over a time interval.
|
||||
* @param deltaTime_s The time elapsed in seconds.
|
||||
*/
|
||||
void update(double deltaTime_s) override;
|
||||
@@ -25,11 +54,54 @@ public:
|
||||
*/
|
||||
std::string getSummary() const override;
|
||||
|
||||
// Specific getters for Lungs properties
|
||||
// --- Getters for Key Respiratory Vitals ---
|
||||
|
||||
/** @brief Gets the current respiration rate in breaths per minute. */
|
||||
double getRespirationRate() const;
|
||||
|
||||
/** @brief Gets the current oxygen saturation (SpO2) as a percentage. */
|
||||
double getOxygenSaturation() const;
|
||||
|
||||
/** @brief Gets the volume of air in a normal breath (tidal volume) in mL. */
|
||||
double getTidalVolume() const;
|
||||
|
||||
/** @brief Gets the end-tidal CO2 (etCO2) value in mmHg. */
|
||||
double getEndTidalCO2() const;
|
||||
|
||||
/** @brief Gets the peak airway pressure during inspiration in cmH2O. */
|
||||
double getPeakInspiratoryPressure() const;
|
||||
|
||||
/** @brief Gets the data for the capnography waveform (etCO2 over time). */
|
||||
const std::deque<double>& getCapnographyWaveform() const;
|
||||
|
||||
private:
|
||||
double respirationRate;
|
||||
double oxygenSaturation;
|
||||
// --- Private Helper Methods ---
|
||||
void updateRespiratoryMechanics(double deltaTime_s);
|
||||
void updateGasLevels(double deltaTime_s);
|
||||
double generateCapnographyValue();
|
||||
|
||||
// --- Physiological Parameters ---
|
||||
double respirationRate; ///< Breaths per minute
|
||||
double oxygenSaturation; ///< SpO2 percentage
|
||||
double tidalVolume_mL; ///< Volume of air per breath
|
||||
double endTidalCO2_mmHg; ///< End-tidal CO2
|
||||
double peakInspiratoryPressure_cmH2O; ///< Peak airway pressure
|
||||
double totalLungCapacity_mL; ///< Total lung capacity
|
||||
|
||||
// --- Simulation State ---
|
||||
RespiratoryState currentState;
|
||||
double cyclePosition_s; ///< Current position in the respiratory cycle (seconds)
|
||||
double totalTime_s; ///< Total simulation time
|
||||
|
||||
// --- Anatomical Components ---
|
||||
Lobe rightUpperLobe;
|
||||
Lobe rightMiddleLobe;
|
||||
Lobe rightLowerLobe;
|
||||
Lobe leftUpperLobe;
|
||||
Lobe leftLowerLobe;
|
||||
Bronchus mainBronchus;
|
||||
|
||||
// --- Waveform Data ---
|
||||
std::deque<double> capnographyData;
|
||||
size_t capnographyHistorySize;
|
||||
};
|
||||
|
||||
+71
-16
@@ -2,8 +2,9 @@
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
// Helper function for random fluctuations
|
||||
// Helper for random fluctuations
|
||||
static double getFluctuation(double stddev) {
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
@@ -11,29 +12,83 @@ static double getFluctuation(double stddev) {
|
||||
return d(gen);
|
||||
}
|
||||
|
||||
Brain::Brain(int id) : Organ(id, "Brain"), consciousnessLevel(1.0), cerebralBloodFlow(50.0) {}
|
||||
Brain::Brain(int id)
|
||||
: Organ(id, "Brain"),
|
||||
gcsScore(15),
|
||||
intracranialPressure_mmHg(10.0),
|
||||
cerebralPerfusionPressure_mmHg(80.0),
|
||||
meanArterialPressure_mmHg(90.0), // Placeholder value
|
||||
totalTime_s(0.0),
|
||||
eegHistorySize(200) {
|
||||
|
||||
// Initialize Brain Regions
|
||||
frontalLobe = {"Frontal Lobe", 0.8, 50.0};
|
||||
temporalLobe = {"Temporal Lobe", 0.7, 50.0};
|
||||
parietalLobe = {"Parietal Lobe", 0.7, 50.0};
|
||||
occipitalLobe = {"Occipital Lobe", 0.8, 55.0};
|
||||
cerebellum = {"Cerebellum", 0.6, 60.0};
|
||||
}
|
||||
|
||||
void Brain::update(double deltaTime_s) {
|
||||
const double baseline_consciousness = 1.0;
|
||||
const double baseline_cbf = 50.0;
|
||||
const double theta = 0.05; // Slower reversion for brain metrics
|
||||
const double consciousness_stddev = 0.001;
|
||||
const double cbf_stddev = 0.5;
|
||||
totalTime_s += deltaTime_s;
|
||||
|
||||
consciousnessLevel += theta * (baseline_consciousness - consciousnessLevel) * deltaTime_s + getFluctuation(consciousness_stddev * deltaTime_s);
|
||||
cerebralBloodFlow += theta * (baseline_cbf - cerebralBloodFlow) * deltaTime_s + getFluctuation(cbf_stddev * deltaTime_s);
|
||||
// In a real scenario, MAP would be provided by the circulatory system (Heart)
|
||||
// For now, we'll just keep it stable with minor fluctuations.
|
||||
meanArterialPressure_mmHg += getFluctuation(0.1);
|
||||
meanArterialPressure_mmHg = std::clamp(meanArterialPressure_mmHg, 85.0, 95.0);
|
||||
|
||||
consciousnessLevel = std::clamp(consciousnessLevel, 0.0, 1.0);
|
||||
cerebralBloodFlow = std::clamp(cerebralBloodFlow, 40.0, 60.0);
|
||||
updateActivity(deltaTime_s);
|
||||
updatePressures(meanArterialPressure_mmHg);
|
||||
|
||||
// Update EEG data
|
||||
eegData.push_front(generateEegValue());
|
||||
if (eegData.size() > eegHistorySize) {
|
||||
eegData.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void Brain::updateActivity(double deltaTime_s) {
|
||||
// Simulate minor fluctuations in brain activity
|
||||
frontalLobe.activityLevel += getFluctuation(0.005);
|
||||
frontalLobe.activityLevel = std::clamp(frontalLobe.activityLevel, 0.7, 0.9);
|
||||
|
||||
// GCS is a clinical score, doesn't typically change second-to-second.
|
||||
// This is a placeholder for future, more complex pathology simulation.
|
||||
gcsScore = 15;
|
||||
}
|
||||
|
||||
void Brain::updatePressures(double meanArterialPressure) {
|
||||
// Autoregulation: Brain tries to maintain constant CPP
|
||||
// Simplified model: ICP drifts slowly
|
||||
intracranialPressure_mmHg += getFluctuation(0.01);
|
||||
intracranialPressure_mmHg = std::clamp(intracranialPressure_mmHg, 8.0, 12.0);
|
||||
|
||||
cerebralPerfusionPressure_mmHg = meanArterialPressure - intracranialPressure_mmHg;
|
||||
cerebralPerfusionPressure_mmHg = std::max(0.0, cerebralPerfusionPressure_mmHg);
|
||||
}
|
||||
|
||||
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
|
||||
double beta_wave = 0.3 * sin(2 * M_PI * 20 * totalTime_s); // 20 Hz
|
||||
double noise = getFluctuation(0.1);
|
||||
return (alpha_wave + beta_wave + noise) * 20; // Scaled to microvolts
|
||||
}
|
||||
|
||||
std::string Brain::getSummary() const {
|
||||
std::stringstream ss;
|
||||
ss << "Type: " << organType << " (ID: " << organId << ")\n"
|
||||
<< " Consciousness Level: " << consciousnessLevel * 100 << "%\n"
|
||||
<< " Cerebral Blood Flow: " << cerebralBloodFlow << " ml/100g/min";
|
||||
ss.precision(1);
|
||||
ss << std::fixed;
|
||||
ss << "--- Brain Summary ---\n"
|
||||
<< "Glasgow Coma Scale (GCS): " << getGCS() << "\n"
|
||||
<< "Intracranial Pressure (ICP): " << getIntracranialPressure() << " mmHg\n"
|
||||
<< "Mean Arterial Pressure (MAP): " << meanArterialPressure_mmHg << " mmHg\n"
|
||||
<< "Cerebral Perfusion (CPP): " << getCerebralPerfusionPressure() << " mmHg\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
double Brain::getConsciousnessLevel() const { return consciousnessLevel; }
|
||||
double Brain::getCerebralBloodFlow() const { return cerebralBloodFlow; }
|
||||
// --- Getters Implementation ---
|
||||
int Brain::getGCS() const { return gcsScore; }
|
||||
double Brain::getIntracranialPressure() const { return intracranialPressure_mmHg; }
|
||||
double Brain::getCerebralPerfusionPressure() const { return cerebralPerfusionPressure_mmHg; }
|
||||
const std::deque<double>& Brain::getEegWaveform() const { return eegData; }
|
||||
|
||||
+120
-13
@@ -2,8 +2,10 @@
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
|
||||
// Helper function for random fluctuations
|
||||
// Helper for random fluctuations
|
||||
static double getFluctuation(double stddev) {
|
||||
static std::random_device rd;
|
||||
static std::mt19937 gen(rd());
|
||||
@@ -11,29 +13,134 @@ static double getFluctuation(double stddev) {
|
||||
return d(gen);
|
||||
}
|
||||
|
||||
Lungs::Lungs(int id) : Organ(id, "Lungs"), respirationRate(16.0), oxygenSaturation(98.0) {}
|
||||
Lungs::Lungs(int id)
|
||||
: Organ(id, "Lungs"),
|
||||
respirationRate(16.0),
|
||||
oxygenSaturation(98.0),
|
||||
tidalVolume_mL(500.0),
|
||||
endTidalCO2_mmHg(40.0),
|
||||
peakInspiratoryPressure_cmH2O(0.0),
|
||||
totalLungCapacity_mL(6000.0),
|
||||
currentState(RespiratoryState::PAUSE),
|
||||
cyclePosition_s(0.0),
|
||||
totalTime_s(0.0),
|
||||
capnographyHistorySize(200) {
|
||||
|
||||
// Initialize Lobes
|
||||
rightUpperLobe = {"Right Upper Lobe", 0, 0.1};
|
||||
rightMiddleLobe = {"Right Middle Lobe", 0, 0.07};
|
||||
rightLowerLobe = {"Right Lower Lobe", 0, 0.13};
|
||||
leftUpperLobe = {"Left Upper Lobe", 0, 0.1};
|
||||
leftLowerLobe = {"Left Lower Lobe", 0, 0.1};
|
||||
|
||||
// Initialize Bronchi
|
||||
mainBronchus = {"Main Bronchus", 0.8};
|
||||
}
|
||||
|
||||
void Lungs::update(double deltaTime_s) {
|
||||
const double baseline_rr = 16.0;
|
||||
const double baseline_spo2 = 98.0;
|
||||
const double theta = 0.1; // Mean reversion speed
|
||||
const double rr_stddev = 0.05;
|
||||
const double spo2_stddev = 0.02;
|
||||
totalTime_s += deltaTime_s;
|
||||
|
||||
respirationRate += theta * (baseline_rr - respirationRate) * deltaTime_s + getFluctuation(rr_stddev * deltaTime_s);
|
||||
oxygenSaturation += theta * (baseline_spo2 - oxygenSaturation) * deltaTime_s + getFluctuation(spo2_stddev * deltaTime_s);
|
||||
updateRespiratoryMechanics(deltaTime_s);
|
||||
updateGasLevels(deltaTime_s);
|
||||
|
||||
// Update capnography data
|
||||
capnographyData.push_front(generateCapnographyValue());
|
||||
if (capnographyData.size() > capnographyHistorySize) {
|
||||
capnographyData.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void Lungs::updateRespiratoryMechanics(double deltaTime_s) {
|
||||
double cycleDuration_s = 60.0 / respirationRate;
|
||||
cyclePosition_s += deltaTime_s;
|
||||
|
||||
// State transitions
|
||||
double inspirationDuration = cycleDuration_s * 0.4; // I:E ratio of 1:1.5
|
||||
double expirationDuration = cycleDuration_s * 0.6;
|
||||
|
||||
if (cyclePosition_s <= inspirationDuration) {
|
||||
currentState = RespiratoryState::INSPIRATION;
|
||||
} else if (cyclePosition_s <= cycleDuration_s) {
|
||||
currentState = RespiratoryState::EXPIRATION;
|
||||
} else {
|
||||
cyclePosition_s -= cycleDuration_s;
|
||||
currentState = RespiratoryState::INSPIRATION;
|
||||
}
|
||||
|
||||
// Pressure and Volume dynamics
|
||||
double flowRate_mL_s = 0;
|
||||
if (currentState == RespiratoryState::INSPIRATION) {
|
||||
// Simple sine wave for pressure generation
|
||||
double pressure_wave = sin(M_PI * (cyclePosition_s / inspirationDuration));
|
||||
peakInspiratoryPressure_cmH2O = 15.0 * pressure_wave; // 15 cmH2O peak
|
||||
flowRate_mL_s = (peakInspiratoryPressure_cmH2O / mainBronchus.resistance) * 100;
|
||||
tidalVolume_mL += flowRate_mL_s * deltaTime_s;
|
||||
} else { // EXPIRATION
|
||||
peakInspiratoryPressure_cmH2O = 0;
|
||||
// Passive recoil drives expiration
|
||||
double recoilPressure = (tidalVolume_mL / 500.0) * 5.0; // Simplified
|
||||
flowRate_mL_s = -(recoilPressure / mainBronchus.resistance) * 100;
|
||||
tidalVolume_mL += flowRate_mL_s * deltaTime_s;
|
||||
}
|
||||
|
||||
// Clamp tidal volume
|
||||
tidalVolume_mL = std::clamp(tidalVolume_mL, 0.0, totalLungCapacity_mL / 2.0);
|
||||
}
|
||||
|
||||
void Lungs::updateGasLevels(double deltaTime_s) {
|
||||
// Fluctuate base vitals slightly
|
||||
respirationRate += getFluctuation(0.01);
|
||||
respirationRate = std::clamp(respirationRate, 12.0, 20.0);
|
||||
oxygenSaturation = std::clamp(oxygenSaturation, 96.0, 100.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);
|
||||
oxygenSaturation += 0.1 * (targetSpo2 - oxygenSaturation) * deltaTime_s + getFluctuation(0.02);
|
||||
oxygenSaturation = std::clamp(oxygenSaturation, 94.0, 100.0);
|
||||
|
||||
// etCO2 is inversely related to ventilation
|
||||
double targetEtCO2 = 40.0 / std::clamp(ventilationFactor, 0.8, 1.2);
|
||||
endTidalCO2_mmHg += 0.2 * (targetEtCO2 - endTidalCO2_mmHg) * deltaTime_s + getFluctuation(0.05);
|
||||
endTidalCO2_mmHg = std::clamp(endTidalCO2_mmHg, 35.0, 50.0);
|
||||
}
|
||||
|
||||
double Lungs::generateCapnographyValue() {
|
||||
double cycleDuration_s = 60.0 / respirationRate;
|
||||
double timeInCycle = fmod(cyclePosition_s, cycleDuration_s);
|
||||
double inspirationEnd = cycleDuration_s * 0.4;
|
||||
double plateauStart = cycleDuration_s * 0.5;
|
||||
double plateauEnd = cycleDuration_s * 0.8;
|
||||
|
||||
if (currentState == RespiratoryState::INSPIRATION) {
|
||||
return 0.0; // Phase I: Inspiratory baseline
|
||||
} else { // EXPIRATION
|
||||
if (timeInCycle < plateauStart) { // Phase II: Expiratory upstroke
|
||||
return endTidalCO2_mmHg * ((timeInCycle - inspirationEnd) / (plateauStart - inspirationEnd));
|
||||
} else if (timeInCycle < plateauEnd) { // Phase III: Alveolar plateau
|
||||
return endTidalCO2_mmHg + getFluctuation(0.1);
|
||||
} else { // Phase IV: Inspiratory downstroke (handled by next cycle's baseline)
|
||||
return endTidalCO2_mmHg * (1.0 - (timeInCycle - plateauEnd) / (cycleDuration_s - plateauEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Lungs::getSummary() const {
|
||||
std::stringstream ss;
|
||||
ss << "Type: " << organType << " (ID: " << organId << ")\n"
|
||||
<< " Respiration Rate: " << respirationRate << " breaths/min\n"
|
||||
<< " Oxygen Saturation: " << oxygenSaturation << " %";
|
||||
ss.precision(1);
|
||||
ss << std::fixed;
|
||||
ss << "--- Lungs Summary ---\n"
|
||||
<< "Respiration Rate: " << getRespirationRate() << " breaths/min\n"
|
||||
<< "Oxygen Saturation (SpO2): " << getOxygenSaturation() << " %\n"
|
||||
<< "Tidal Volume: " << getTidalVolume() << " mL\n"
|
||||
<< "End-Tidal CO2 (etCO2): " << getEndTidalCO2() << " mmHg\n"
|
||||
<< "Peak Airway Pressure: " << getPeakInspiratoryPressure() << " cmH2O\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// --- Getters Implementation ---
|
||||
double Lungs::getRespirationRate() const { return respirationRate; }
|
||||
double Lungs::getOxygenSaturation() const { return oxygenSaturation; }
|
||||
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; }
|
||||
|
||||
Reference in New Issue
Block a user