From e5e3c11754962ef49a83683dc7d5a33b3ede2462 Mon Sep 17 00:00:00 2001 From: Austin Date: Wed, 7 Mar 2018 15:15:17 -0500 Subject: [PATCH] source additions for new 6.3 release/support --- src/build.properties | 2 +- src/build.xml | 15 +- .../tissue/SETissueCompartment.cpp | 25 + .../compartment/tissue/SETissueCompartment.h | 5 + src/cdm/cpp/engine/PhysiologyEngineTrack.cpp | 8 +- src/cdm/cpp/patient/SEPatient.cpp | 34 +- src/cdm/cpp/patient/SEPatient.h | 5 + src/cdm/cpp/patient/actions/SEHemorrhage.cpp | 213 ++- src/cdm/cpp/patient/actions/SEHemorrhage.h | 19 +- .../actions/SESubstanceCompoundInfusion.cpp | 13 + .../cpp/patient/conditions/SEDehydration.cpp | 80 ++ .../cpp/patient/conditions/SEDehydration.h | 43 + .../patient/conditions/SEDiabetesType1.cpp | 80 ++ .../cpp/patient/conditions/SEDiabetesType1.h | 43 + .../patient/conditions/SEDiabetesType2.cpp | 98 ++ .../cpp/patient/conditions/SEDiabetesType2.h | 46 + .../cpp/patient/conditions/SEStarvation.cpp | 80 ++ src/cdm/cpp/patient/conditions/SEStarvation.h | 44 + src/cdm/cpp/scenario/SECondition.cpp | 39 +- src/cdm/cpp/scenario/SEConditionManager.cpp | 152 ++- src/cdm/cpp/scenario/SEConditionManager.h | 18 +- .../scenario/SEPatientActionCollection.cpp | 32 +- src/cdm/cpp/substance/SESubstance.cpp | 50 + src/cdm/cpp/substance/SESubstance.h | 12 + src/cdm/cpp/substance/SESubstanceManager.cpp | 8 +- .../physiology/SEBloodChemistrySystem.cpp | 25 + .../physiology/SEBloodChemistrySystem.h | 11 + .../cpp/system/physiology/SEEnergySystem.cpp | 103 +- .../cpp/system/physiology/SEEnergySystem.h | 20 +- .../cpp/system/physiology/SEHepaticSystem.cpp | 58 +- .../cpp/system/physiology/SEHepaticSystem.h | 15 +- .../cpp/system/physiology/SETissueSystem.cpp | 54 + .../cpp/system/physiology/SETissueSystem.h | 16 +- src/cdm/cpp/utils/GeneralMath.cpp | 32 +- src/cdm/cpp/utils/GeneralMath.h | 1 + .../compartment/SETissueCompartment.java | 20 + .../datamodel/dataset/DataSetReader.java | 10 + .../datamodel/patient/SEPatient.java | 15 + .../patient/actions/SEHemorrhage.java | 157 ++- .../patient/conditions/Dehydration.java | 86 ++ .../patient/conditions/DiabetesType1.java | 86 ++ .../patient/conditions/DiabetesType2.java | 110 ++ .../patient/conditions/Starvation.java | 84 ++ .../datamodel/properties/CommonUnits.java | 45 + .../SEScalarElectricResistance.java | 99 ++ .../datamodel/substance/SESubstance.java | 26 + .../physiology/SEBloodChemistrySystem.java | 22 + .../system/physiology/SEEnergySystem.java | 122 +- .../system/physiology/SEHepaticSystem.java | 42 +- .../system/physiology/SETissueSystem.java | 41 +- .../utilities/csv/CSVComparison.java | 1 + src/engine/cpp/Controller/BioGears.cpp | 51 +- .../cpp/Controller/BioGearsConfiguration.cpp | 6 +- .../cpp/Controller/BioGearsSubstances.cpp | 176 ++- .../cpp/Controller/BioGearsSubstances.h | 1 + src/engine/cpp/Systems/BloodChemistry.cpp | 78 +- src/engine/cpp/Systems/Cardiovascular.cpp | 18 +- src/engine/cpp/Systems/Drugs.cpp | 263 +++- src/engine/cpp/Systems/Drugs.h | 6 + src/engine/cpp/Systems/Endocrine.cpp | 90 +- src/engine/cpp/Systems/Energy.cpp | 143 +- src/engine/cpp/Systems/Energy.h | 15 +- src/engine/cpp/Systems/Environment.cpp | 7 +- src/engine/cpp/Systems/Gastrointestinal.cpp | 194 +-- src/engine/cpp/Systems/Gastrointestinal.h | 2 +- src/engine/cpp/Systems/Hepatic.cpp | 74 +- src/engine/cpp/Systems/Hepatic.h | 2 +- src/engine/cpp/Systems/Nervous.cpp | 140 +- src/engine/cpp/Systems/Nervous.h | 6 +- src/engine/cpp/Systems/Renal.cpp | 42 +- src/engine/cpp/Systems/Respiratory.cpp | 14 +- src/engine/cpp/Systems/Tissue.cpp | 843 ++++++++---- src/engine/cpp/Systems/Tissue.h | 34 +- src/engine/test/cpp/BioGearsEngineTest.cpp | 1 + src/engine/test/cpp/BioGearsEngineTest.h | 7 +- src/engine/test/cpp/Circuits/TissueTests.cpp | 290 ++++- .../test/cpp/Engine/FourCompartmentTest.cpp | 249 +++- .../test/cpp/Engine/NutrientKineticsTest.cpp | 12 +- src/gui/.classpath | 10 + src/gui/.project | 17 + src/gui/build.xml | 37 + .../physiology/biogears/gui/BioGearsGUI.java | 39 + .../physiology/biogears/gui/ExitListener.java | 21 + .../physiology/biogears/gui/GUIContext.java | 119 ++ .../physiology/biogears/gui/MainMenu.java | 145 +++ .../physiology/biogears/gui/MainPanel.java | 41 + .../physiology/biogears/gui/ToolBar.java | 51 + .../biogears/gui/ToolBarAction.java | 42 + .../biogears/gui/WindowUtilities.java | 108 ++ .../biogears/gui/controls/ConsoleCtrl.java | 49 + .../gui/controls/ConsoleListener.java | 52 + .../biogears/gui/controls/ScalarCtrl.java | 95 ++ .../CompartmentRequestSelection.java | 468 +++++++ .../gui/datarequests/DataRequestPanel.java | 270 ++++ .../gui/datarequests/DataRequestRow.java | 51 + .../datarequests/DataRequestSelection.java | 121 ++ .../gui/datarequests/DataRequestWindow.java | 164 +++ .../EquipmentRequestSelection.java | 49 + .../SubstanceRequestSelection.java | 173 +++ .../datarequests/SystemRequestSelection.java | 64 + .../scenario/ConsciousRespirationEditor.java | 67 + .../scenario/ConsciousRespirationPanel.java | 332 +++++ .../gui/scenario/DynamicPropertyEditor.java | 68 + .../gui/scenario/DynamicPropertyPanel.java | 524 ++++++++ .../biogears/gui/scenario/ResultsWindow.java | 330 +++++ .../gui/scenario/ScenarioCalcCallback.java | 64 + .../gui/scenario/ScenarioCalcDialog.java | 61 + .../gui/scenario/ScenarioCalcDisplay.java | 354 +++++ .../gui/scenario/ScenarioCalcPanel.java | 65 + .../gui/scenario/ScenarioCalcThread.java | 76 ++ .../biogears/gui/scenario/ScenarioEditor.java | 923 +++++++++++++ .../xsd/biogears/BioGearsConfiguration.xsd | 1 - .../xsd/biogears/BioGearsPhysiology.xsd | 16 +- src/schema/xsd/cdm/Compartment.xsd | 1 + src/schema/xsd/cdm/Patient.xsd | 10 + src/schema/xsd/cdm/PatientActions.xsd | 5 +- src/schema/xsd/cdm/PatientConditions.xsd | 61 +- src/schema/xsd/cdm/PatientNutrition.xsd | 12 - src/schema/xsd/cdm/Physiology.xsd | 16 +- src/schema/xsd/cdm/Substance.xsd | 14 +- src/sdk/howto/cpp/BioGearsEngineHowTo.cpp | 42 +- src/sdk/howto/cpp/BioGearsEngineHowTo.h | 4 + src/sdk/howto/cpp/HowTo-BolusDrug.cpp | 5 +- src/sdk/howto/cpp/HowTo-Fasciculation.cpp | 106 ++ src/sdk/howto/cpp/HowTo-Hemorrhage.cpp | 113 +- src/sdk/howto/cpp/HowTo-InfusionDrug.cpp | 113 ++ src/sdk/howto/cpp/HowTo-SarinExposure.cpp | 183 +++ src/sdk/howto/cpp/HowTo-ThreadedBioGears.cpp | 35 +- src/sdk/howto/cpp/HowTo-ThreadedBioGears.h | 2 +- .../cpp/HowTo-VasopressinShockTherapy.cpp | 152 +++ src/test/main.cpp | 5 +- src/utils/PKHuisinga/GenericPBPKmodel.m | 211 +++ .../GenericPBPKmodel_drugDatabase.m | 1150 +++++++++++++++++ .../GenericPBPKmodel_graphicalOutput.m | 240 ++++ .../GenericPBPKmodel_graphicalOutputLumping.m | 76 ++ .../GenericPBPKmodel_mechanisticLumping.m | 191 +++ .../PKHuisinga/GenericPBPKmodel_physiology.m | 863 +++++++++++++ .../GenericPBPKmodel_randomEffects.m | 167 +++ .../GenericPBPKmodel_referenceIndividual.m | 324 +++++ .../GenericPBPKmodel_simulatePBPKmodel.m | 289 +++++ .../GenericPBPKmodel_virtualPopulation.m | 290 +++++ src/utils/PKHuisinga/README.TXT | 208 +++ src/utils/PKHuisinga/fett.m | 19 + src/utils/PKHuisinga/printNicely.m | 10 + src/utils/PlasmaConcentrationError.m | 128 ++ src/utils/UNC PK Analysis/PKDataShift.m | 22 + .../UNC PK Analysis/PK_Validation_Script.m | 365 ++++++ src/utils/UNC PK Analysis/bars.m | 65 + src/utils/UNC PK Analysis/createFit.m | 59 + .../discrete_differentiation.m | 33 + src/utils/UNC PK Analysis/doxyTable.m | 127 ++ src/utils/UNC PK Analysis/lowerbound.m | 37 + src/utils/UNC PK Analysis/smoothBG.m | 38 + src/utils/UNC PK Analysis/smoothing.m | 22 + src/utils/UNC PK Analysis/stats.m | 68 + src/utils/UNC PK Analysis/transition_bounds.m | 148 +++ 156 files changed, 15054 insertions(+), 1271 deletions(-) create mode 100644 src/cdm/cpp/patient/conditions/SEDehydration.cpp create mode 100644 src/cdm/cpp/patient/conditions/SEDehydration.h create mode 100644 src/cdm/cpp/patient/conditions/SEDiabetesType1.cpp create mode 100644 src/cdm/cpp/patient/conditions/SEDiabetesType1.h create mode 100644 src/cdm/cpp/patient/conditions/SEDiabetesType2.cpp create mode 100644 src/cdm/cpp/patient/conditions/SEDiabetesType2.h create mode 100644 src/cdm/cpp/patient/conditions/SEStarvation.cpp create mode 100644 src/cdm/cpp/patient/conditions/SEStarvation.h create mode 100644 src/cdm/java/mil/tatrc/physiology/datamodel/patient/conditions/Dehydration.java create mode 100644 src/cdm/java/mil/tatrc/physiology/datamodel/patient/conditions/DiabetesType1.java create mode 100644 src/cdm/java/mil/tatrc/physiology/datamodel/patient/conditions/DiabetesType2.java create mode 100644 src/cdm/java/mil/tatrc/physiology/datamodel/patient/conditions/Starvation.java create mode 100644 src/cdm/java/mil/tatrc/physiology/datamodel/properties/SEScalarElectricResistance.java create mode 100644 src/gui/.classpath create mode 100644 src/gui/.project create mode 100644 src/gui/build.xml create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/BioGearsGUI.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/ExitListener.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/GUIContext.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/MainMenu.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/MainPanel.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/ToolBar.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/ToolBarAction.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/WindowUtilities.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ConsoleCtrl.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ConsoleListener.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ScalarCtrl.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/CompartmentRequestSelection.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestPanel.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestRow.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestSelection.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestWindow.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/EquipmentRequestSelection.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/SubstanceRequestSelection.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/SystemRequestSelection.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ConsciousRespirationEditor.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ConsciousRespirationPanel.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/DynamicPropertyEditor.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/DynamicPropertyPanel.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ResultsWindow.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcCallback.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcDialog.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcDisplay.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcPanel.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcThread.java create mode 100644 src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioEditor.java create mode 100644 src/sdk/howto/cpp/HowTo-Fasciculation.cpp create mode 100644 src/sdk/howto/cpp/HowTo-InfusionDrug.cpp create mode 100644 src/sdk/howto/cpp/HowTo-SarinExposure.cpp create mode 100644 src/sdk/howto/cpp/HowTo-VasopressinShockTherapy.cpp create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_drugDatabase.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_graphicalOutput.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_graphicalOutputLumping.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_mechanisticLumping.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_physiology.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_randomEffects.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_referenceIndividual.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_simulatePBPKmodel.m create mode 100644 src/utils/PKHuisinga/GenericPBPKmodel_virtualPopulation.m create mode 100644 src/utils/PKHuisinga/README.TXT create mode 100644 src/utils/PKHuisinga/fett.m create mode 100644 src/utils/PKHuisinga/printNicely.m create mode 100644 src/utils/PlasmaConcentrationError.m create mode 100644 src/utils/UNC PK Analysis/PKDataShift.m create mode 100644 src/utils/UNC PK Analysis/PK_Validation_Script.m create mode 100644 src/utils/UNC PK Analysis/bars.m create mode 100644 src/utils/UNC PK Analysis/createFit.m create mode 100644 src/utils/UNC PK Analysis/discrete_differentiation.m create mode 100644 src/utils/UNC PK Analysis/doxyTable.m create mode 100644 src/utils/UNC PK Analysis/lowerbound.m create mode 100644 src/utils/UNC PK Analysis/smoothBG.m create mode 100644 src/utils/UNC PK Analysis/smoothing.m create mode 100644 src/utils/UNC PK Analysis/stats.m create mode 100644 src/utils/UNC PK Analysis/transition_bounds.m diff --git a/src/build.properties b/src/build.properties index 6af01af..4405919 100644 --- a/src/build.properties +++ b/src/build.properties @@ -1,4 +1,4 @@ -biogears.version=BioGears_6.1.1-beta +biogears.version=BioGears_6.3.0-beta biogears.home=${basedir}/.. biogears.bin=${biogears.home}/bin biogears.config=release diff --git a/src/build.xml b/src/build.xml index e035a07..19a50aa 100644 --- a/src/build.xml +++ b/src/build.xml @@ -314,10 +314,13 @@ + + + @@ -626,6 +629,16 @@ + + + + + + + + + + @@ -959,7 +972,7 @@ - + diff --git a/src/cdm/cpp/compartment/tissue/SETissueCompartment.cpp b/src/cdm/cpp/compartment/tissue/SETissueCompartment.cpp index 456d865..b4715c0 100644 --- a/src/cdm/cpp/compartment/tissue/SETissueCompartment.cpp +++ b/src/cdm/cpp/compartment/tissue/SETissueCompartment.cpp @@ -27,6 +27,7 @@ SETissueCompartment::SETissueCompartment(const std::string& name, Logger* logger { m_AcidicPhospohlipidConcentration = nullptr; m_MatrixVolume = nullptr; + m_MembranePotential = nullptr; m_NeutralLipidsVolumeFraction = nullptr; m_NeutralPhospholipidsVolumeFraction = nullptr; m_TissueToPlasmaAlbuminRatio = nullptr; @@ -45,6 +46,7 @@ void SETissueCompartment::Clear() SECompartment::Clear(); SAFE_DELETE(m_AcidicPhospohlipidConcentration); SAFE_DELETE(m_MatrixVolume); + SAFE_DELETE(m_MembranePotential); SAFE_DELETE(m_NeutralLipidsVolumeFraction); SAFE_DELETE(m_NeutralPhospholipidsVolumeFraction); SAFE_DELETE(m_TissueToPlasmaAlbuminRatio); @@ -61,6 +63,8 @@ bool SETissueCompartment::Load(const CDM::TissueCompartmentData& in, SESubstance GetAcidicPhospohlipidConcentration().Load(in.AcidicPhospohlipidConcentration().get()); if (in.MatrixVolume().present()) GetMatrixVolume().Load(in.MatrixVolume().get()); + if (in.MembranePotential().present()) + GetMembranePotential().Load(in.MembranePotential().get()); if (in.NeutralLipidsVolumeFraction().present()) GetNeutralLipidsVolumeFraction().Load(in.NeutralLipidsVolumeFraction().get()); if (in.NeutralPhospholipidsVolumeFraction().present()) @@ -89,6 +93,8 @@ void SETissueCompartment::Unload(CDM::TissueCompartmentData& data) data.AcidicPhospohlipidConcentration(std::unique_ptr(m_AcidicPhospohlipidConcentration->Unload())); if (HasMatrixVolume()) data.MatrixVolume(std::unique_ptr(m_MatrixVolume->Unload())); + if (HasMembranePotential()) + data.MembranePotential(std::unique_ptr(m_MembranePotential->Unload())); if (HasNeutralLipidsVolumeFraction()) data.NeutralLipidsVolumeFraction(std::unique_ptr(m_NeutralLipidsVolumeFraction->Unload())); if (HasNeutralPhospholipidsVolumeFraction()) @@ -111,6 +117,8 @@ const SEScalar* SETissueCompartment::GetScalar(const std::string& name) return &GetAcidicPhospohlipidConcentration(); if (name.compare("MatrixVolume") == 0) return &GetMatrixVolume(); + if (name.compare("MembranePotential") == 0) + return &GetMembranePotential(); if (name.compare("NeutralLipidsVolumeFraction") == 0) return &GetNeutralLipidsVolumeFraction(); if (name.compare("NeutralPhospholipidsVolumeFraction") == 0) @@ -165,6 +173,23 @@ double SETissueCompartment::GetMatrixVolume(const VolumeUnit& unit) const return m_MatrixVolume->GetValue(unit); } +bool SETissueCompartment::HasMembranePotential() const +{ + return m_MembranePotential == nullptr ? false : m_MembranePotential->IsValid(); +} +SEScalarElectricPotential& SETissueCompartment::GetMembranePotential() +{ + if (m_MembranePotential == nullptr) + m_MembranePotential = new SEScalarElectricPotential(); + return *m_MembranePotential; +} +double SETissueCompartment::GetMembranePotential(const ElectricPotentialUnit& unit) const +{ + if (m_MembranePotential == nullptr) + return SEScalar::dNaN(); + return m_MembranePotential->GetValue(unit); +} + bool SETissueCompartment::HasNeutralLipidsVolumeFraction() const { return m_NeutralLipidsVolumeFraction == nullptr ? false : m_NeutralLipidsVolumeFraction->IsValid(); diff --git a/src/cdm/cpp/compartment/tissue/SETissueCompartment.h b/src/cdm/cpp/compartment/tissue/SETissueCompartment.h index cc0f891..e5cf44d 100644 --- a/src/cdm/cpp/compartment/tissue/SETissueCompartment.h +++ b/src/cdm/cpp/compartment/tissue/SETissueCompartment.h @@ -48,6 +48,10 @@ class DLL_DECL SETissueCompartment : public SECompartment virtual SEScalarVolume& GetMatrixVolume(); virtual double GetMatrixVolume(const VolumeUnit& unit) const; + virtual bool HasMembranePotential() const; + virtual SEScalarElectricPotential& GetMembranePotential(); + virtual double GetMembranePotential(const ElectricPotentialUnit& unit) const; + virtual bool HasNeutralLipidsVolumeFraction() const; virtual SEScalarFraction& GetNeutralLipidsVolumeFraction(); virtual double GetNeutralLipidsVolumeFraction() const; @@ -76,6 +80,7 @@ class DLL_DECL SETissueCompartment : public SECompartment SEScalarMassPerMass* m_AcidicPhospohlipidConcentration; SEScalarVolume* m_MatrixVolume; + SEScalarElectricPotential* m_MembranePotential; SEScalarFraction* m_NeutralLipidsVolumeFraction; SEScalarFraction* m_NeutralPhospholipidsVolumeFraction; SEScalar* m_TissueToPlasmaAlbuminRatio; diff --git a/src/cdm/cpp/engine/PhysiologyEngineTrack.cpp b/src/cdm/cpp/engine/PhysiologyEngineTrack.cpp index 6e22c85..246c362 100644 --- a/src/cdm/cpp/engine/PhysiologyEngineTrack.cpp +++ b/src/cdm/cpp/engine/PhysiologyEngineTrack.cpp @@ -37,6 +37,7 @@ specific language governing permissions and limitations under the License. #include "system/physiology/SERenalSystem.h" #include "system/physiology/SERespiratorySystem.h" #include "system/physiology/SETissueSystem.h" +#include "system/physiology/SEHepaticSystem.h" #include "system/equipment/Anesthesia/SEAnesthesiaMachine.h" #include "system/equipment/ElectroCardioGram/SEElectroCardioGram.h" #include "system/equipment/Inhaler/SEInhaler.h" @@ -102,6 +103,9 @@ PhysiologyEngineTrack::PhysiologyEngineTrack(PhysiologyEngine& engine) : Loggabl SENervousSystem* nervous = (SENervousSystem*)engine.GetNervousSystem(); if (nervous != nullptr) m_PhysiologySystems.push_back(nervous); + SEHepaticSystem* hepatic = (SEHepaticSystem*)engine.GetHepaticSystem(); + if (hepatic != nullptr) + m_PhysiologySystems.push_back(hepatic); m_Environment = (SEEnvironment*)engine.GetEnvironment(); @@ -155,7 +159,7 @@ void PhysiologyEngineTrack::SetupRequests() { bool isOpen = m_ResultsStream.is_open(); if (!isOpen || m_ForceConnection) - {// Process/Hook up all requests with their associated scalers + {// Process/Hook up all requests with their associated scalars for (SEDataRequest* dr : m_DataRequestMgr.GetDataRequests()) { if (!TrackRequest(*dr)) @@ -190,7 +194,7 @@ void PhysiologyEngineTrack::PullData() if (ds == nullptr) { Error("You cannot modify CSV Results file data requests in the middle of a run."); - Error("Ignorning data request " + dr->GetName()); + Error("Ignoring data request " + dr->GetName()); continue; } if (!ds->HasScalar()) diff --git a/src/cdm/cpp/patient/SEPatient.cpp b/src/cdm/cpp/patient/SEPatient.cpp index ee21ec0..e784cd2 100644 --- a/src/cdm/cpp/patient/SEPatient.cpp +++ b/src/cdm/cpp/patient/SEPatient.cpp @@ -70,6 +70,7 @@ SEPatient::SEPatient(Logger* logger) : Loggable(logger) m_InspiratoryCapacity = nullptr; m_InspiratoryReserveVolume = nullptr; m_LeanBodyMass = nullptr; + m_MuscleMass = nullptr; m_MeanArterialPressureBaseline = nullptr; m_ResidualVolume = nullptr; m_RespirationRateBaseline = nullptr; @@ -128,6 +129,7 @@ void SEPatient::Clear() SAFE_DELETE(m_InspiratoryCapacity); SAFE_DELETE(m_InspiratoryReserveVolume); SAFE_DELETE(m_LeanBodyMass); + SAFE_DELETE(m_MuscleMass); SAFE_DELETE(m_MeanArterialPressureBaseline); SAFE_DELETE(m_ResidualVolume); SAFE_DELETE(m_RespirationRateBaseline); @@ -176,6 +178,8 @@ const SEScalar* SEPatient::GetScalar(const std::string& name) return &GetInspiratoryReserveVolume(); if (name.compare("LeanBodyMass") == 0) return &GetLeanBodyMass(); + if (name.compare("MuscleMass") == 0) + return &GetMuscleMass(); if (name.compare("MeanArterialPressureBaseline") == 0) return &GetMeanArterialPressureBaseline(); if (name.compare("ResidualVolume") == 0) @@ -240,6 +244,8 @@ bool SEPatient::Load(const CDM::PatientData& in) GetInspiratoryReserveVolume().Load(in.InspiratoryReserveVolume().get()); if (in.LeanBodyMass().present()) GetLeanBodyMass().Load(in.LeanBodyMass().get()); + if (in.MuscleMass().present()) + GetMuscleMass().Load(in.MuscleMass().get()); if(in.MeanArterialPressureBaseline().present()) GetMeanArterialPressureBaseline().Load(in.MeanArterialPressureBaseline().get()); if (in.ResidualVolume().present()) @@ -318,6 +324,8 @@ void SEPatient::Unload(CDM::PatientData& data) const data.InspiratoryReserveVolume(std::unique_ptr(m_InspiratoryReserveVolume->Unload())); if (m_LeanBodyMass != nullptr) data.LeanBodyMass(std::unique_ptr(m_LeanBodyMass->Unload())); + if (m_MuscleMass != nullptr) + data.MuscleMass(std::unique_ptr(m_MuscleMass->Unload())); if(m_MeanArterialPressureBaseline!=nullptr) data.MeanArterialPressureBaseline(std::unique_ptr(m_MeanArterialPressureBaseline->Unload())); if (m_ResidualVolume != nullptr) @@ -468,6 +476,9 @@ void SEPatient::SetEvent(CDM::enumPatientEvent::value type, bool active, const S case CDM::enumPatientEvent::ModerateAcuteRespiratoryDistress: m_ss << " The patient has Moderate Acute Respiratory Distress"; break; + case CDM::enumPatientEvent::MuscleCatabolism: + m_ss << " Patient has begun muscle catabolism"; + break; case CDM::enumPatientEvent::MuscleGlycogenDepleted: m_ss << " Patient's muscle glycogen is depleted"; break; @@ -496,7 +507,7 @@ void SEPatient::SetEvent(CDM::enumPatientEvent::value type, bool active, const S m_ss << " Patient has Tachypnea"; break; case CDM::enumPatientEvent::Fatigue: - m_ss << "Patient has fatigue"; + m_ss << " Patient has fatigue"; break; case CDM::enumPatientEvent::StartOfCardiacCycle: case CDM::enumPatientEvent::StartOfExhale: @@ -542,7 +553,7 @@ void SEPatient::SetEvent(CDM::enumPatientEvent::value type, bool active, const S m_ss << " Patient no longer has Diuresis"; break; case CDM::enumPatientEvent::Fasciculation: - m_ss << "Patient no longer has fasciculations"; + m_ss << "Patient no longer has Fasciculation"; break; case CDM::enumPatientEvent::FunctionalIncontinence: m_ss << " Patient has an empty bladder"; @@ -635,7 +646,7 @@ void SEPatient::SetEvent(CDM::enumPatientEvent::value type, bool active, const S m_ss << " Patient no longer has Tachypnea"; break; case CDM::enumPatientEvent::Fatigue: - m_ss << "Patient is no longer fatigued"; + m_ss << " Patient is no longer fatigued"; break; case CDM::enumPatientEvent::StartOfCardiacCycle: case CDM::enumPatientEvent::StartOfExhale: @@ -1005,6 +1016,23 @@ double SEPatient::GetLeanBodyMass(const MassUnit& unit) const return m_LeanBodyMass->GetValue(unit); } +bool SEPatient::HasMuscleMass() const +{ + return m_MuscleMass == nullptr ? false : m_MuscleMass->IsValid(); +} +SEScalarMass& SEPatient::GetMuscleMass() +{ + if (m_MuscleMass == nullptr) + m_MuscleMass = new SEScalarMass(); + return *m_MuscleMass; +} +double SEPatient::GetMuscleMass(const MassUnit& unit) const +{ + if (m_MuscleMass == nullptr) + return SEScalar::dNaN(); + return m_MuscleMass->GetValue(unit); +} + bool SEPatient::HasMeanArterialPressureBaseline() const { return m_MeanArterialPressureBaseline==nullptr?false:m_MeanArterialPressureBaseline->IsValid(); diff --git a/src/cdm/cpp/patient/SEPatient.h b/src/cdm/cpp/patient/SEPatient.h index 771c508..a6d6fa4 100644 --- a/src/cdm/cpp/patient/SEPatient.h +++ b/src/cdm/cpp/patient/SEPatient.h @@ -136,6 +136,10 @@ class DLL_DECL SEPatient : public Loggable virtual SEScalarMass& GetLeanBodyMass(); virtual double GetLeanBodyMass(const MassUnit& unit) const; + virtual bool HasMuscleMass() const; + virtual SEScalarMass& GetMuscleMass(); + virtual double GetMuscleMass(const MassUnit& unit) const; + virtual bool HasMeanArterialPressureBaseline() const; virtual SEScalarPressure& GetMeanArterialPressureBaseline(); virtual double GetMeanArterialPressureBaseline(const PressureUnit& unit) const; @@ -187,6 +191,7 @@ class DLL_DECL SEPatient : public Loggable SEScalarMassPerVolume* m_BodyDensity; SEScalarFraction* m_BodyFatFraction; SEScalarMass* m_LeanBodyMass; + SEScalarMass* m_MuscleMass; SEScalarArea* m_AlveoliSurfaceArea; SEScalarFraction* m_RightLungRatio; diff --git a/src/cdm/cpp/patient/actions/SEHemorrhage.cpp b/src/cdm/cpp/patient/actions/SEHemorrhage.cpp index 6a27416..5170acf 100644 --- a/src/cdm/cpp/patient/actions/SEHemorrhage.cpp +++ b/src/cdm/cpp/patient/actions/SEHemorrhage.cpp @@ -17,25 +17,25 @@ specific language governing permissions and limitations under the License. #include "bind/ScalarVolumePerTimeData.hxx" #include "bind/IntegerArray.hxx" #include "bind/IntegerList.hxx" +#include "properties/SEScalar0To1.h" SEHemorrhage::SEHemorrhage() : SEPatientAction() { m_Compartment = ""; //This is the compartment we use to store information about hemorrhage m_MCIS; m_BleedName = ""; //This is the name of the pathway in circuit that will have its resistance changed + m_Severity = nullptr; - //Place organs in a map so that we don't get too messy with nested conditionals. Each vector is digits 2-4 of the MCIS code - organMap[{6, 4}] = std::make_pair("AortaBleed", "Major Artery"); - organMap[{6, 6}] = std::make_pair("VenaCavaBleed", "Vena Cava"); - organMap[{6, 5}] = std::make_pair("AortaBleed", "Major Artery"); - organMap[{7, 1}] = std::make_pair("LungBleed", "Lungs"); - organMap[{7, 2}] = std::make_pair("HeartBleed", "Heart"); - organMap[{8, 1}] = std::make_pair("LiverBleed", "Liver"); - organMap[{8, 2}] = std::make_pair("SpleenBleed", "Spleen"); - organMap[{8, 3}] = std::make_pair("SplanchnicBleed", "Splanchnic"); - organMap[{8, 4}] = std::make_pair("KidneyBleed", "Kidney"); - organMap[{8, 5}] = std::make_pair("SmallIntestineBleed", "Small Intestine"); - organMap[{8, 6}] = std::make_pair("LargeIntestineBleed", "Large Intestine"); + //Place paths in torso in a map so that we don't get too messy with nested conditionals. Each vector is digits 2-4 of the MCIS code + organMap["Vena Cava"] = std::make_pair("VenaCavaBleed", std::vector{6, 6, 0}); + organMap["Lung"] = std::make_pair("LungBleed", std::vector{7, 1, 0}); + organMap["Myocardium"] = std::make_pair("HeartBleed", std::vector{7, 2, 0}); + organMap["Liver"] = std::make_pair("LiverBleed", std::vector{8, 1, 0}); + organMap["Spleen"] = std::make_pair("SpleenBleed", std::vector{8, 2, 0}); + organMap["Splanchnic"] = std::make_pair("SplanchnicBleed", std::vector{8, 3, 0}); + organMap["Kidney"] = std::make_pair("KidneyBleed", std::vector{8, 4, 0}); + organMap["Small Intestine"] = std::make_pair("SmallIntestineBleed", std::vector{8, 5, 0}); + organMap["Large Intestine"] = std::make_pair("LargeIntestineBleed", std::vector{8, 6, 0}); } SEHemorrhage::~SEHemorrhage() @@ -46,6 +46,7 @@ SEHemorrhage::~SEHemorrhage() void SEHemorrhage::Clear() { SEPatientAction::Clear(); + SAFE_DELETE(m_Severity); m_Compartment = ""; m_MCIS.clear(); m_BleedName = ""; @@ -53,35 +54,20 @@ void SEHemorrhage::Clear() bool SEHemorrhage::IsValid() const { - return SEPatientAction::IsValid() && HasCompartment() && HasMCIS() && HasBleedName(); + return SEPatientAction::IsValid() && HasCompartment() && HasBleedName(); } bool SEHemorrhage::IsActive() const { - return IsValid() ? !m_MCIS[0]==0 : false; + return IsValid() ? !(m_Severity->GetValue()<=ZERO_APPROX) : false; } bool SEHemorrhage::Load(const CDM::HemorrhageData& in) { SEPatientAction::Load(in); - if (in.MCIS().present()) - { - for (size_t i = 0; i < in.MCIS().get().IntegerList().size(); i++) - { - m_MCIS.push_back(in.MCIS().get().IntegerList()[i]); - } - if ((m_MCIS[0] < 0) || (m_MCIS[0] > 5)) //check to make sure no one puts in a severity of a million - { - SetComment("Invalid MCIS Code: Severity out of bounds (0-5). Defaulting to 3"); - m_MCIS[0] = 3; - } - if (m_MCIS.size() != 5) //make sure mcis code is proper length - { - SetComment("Invalid MCIS Code: Code must be 5 digits. Defaulting to aorta with bleeding severity = 3"); - m_MCIS = { 3,2,6,3,0 }; - } - } - ProcessMCIS(); + GetSeverity().Load(in.Severity()); + m_Compartment = in.Compartment(); + SetBleedPath(); return true; } @@ -96,69 +82,65 @@ CDM::HemorrhageData* SEHemorrhage::Unload() const void SEHemorrhage::Unload(CDM::HemorrhageData& data) const { SEPatientAction::Unload(data); - //Create Integer Array that stores Integer List and pass m_MCIS values to it (modeled after GetActiveIndices in electrocardiogram- - //interpolatorWaveform.cpp) - data.MCIS(std::unique_ptr(new CDM::IntegerArray())); - data.MCIS().get().IntegerList(std::unique_ptr(new CDM::IntegerList())); - for (int i : m_MCIS) - data.MCIS().get().IntegerList().push_back(i); -} - -void SEHemorrhage::ProcessMCIS() -{ - switch (m_MCIS[1]) { - case Head: - //Note that this assumes that the third digit is 6 (for vessels). - if (m_MCIS[3] == 1) //If bleeding is intracranial - { - SetBleedName("BrainBleed"); - SetCompartment("Head"); - } - else - { //If the bleeding is from the carotid artery/jugular vein - SetBleedName("AortaBleed"); - SetCompartment("Major Artery"); - } - break; - case Torso: - if (organMap.find({ m_MCIS.begin() + 2, m_MCIS.end()-1}) != organMap.end()) //extract the two digits that map to an organ - { - SetBleedName(organMap[{m_MCIS.begin() + 2, m_MCIS.end()-1}].first); - SetCompartment(organMap[{m_MCIS.begin() + 2, m_MCIS.end()-1}].second); - } - else - { - SetComment("Invalid MCIS Code: Does not map to BioGears compartment. Defaulting to Aorta"); - SetBleedName("AortaBleed"); - SetCompartment("Major Artery"); - } - break; - case Arms: - SetBleedName("ArmBleed"); - SetCompartment("Arm"); - break; - case Legs: - SetBleedName("LegBleed"); - SetCompartment("Leg"); - break; - default: - SetComment("Invalid MCIS Code: Does not map to BioGears compartment. Defaulting to Aorta"); - SetBleedName("AortaBleed"); - SetCompartment("Major Artery"); - break; - } + if(HasSeverity()) + data.Severity(std::unique_ptr(m_Severity->Unload())); + if (HasCompartment()) + data.Compartment(m_Compartment); } - +void SEHemorrhage::SetBleedPath() +{ + bool found = false; + int sev = (int)ceil(5.0*m_Severity->GetValue()); + m_MCIS.push_back(sev); + if (m_Compartment == "Head") + { + m_MCIS.insert(m_MCIS.end(), { 1,6,1,0 }); + m_BleedName = "BrainBleed"; + found = true; + } + else if (m_Compartment == "Major Artery") + { + m_MCIS.insert(m_MCIS.end(), { 2,6,4,0 }); + m_BleedName = "AortaBleed"; + found = true; + } + else if (m_Compartment == "Arm") + { + m_MCIS.insert(m_MCIS.end(), { 3,0,0,0 }); + m_BleedName = "ArmBleed"; + found = true; + } + else if (m_Compartment == "Leg") + { + m_MCIS.insert(m_MCIS.end(), { 4,0,0,0 }); + m_BleedName = "LegBleed"; + found = true; + } + else + { + m_MCIS.push_back(2); + //This inserts the code of integers stored in the organ map (associated with the compartment) at the end of the mcis vector + m_MCIS.insert(m_MCIS.end(), organMap[m_Compartment].second.begin(),organMap[m_Compartment].second.end()); + m_BleedName = organMap[m_Compartment].first; + found = true; + } + if (!found) + { + SetComment("Could not find compartment, defaulting to Aorta"); + m_MCIS.insert(m_MCIS.end(), { 2,6,4,0 }); + m_BleedName = "AortaBleed"; + } +} +bool SEHemorrhage::HasMCIS() const +{ + return !m_MCIS.empty(); +} std::string SEHemorrhage::GetBleedName() const { return m_BleedName; } -void SEHemorrhage::SetBleedName(const std::string& name) -{ - m_BleedName = name; -} bool SEHemorrhage::HasBleedName() const { return !m_BleedName.empty(); @@ -168,45 +150,50 @@ std::string SEHemorrhage::GetCompartment() const { return m_Compartment; } - -void SEHemorrhage::SetCompartment(const std::string& name) -{ - m_Compartment = name; -} - bool SEHemorrhage::HasCompartment() const { return !m_Compartment.empty(); } - +void SEHemorrhage::SetCompartment(const std::string& name) +{ + m_Compartment = name; +} void SEHemorrhage::InvalidateCompartment() { m_Compartment = ""; } -bool SEHemorrhage::HasMCIS() const +bool SEHemorrhage::HasSeverity() const { - return !m_MCIS.empty(); + return m_Severity == nullptr ? false : true; } - -void SEHemorrhage::SetMCIS(const std::vector& mcisIn) +SEScalar0To1& SEHemorrhage::GetSeverity() { - if (mcisIn.size() != 5) - Error("MCIS code must be five digits"); - else - m_MCIS = mcisIn; + if (m_Severity == nullptr) + m_Severity = new SEScalar0To1(); + return *m_Severity; } + void SEHemorrhage::ToString(std::ostream &str) const { - str << "Patient Action : Hemorrhage"; - if (HasComment()) - str << "\n\tComment: " << m_Comment; - str << "\n\tInjury Code: "; - for (int i : m_MCIS) - str << i; - str << "\n\tCompartment: "; HasCompartment() ? str << GetCompartment() : str << "No Compartment Set"; - str << "\n\tSeverity: "; str << m_MCIS[0]; - str << std::flush; - + if (m_Severity->GetValue() == 0.0) + { + str << "Patient Action : Stop Hemorrhage"; + if (HasComment()) + str << "\n\tComment: "; str << m_Comment; + str << "\n\tCompartment: "; HasCompartment() ? str << GetCompartment() : str << "No Compartment Set"; + } + else + { + str << "Patient Action : Hemorrhage"; + if (HasComment()) + str << "\n\tComment: " << m_Comment; + str << "\n\tSeverity: "; str << *m_Severity; + str << "\n\tCompartment: "; HasCompartment() ? str << GetCompartment() : str << "No Compartment Set"; + str << "\n\tInjury Code: "; + for (int i : m_MCIS) + str << i; + str << std::flush; + } } diff --git a/src/cdm/cpp/patient/actions/SEHemorrhage.h b/src/cdm/cpp/patient/actions/SEHemorrhage.h index 7f6b496..4a660b2 100644 --- a/src/cdm/cpp/patient/actions/SEHemorrhage.h +++ b/src/cdm/cpp/patient/actions/SEHemorrhage.h @@ -14,12 +14,6 @@ specific language governing permissions and limitations under the License. #include "patient/actions/SEPatientAction.h" #include "bind/HemorrhageData.hxx" - - - - - - class DLL_DECL SEHemorrhage : public SEPatientAction { public: @@ -40,19 +34,19 @@ class DLL_DECL SEHemorrhage : public SEPatientAction public: virtual std::string GetCompartment() const; - virtual void SetCompartment(const std::string& name); virtual bool HasCompartment() const; + virtual void SetCompartment(const std::string& name); virtual void InvalidateCompartment(); - virtual void ProcessMCIS(); virtual bool HasMCIS() const; virtual std::vector& GetMCIS() { return m_MCIS; } - virtual void SetMCIS(const std::vector& mcisIn); + virtual void SetBleedPath(); virtual bool HasBleedName() const; virtual std::string GetBleedName() const; - virtual void SetBleedName(const std::string& name); + virtual bool HasSeverity() const; + virtual SEScalar0To1& GetSeverity(); virtual void ToString(std::ostream &str) const; @@ -60,7 +54,6 @@ class DLL_DECL SEHemorrhage : public SEPatientAction std::string m_Compartment; std::vector m_MCIS; std::string m_BleedName; - std::map, std::pair> organMap; - - enum region { Head = 1, Torso = 2, Arms = 3, Legs = 4 }; //mcis digit 2 + SEScalar0To1* m_Severity; + std::map < std::string, std::pair>> organMap; }; \ No newline at end of file diff --git a/src/cdm/cpp/patient/actions/SESubstanceCompoundInfusion.cpp b/src/cdm/cpp/patient/actions/SESubstanceCompoundInfusion.cpp index 239af24..91cfdf0 100644 --- a/src/cdm/cpp/patient/actions/SESubstanceCompoundInfusion.cpp +++ b/src/cdm/cpp/patient/actions/SESubstanceCompoundInfusion.cpp @@ -41,6 +41,19 @@ bool SESubstanceCompoundInfusion::Load(const CDM::SubstanceCompoundInfusionData& { SESubstanceAdministration::Load(in); GetRate().Load(in.Rate()); + if (m_Rate->GetValue(VolumePerTimeUnit::mL_Per_min) > 285.0) + { + std::string msg = "Infusion rate of " + std::to_string(m_Rate->GetValue(VolumePerTimeUnit::mL_Per_min)) + + " mL/min is greater than the maximum allowable rate of 285.0 mL/min. Setting to maximum rate of 285.0 mL/min"; + m_Rate->SetValue(285.0, VolumePerTimeUnit::mL_Per_min); + SetComment(msg); + } + if (m_Rate->GetValue(VolumePerTimeUnit::mL_Per_min) > 100) + { + std::string msg = "Infusion rate of " + std::to_string(m_Rate->GetValue(VolumePerTimeUnit::mL_Per_min)) + + " mL/min is greater than the advised maximum of 100.0 mL/min. Undesirable side effects issues may arise"; + SetComment(msg); + } GetBagVolume().Load(in.BagVolume()); return true; } diff --git a/src/cdm/cpp/patient/conditions/SEDehydration.cpp b/src/cdm/cpp/patient/conditions/SEDehydration.cpp new file mode 100644 index 0000000..61a6c87 --- /dev/null +++ b/src/cdm/cpp/patient/conditions/SEDehydration.cpp @@ -0,0 +1,80 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +#include "stdafx.h" +#include "patient/conditions/SEDehydration.h" +#include "bind/DehydrationData.hxx" +#include "properties/SEScalar0To1.h" +#include "bind/Scalar0To1Data.hxx" + +SEDehydration::SEDehydration() : SEPatientCondition() +{ + m_DehydrationFraction=nullptr; +} + +SEDehydration::~SEDehydration() +{ + Clear(); +} + +void SEDehydration::Clear() +{ + SEPatientCondition::Clear(); + SAFE_DELETE(m_DehydrationFraction); +} + +bool SEDehydration::IsValid() const +{ + return SEPatientCondition::IsValid() && HasDehydrationFraction(); +} + +bool SEDehydration::Load(const CDM::DehydrationData& in) +{ + SEPatientCondition::Load(in); + GetDehydrationFraction().Load(in.DehydrationFraction()); + return true; +} + +CDM::DehydrationData* SEDehydration::Unload() const +{ + CDM::DehydrationData*data(new CDM::DehydrationData()); + Unload(*data); + return data; +} + +void SEDehydration::Unload(CDM::DehydrationData& data) const +{ + SEPatientCondition::Unload(data); + if(m_DehydrationFraction!=nullptr) + data.DehydrationFraction(std::unique_ptr(m_DehydrationFraction->Unload())); +} + +bool SEDehydration::HasDehydrationFraction() const +{ + return m_DehydrationFraction==nullptr?false:m_DehydrationFraction->IsValid(); +} + +SEScalar0To1& SEDehydration::GetDehydrationFraction() +{ + if(m_DehydrationFraction==nullptr) + m_DehydrationFraction=new SEScalar0To1(); + return *m_DehydrationFraction; +} + +void SEDehydration::ToString(std::ostream &str) const +{ + str << "Patient Condition : Dehydration"; + if(HasComment()) + str<<"\n\tComment: "<(m_InsulinProductionSeverity->Unload())); +} + +bool SEDiabetesType1::HasInsulinProductionSeverity() const +{ + return m_InsulinProductionSeverity==nullptr?false:m_InsulinProductionSeverity->IsValid(); +} + +SEScalar0To1& SEDiabetesType1::GetInsulinProductionSeverity() +{ + if(m_InsulinProductionSeverity==nullptr) + m_InsulinProductionSeverity =new SEScalar0To1(); + return *m_InsulinProductionSeverity; +} + +void SEDiabetesType1::ToString(std::ostream &str) const +{ + str << "Patient Condition : DiabetesType1"; + if(HasComment()) + str<<"\n\tComment: "<(m_InsulinProductionSeverity->Unload())); + if (m_InsulinResistanceSeverity != nullptr) + data.InsulinResistanceSeverity(std::unique_ptr(m_InsulinResistanceSeverity->Unload())); +} + +bool SEDiabetesType2::HasInsulinProductionSeverity() const +{ + return m_InsulinProductionSeverity==nullptr?false:m_InsulinProductionSeverity->IsValid(); +} + +SEScalar0To1& SEDiabetesType2::GetInsulinProductionSeverity() +{ + if(m_InsulinProductionSeverity==nullptr) + m_InsulinProductionSeverity =new SEScalar0To1(); + return *m_InsulinProductionSeverity; +} + +bool SEDiabetesType2::HasInsulinResistanceSeverity() const +{ + return m_InsulinResistanceSeverity == nullptr ? false : m_InsulinResistanceSeverity->IsValid(); +} + +SEScalar0To1& SEDiabetesType2::GetInsulinResistanceSeverity() +{ + if (m_InsulinResistanceSeverity == nullptr) + m_InsulinResistanceSeverity = new SEScalar0To1(); + return *m_InsulinResistanceSeverity; +} + +void SEDiabetesType2::ToString(std::ostream &str) const +{ + str << "Patient Condition : DiabetesType2"; + if(HasComment()) + str<<"\n\tComment: "<(m_TimeSinceMeal->Unload())); +} + +bool SEStarvation::HasTimeSinceMeal() const +{ + return m_TimeSinceMeal == nullptr ? false : m_TimeSinceMeal->IsValid(); +} + +SEScalarTime& SEStarvation::GetTimeSinceMeal() +{ + if (m_TimeSinceMeal == nullptr) + m_TimeSinceMeal = new SEScalarTime(); + return *m_TimeSinceMeal; +} + + +void SEStarvation::ToString(std::ostream &str) const +{ + str << "Patient Condition : Starvation"; + if (HasComment()) + str << "\n\tComment: " << m_Comment; + str << std::flush; +} \ No newline at end of file diff --git a/src/cdm/cpp/patient/conditions/SEStarvation.h b/src/cdm/cpp/patient/conditions/SEStarvation.h new file mode 100644 index 0000000..d06aa92 --- /dev/null +++ b/src/cdm/cpp/patient/conditions/SEStarvation.h @@ -0,0 +1,44 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +#pragma once +#include "patient/conditions/SEPatientCondition.h" +#include "bind/StarvationData.hxx" + +class DLL_DECL SEStarvation : public SEPatientCondition +{ +public: + + SEStarvation(); + virtual ~SEStarvation(); + + virtual void Clear(); + + virtual bool IsValid() const; + + virtual bool Load(const CDM::StarvationData& in); + virtual CDM::StarvationData* Unload() const; +protected: + virtual void Unload(CDM::StarvationData& data) const; + +public: + virtual std::string GetName() const { return "Starvation"; } + + virtual bool HasTimeSinceMeal() const; + virtual SEScalarTime& GetTimeSinceMeal(); + + virtual void ToString(std::ostream &str) const; + +protected: + + SEScalarTime* m_TimeSinceMeal; +}; \ No newline at end of file diff --git a/src/cdm/cpp/scenario/SECondition.cpp b/src/cdm/cpp/scenario/SECondition.cpp index aa6db74..e11da28 100644 --- a/src/cdm/cpp/scenario/SECondition.cpp +++ b/src/cdm/cpp/scenario/SECondition.cpp @@ -26,8 +26,14 @@ specific language governing permissions and limitations under the License. #include "bind/ChronicRenalStenosisData.hxx" #include "patient/conditions/SEChronicVentricularSystolicDysfunction.h" #include "bind/ChronicVentricularSystolicDysfunctionData.hxx" -#include "patient/conditions/SEConsumeMeal.h" -#include "bind/ConsumeMealData.hxx" +#include "patient/conditions/SEStarvation.h" +#include "bind/StarvationData.hxx" +#include "patient/conditions/SEDiabetesType1.h" +#include "bind/DiabetesType1Data.hxx" +#include "patient/conditions/SEDiabetesType2.h" +#include "bind/DiabetesType2Data.hxx" +#include "patient/conditions/SEDehydration.h" +#include "bind/DehydrationData.hxx" #include "patient/conditions/SELobarPneumonia.h" #include "bind/LobarPneumoniaData.hxx" #include "patient/conditions/SEChronicPericardialEffusion.h" @@ -92,11 +98,32 @@ SECondition* SECondition::newFromBind(const CDM::ConditionData& data, SESubstanc cc->Load(*ccRenalStenosisData); return cc; } - const CDM::ConsumeMealData* ccConsumeMealData = dynamic_cast(&data); - if (ccConsumeMealData != nullptr) + const CDM::DehydrationData* ccDehydrationData = dynamic_cast(&data); + if (ccDehydrationData != nullptr) + { + SEDehydration* cc = new SEDehydration(); + cc->Load(*ccDehydrationData); + return cc; + } + const CDM::DiabetesType1Data* ccDiabetesType1Data = dynamic_cast(&data); + if (ccDiabetesType1Data != nullptr) + { + SEDiabetesType1* cc = new SEDiabetesType1(); + cc->Load(*ccDiabetesType1Data); + return cc; + } + const CDM::DiabetesType2Data* ccDiabetesType2Data = dynamic_cast(&data); + if (ccDiabetesType2Data != nullptr) + { + SEDiabetesType2* cc = new SEDiabetesType2(); + cc->Load(*ccDiabetesType2Data); + return cc; + } + const CDM::StarvationData* ccStarvationData = dynamic_cast(&data); + if (ccStarvationData != nullptr) { - SEConsumeMeal* cc = new SEConsumeMeal(); - cc->Load(*ccConsumeMealData); + SEStarvation* cc = new SEStarvation(); + cc->Load(*ccStarvationData); return cc; } const CDM::ImpairedAlveolarExchangeData* ccImpairedAlveolarExchangeData = dynamic_cast(&data); diff --git a/src/cdm/cpp/scenario/SEConditionManager.cpp b/src/cdm/cpp/scenario/SEConditionManager.cpp index 40e5799..52ef1c9 100644 --- a/src/cdm/cpp/scenario/SEConditionManager.cpp +++ b/src/cdm/cpp/scenario/SEConditionManager.cpp @@ -25,8 +25,14 @@ specific language governing permissions and limitations under the License. #include "bind/ChronicRenalStenosisData.hxx" #include "patient/conditions/SEChronicVentricularSystolicDysfunction.h" #include "bind/ChronicVentricularSystolicDysfunctionData.hxx" -#include "patient/conditions/SEConsumeMeal.h" -#include "bind/ConsumeMealData.hxx" +#include "patient/conditions/SEDehydration.h" +#include "bind/DehydrationData.hxx" +#include "patient/conditions/SEDiabetesType1.h" +#include "bind/DiabetesType1Data.hxx" +#include "patient/conditions/SEDiabetesType2.h" +#include "bind/DiabetesType2Data.hxx" +#include "patient/conditions/SEStarvation.h" +#include "bind/StarvationData.hxx" #include "patient/conditions/SELobarPneumonia.h" #include "bind/LobarPneumoniaData.hxx" #include "patient/conditions/SEChronicPericardialEffusion.h" @@ -47,7 +53,9 @@ SEConditionManager::SEConditionManager(SESubstanceManager& substances) : Loggabl m_HeartFailure = nullptr; m_RenalStenosis = nullptr; m_Dehydration = nullptr; - m_ConsumeMeal = nullptr; + m_DiabetesType1 = nullptr; + m_DiabetesType2 = nullptr; + m_Starvation = nullptr; m_LobarPneumonia = nullptr; m_PericardialEffusion = nullptr; m_ImpairedAlveolarExchange = nullptr; @@ -66,7 +74,9 @@ void SEConditionManager::Clear() m_HeartFailure = nullptr; m_RenalStenosis = nullptr; m_Dehydration = nullptr; - m_ConsumeMeal = nullptr; + m_DiabetesType1 = nullptr; + m_DiabetesType2 = nullptr; + m_Starvation = nullptr; m_LobarPneumonia = nullptr; m_PericardialEffusion = nullptr; m_ImpairedAlveolarExchange = nullptr; @@ -92,8 +102,8 @@ bool SEConditionManager::ProcessCondition(const SECondition& condition) bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) { - const CDM::ChronicAnemiaData* a = dynamic_cast(&condition); - if (a != nullptr) + const CDM::ChronicAnemiaData* anemiaData = dynamic_cast(&condition); + if (anemiaData != nullptr) { if (HasChronicAnemia()) { @@ -101,13 +111,13 @@ bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) return false; } m_Anemia = new SEChronicAnemia(); - m_Anemia->Load(*a); + m_Anemia->Load(*anemiaData); m_Conditions.push_back(m_Anemia); return true; } - const CDM::ChronicObstructivePulmonaryDiseaseData* copd = dynamic_cast(&condition); - if (copd != nullptr) + const CDM::ChronicObstructivePulmonaryDiseaseData* copdData = dynamic_cast(&condition); + if (copdData != nullptr) { if (HasChronicObstructivePulmonaryDisease()) { @@ -115,24 +125,24 @@ bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) return false; } m_COPD = new SEChronicObstructivePulmonaryDisease(); - m_COPD->Load(*copd); + m_COPD->Load(*copdData); m_Conditions.push_back(m_COPD); return true; } - const CDM::ChronicHeartFailureData* h = dynamic_cast(&condition); - if (h != nullptr) + const CDM::ChronicHeartFailureData* heartFailureData = dynamic_cast(&condition); + if (heartFailureData != nullptr) { if (HasChronicHeartFailure()) { Error("Cannot have multiple Heart Failure conditions"); return false; } - const CDM::ChronicVentricularSystolicDysfunctionData* vsd = dynamic_cast(&condition); - if (vsd != nullptr) + const CDM::ChronicVentricularSystolicDysfunctionData* vsdData = dynamic_cast(&condition); + if (vsdData != nullptr) { m_HeartFailure = new SEChronicVentricularSystolicDysfunction(); - m_HeartFailure->Load(*vsd); + m_HeartFailure->Load(*vsdData); m_Conditions.push_back(m_HeartFailure); return true; } @@ -140,8 +150,8 @@ bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) return false; } - const CDM::ChronicPericardialEffusionData* pe = dynamic_cast(&condition); - if (pe != nullptr) + const CDM::ChronicPericardialEffusionData* peData = dynamic_cast(&condition); + if (peData != nullptr) { if (HasChronicPericardialEffusion()) { @@ -149,13 +159,13 @@ bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) return false; } m_PericardialEffusion = new SEChronicPericardialEffusion(); - m_PericardialEffusion->Load(*pe); + m_PericardialEffusion->Load(*peData); m_Conditions.push_back(m_PericardialEffusion); return true; } - const CDM::ChronicRenalStenosisData* r = dynamic_cast(&condition); - if (r != nullptr) + const CDM::ChronicRenalStenosisData* renalStenosisData = dynamic_cast(&condition); + if (renalStenosisData != nullptr) { if (HasChronicRenalStenosis()) { @@ -163,27 +173,69 @@ bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) return false; } m_RenalStenosis = new SEChronicRenalStenosis(); - m_RenalStenosis->Load(*r); + m_RenalStenosis->Load(*renalStenosisData); m_Conditions.push_back(m_RenalStenosis); return true; } - const CDM::ConsumeMealData* g = dynamic_cast(&condition); - if (g != nullptr) + const CDM::DehydrationData* dehydrationData = dynamic_cast(&condition); + if (dehydrationData != nullptr) + { + if (HasDehydration()) + { + Error("Cannot have multiple Dehydration conditions"); + return false; + } + m_Dehydration = new SEDehydration(); + m_Dehydration->Load(*dehydrationData); + m_Conditions.push_back(m_Dehydration); + return true; + } + + const CDM::DiabetesType1Data* diabetes1Data = dynamic_cast(&condition); + if (diabetes1Data != nullptr) + { + if (HasDiabetesType1()) + { + Error("Cannot have multiple DiabetesType1 conditions"); + return false; + } + m_DiabetesType1 = new SEDiabetesType1(); + m_DiabetesType1->Load(*diabetes1Data); + m_Conditions.push_back(m_DiabetesType1); + return true; + } + + const CDM::DiabetesType2Data* diabetes2Data = dynamic_cast(&condition); + if (diabetes2Data != nullptr) + { + if (HasDiabetesType2()) + { + Error("Cannot have multiple DiabetesType2 conditions"); + return false; + } + m_DiabetesType2 = new SEDiabetesType2(); + m_DiabetesType2->Load(*diabetes2Data); + m_Conditions.push_back(m_DiabetesType2); + return true; + } + + const CDM::StarvationData* starvationData = dynamic_cast(&condition); + if (starvationData != nullptr) { - if (HasConsumeMeal()) + if (HasStarvation()) { - Error("Cannot have multiple Gut Nutrient conditions"); + Error("Cannot have multiple Starvation conditions"); return false; } - m_ConsumeMeal = new SEConsumeMeal(); - m_ConsumeMeal->Load(*g); - m_Conditions.push_back(m_ConsumeMeal); + m_Starvation = new SEStarvation(); + m_Starvation->Load(*starvationData); + m_Conditions.push_back(m_Starvation); return true; } - const CDM::ImpairedAlveolarExchangeData* iae = dynamic_cast(&condition); - if (iae != nullptr) + const CDM::ImpairedAlveolarExchangeData* impairedAlvioliData = dynamic_cast(&condition); + if (impairedAlvioliData != nullptr) { if (HasImpairedAlveolarExchange()) { @@ -191,13 +243,13 @@ bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) return false; } m_ImpairedAlveolarExchange = new SEImpairedAlveolarExchange(); - m_ImpairedAlveolarExchange->Load(*iae); + m_ImpairedAlveolarExchange->Load(*impairedAlvioliData); m_Conditions.push_back(m_ImpairedAlveolarExchange); return true; } - const CDM::LobarPneumoniaData* lp = dynamic_cast(&condition); - if (lp != nullptr) + const CDM::LobarPneumoniaData* pneumoniaData = dynamic_cast(&condition); + if (pneumoniaData != nullptr) { if (HasLobarPneumonia()) { @@ -205,14 +257,14 @@ bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) return false; } m_LobarPneumonia = new SELobarPneumonia(); - m_LobarPneumonia->Load(*lp); + m_LobarPneumonia->Load(*pneumoniaData); m_Conditions.push_back(m_LobarPneumonia); return true; } - const CDM::InitialEnvironmentData* ie = dynamic_cast(&condition); - if (ie != nullptr) + const CDM::InitialEnvironmentData* environmentData = dynamic_cast(&condition); + if (environmentData != nullptr) { if (HasInitialEnvironment()) { @@ -220,7 +272,7 @@ bool SEConditionManager::ProcessCondition(const CDM::ConditionData& condition) return false; } m_InitialEnvironment = new SEInitialEnvironment(m_Substances); - m_InitialEnvironment->Load(*ie); + m_InitialEnvironment->Load(*environmentData); m_Conditions.push_back(m_InitialEnvironment); return true; } @@ -288,13 +340,31 @@ SEDehydration* SEConditionManager::GetDehydration() const return m_Dehydration; } -bool SEConditionManager::HasConsumeMeal() const +bool SEConditionManager::HasDiabetesType1() const +{ + return m_DiabetesType1 != nullptr; +} +SEDiabetesType1* SEConditionManager::GetDiabetesType1() const +{ + return m_DiabetesType1; +} + +bool SEConditionManager::HasDiabetesType2() const +{ + return m_DiabetesType2 != nullptr; +} +SEDiabetesType2* SEConditionManager::GetDiabetesType2() const +{ + return m_DiabetesType2; +} + +bool SEConditionManager::HasStarvation() const { - return m_ConsumeMeal != nullptr; + return m_Starvation != nullptr; } -SEConsumeMeal* SEConditionManager::GetConsumeMeal() const +SEStarvation* SEConditionManager::GetStarvation() const { - return m_ConsumeMeal; + return m_Starvation; } bool SEConditionManager::HasImpairedAlveolarExchange() const diff --git a/src/cdm/cpp/scenario/SEConditionManager.h b/src/cdm/cpp/scenario/SEConditionManager.h index 02d9fd2..a9bc5db 100644 --- a/src/cdm/cpp/scenario/SEConditionManager.h +++ b/src/cdm/cpp/scenario/SEConditionManager.h @@ -18,8 +18,10 @@ class SEChronicObstructivePulmonaryDisease; class SEChronicPericardialEffusion; class SEChronicVentricularSystolicDysfunction; class SEChronicRenalStenosis; -class SEConsumeMeal; +class SEStarvation; class SEDehydration; +class SEDiabetesType1; +class SEDiabetesType2; class SEFasting; class SEImpairedAlveolarExchange; class SELobarPneumonia; @@ -62,12 +64,18 @@ class DLL_DECL SEConditionManager : public Loggable bool HasChronicRenalStenosis() const; SEChronicRenalStenosis* GetChronicRenalStenosis() const; - bool HasConsumeMeal() const; - SEConsumeMeal* GetConsumeMeal() const; + bool HasStarvation() const; + SEStarvation* GetStarvation() const; bool HasDehydration() const; SEDehydration* GetDehydration() const; + bool HasDiabetesType1() const; + SEDiabetesType1* GetDiabetesType1() const; + + bool HasDiabetesType2() const; + SEDiabetesType2* GetDiabetesType2() const; + bool HasImpairedAlveolarExchange() const; SEImpairedAlveolarExchange* GetImpairedAlveolarExchange() const; @@ -84,9 +92,11 @@ class DLL_DECL SEConditionManager : public Loggable SESubstanceManager& m_Substances; SEChronicAnemia* m_Anemia; - SEConsumeMeal* m_ConsumeMeal; + SEStarvation* m_Starvation; SEChronicObstructivePulmonaryDisease* m_COPD; SEDehydration* m_Dehydration; + SEDiabetesType1* m_DiabetesType1; + SEDiabetesType2* m_DiabetesType2; SEChronicHeartFailure* m_HeartFailure; SEImpairedAlveolarExchange* m_ImpairedAlveolarExchange; SEChronicPericardialEffusion* m_PericardialEffusion; diff --git a/src/cdm/cpp/scenario/SEPatientActionCollection.cpp b/src/cdm/cpp/scenario/SEPatientActionCollection.cpp index 9da043d..d6e3557 100644 --- a/src/cdm/cpp/scenario/SEPatientActionCollection.cpp +++ b/src/cdm/cpp/scenario/SEPatientActionCollection.cpp @@ -387,30 +387,20 @@ bool SEPatientActionCollection::ProcessAction(const CDM::PatientActionData& acti const CDM::HemorrhageData* hem = dynamic_cast(&action); if(hem!=nullptr) { - //We only store the MCIS on the CDM (so that our input is just one code and nothing else). But we need the compartment information to - //check if hemorrhage already exists in m_Hemorrhages map. Thus we use a temporary variable to check. - SEHemorrhage* myHem; - SEHemorrhage* temp = new SEHemorrhage; - temp->Load(*hem); - if (m_Hemorrhages.find(temp->GetCompartment()) == m_Hemorrhages.end()) + SEHemorrhage* myHem = m_Hemorrhages[hem->Compartment()]; + if (myHem == nullptr) { - myHem = new SEHemorrhage; - m_Hemorrhages[temp->GetCompartment()] = myHem; - myHem->Load(*hem); + myHem = new SEHemorrhage(); + m_Hemorrhages[hem->Compartment()] = myHem; } - else + myHem->Load(*hem); + + if (!myHem->IsActive()) { - myHem = m_Hemorrhages[temp->GetCompartment()]; - myHem->Load(*hem); - } - if (!myHem->IsActive()) - { - RemoveHemorrhage(myHem->GetCompartment()); - SAFE_DELETE(temp); - return true; - } - SAFE_DELETE(temp); - return IsValid(*myHem); + RemoveHemorrhage(myHem->GetCompartment()); + return true; + } + return IsValid(*myHem); } const CDM::IntubationData* intubation = dynamic_cast(&action); diff --git a/src/cdm/cpp/substance/SESubstance.cpp b/src/cdm/cpp/substance/SESubstance.cpp index 8f2b888..fb6b268 100644 --- a/src/cdm/cpp/substance/SESubstance.cpp +++ b/src/cdm/cpp/substance/SESubstance.cpp @@ -30,16 +30,19 @@ specific language governing permissions and limitations under the License. #include "properties/SEScalarMassPerVolume.h" #include "properties/SEScalarVolumePerTime.h" #include "properties/SEScalarVolumePerTimePressure.h" +#include "properties/SEScalarElectricResistance.h" SESubstance::SESubstance(Logger* logger) : Loggable(logger) { m_Name = ""; m_State = (CDM::enumSubstanceState::value) - 1; + m_Classification = (CDM::enumSubstanceClass::value) - 1; m_Density = nullptr; m_MolarMass = nullptr; m_MaximumDiffusionFlux = nullptr; m_MichaelisCoefficient = nullptr; + m_MembraneResistance = nullptr; m_Aerosolization = nullptr; m_BloodConcentration = nullptr; @@ -72,11 +75,13 @@ void SESubstance::Clear() { m_Name = ""; m_State = (CDM::enumSubstanceState::value) - 1; + m_Classification = (CDM::enumSubstanceClass::value) - 1; SAFE_DELETE(m_Density); SAFE_DELETE(m_MolarMass); SAFE_DELETE(m_MaximumDiffusionFlux); SAFE_DELETE(m_MichaelisCoefficient); + SAFE_DELETE(m_MembraneResistance); SAFE_DELETE(m_BloodConcentration); SAFE_DELETE(m_MassInBody); @@ -113,6 +118,8 @@ const SEScalar* SESubstance::GetScalar(const std::string& name) return &GetMaximumDiffusionFlux(); if (name.compare("MichaelisCoefficient") == 0) return &GetMichaelisCoefficient(); + if (name.compare("MembraneConductivity") == 0) + return &GetMembraneResistance(); if (name.compare("BloodConcentration") == 0) return &GetBloodConcentration(); @@ -169,6 +176,8 @@ bool SESubstance::Load(const CDM::SubstanceData& in) if (in.State().present()) m_State = in.State().get(); + if (in.Classification().present()) + m_Classification = in.Classification().get(); if (in.Density().present()) GetDensity().Load(in.Density().get()); if (in.MolarMass().present()) @@ -178,6 +187,8 @@ bool SESubstance::Load(const CDM::SubstanceData& in) GetMaximumDiffusionFlux().Load(in.MaximumDiffusionFlux().get()); if (in.MichaelisCoefficient().present()) GetMichaelisCoefficient().Load(in.MichaelisCoefficient().get()); + if (in.MembraneResistance().present()) + GetMembraneResistance().Load(in.MembraneResistance().get()); if (in.BloodConcentration().present()) GetBloodConcentration().Load(in.BloodConcentration().get()); @@ -241,6 +252,8 @@ void SESubstance::Unload(CDM::SubstanceData& data) const data.Name(m_Name); if (HasState()) data.State(m_State); + if (HasClassification()) + data.Classification(m_Classification); if (HasDensity()) data.Density(std::unique_ptr(m_Density->Unload())); if (HasMolarMass()) @@ -250,6 +263,8 @@ void SESubstance::Unload(CDM::SubstanceData& data) const data.MaximumDiffusionFlux(std::unique_ptr(m_MaximumDiffusionFlux->Unload())); if (HasMichaelisCoefficient()) data.MichaelisCoefficient(std::unique_ptr(m_MichaelisCoefficient->Unload())); + if (HasMembraneResistance()) + data.MembraneResistance(std::unique_ptr(m_MembraneResistance->Unload())); if (HasBloodConcentration()) data.BloodConcentration(std::unique_ptr(m_BloodConcentration->Unload())); @@ -325,6 +340,23 @@ void SESubstance::InvalidateState() m_State = (CDM::enumSubstanceState::value) - 1; } +CDM::enumSubstanceClass::value SESubstance::GetClassification() const +{ + return m_Classification; +} +void SESubstance::SetClassification(CDM::enumSubstanceClass::value subClass) +{ + m_Classification = subClass; +} +bool SESubstance::HasClassification() const +{ + return m_Classification == ((CDM::enumSubstanceState::value) - 1) ? false : true; +} +void SESubstance::InvalidateClassification() +{ + m_Classification = (CDM::enumSubstanceClass::value) - 1; +} + bool SESubstance::HasDensity() const { return (m_Density == nullptr) ? false : m_Density->IsValid(); @@ -393,6 +425,24 @@ double SESubstance::GetMichaelisCoefficient() const return m_MichaelisCoefficient->GetValue(); } +bool SESubstance::HasMembraneResistance() const +{ + return (m_MembraneResistance == nullptr) ? false : m_MembraneResistance->IsValid(); +} +SEScalarElectricResistance& SESubstance::GetMembraneResistance() +{ + if (m_MembraneResistance == nullptr) + m_MembraneResistance = new SEScalarElectricResistance(); + return *m_MembraneResistance; +} +double SESubstance::GetMembraneResistance(const ElectricResistanceUnit& unit) const +{ + if (m_MembraneResistance == nullptr) + return SEScalarElectricResistance::dNaN(); + return m_MembraneResistance->GetValue(unit); +} + + bool SESubstance::HasAerosolization() const { return (m_Aerosolization != nullptr && m_Aerosolization->IsValid()); diff --git a/src/cdm/cpp/substance/SESubstance.h b/src/cdm/cpp/substance/SESubstance.h index 996c14d..3b703b5 100644 --- a/src/cdm/cpp/substance/SESubstance.h +++ b/src/cdm/cpp/substance/SESubstance.h @@ -13,6 +13,7 @@ specific language governing permissions and limitations under the License. #pragma once CDM_BIND_DECL(SubstanceData) #include "bind/enumSubstanceState.hxx" +#include "bind/enumSubstanceClass.hxx" #include "substance/SESubstanceAerosolization.h" #include "substance/SESubstanceClearance.h" @@ -47,6 +48,11 @@ class DLL_DECL SESubstance : public Loggable virtual bool HasState() const; virtual void InvalidateState(); + virtual CDM::enumSubstanceClass::value GetClassification() const; + virtual void SetClassification(CDM::enumSubstanceClass::value subClass); + virtual bool HasClassification() const; + virtual void InvalidateClassification(); + virtual bool HasDensity() const; virtual SEScalarMassPerVolume& GetDensity(); virtual double GetDensity(const MassPerVolumeUnit& unit) const; @@ -64,6 +70,10 @@ class DLL_DECL SESubstance : public Loggable virtual SEScalar& GetMichaelisCoefficient(); virtual double GetMichaelisCoefficient() const; + virtual bool HasMembraneResistance() const; + virtual SEScalarElectricResistance& GetMembraneResistance(); + virtual double GetMembraneResistance(const ElectricResistanceUnit& unit) const; + // Liquid-ish virtual bool HasAerosolization() const; virtual SESubstanceAerosolization& GetAerosolization(); @@ -146,12 +156,14 @@ class DLL_DECL SESubstance : public Loggable protected: std::string m_Name; + CDM::enumSubstanceClass::value m_Classification; CDM::enumSubstanceState::value m_State; SEScalarMassPerVolume* m_Density; SEScalarMassPerAmount* m_MolarMass; SEScalarMassPerAreaTime* m_MaximumDiffusionFlux; SEScalar* m_MichaelisCoefficient; + SEScalarElectricResistance* m_MembraneResistance; SESubstanceAerosolization* m_Aerosolization; SEScalarMassPerVolume* m_BloodConcentration; diff --git a/src/cdm/cpp/substance/SESubstanceManager.cpp b/src/cdm/cpp/substance/SESubstanceManager.cpp index ddf037a..a46436d 100644 --- a/src/cdm/cpp/substance/SESubstanceManager.cpp +++ b/src/cdm/cpp/substance/SESubstanceManager.cpp @@ -333,15 +333,15 @@ bool SESubstanceManager::LoadSubstanceDirectory() sub = new SESubstance(GetLogger()); sub->Load(*subData); AddSubstance(*sub); - m_OriginalSubstanceData[sub] = subData; + m_OriginalSubstanceData[sub] = subData; continue; } compoundData = dynamic_cast(obj); if (compoundData != nullptr) {// Save this off and process it till later, once all substances are read - compound = new SESubstanceCompound(GetLogger()); - m_OriginalCompoundData[compound] = compoundData; - AddCompound(*compound); + compound = new SESubstanceCompound(GetLogger()); + m_OriginalCompoundData[compound] = compoundData; + AddCompound(*compound); continue; } Error("Unknown Type"); diff --git a/src/cdm/cpp/system/physiology/SEBloodChemistrySystem.cpp b/src/cdm/cpp/system/physiology/SEBloodChemistrySystem.cpp index d41e77a..c8919a9 100644 --- a/src/cdm/cpp/system/physiology/SEBloodChemistrySystem.cpp +++ b/src/cdm/cpp/system/physiology/SEBloodChemistrySystem.cpp @@ -43,6 +43,7 @@ SEBloodChemistrySystem::SEBloodChemistrySystem(Logger* logger) : SESystem(logger m_Phosphate = nullptr; m_PlasmaVolume = nullptr; m_PulseOximetry = nullptr; + m_RedBloodCellAcetylcholinesterase = nullptr; m_RedBloodCellCount = nullptr; m_ShuntFraction = nullptr; m_StrongIonDifference = nullptr; @@ -82,6 +83,7 @@ void SEBloodChemistrySystem::Clear() SAFE_DELETE(m_Phosphate); SAFE_DELETE(m_PlasmaVolume); SAFE_DELETE(m_PulseOximetry); + SAFE_DELETE(m_RedBloodCellAcetylcholinesterase); SAFE_DELETE(m_RedBloodCellCount); SAFE_DELETE(m_ShuntFraction); SAFE_DELETE(m_StrongIonDifference); @@ -126,6 +128,8 @@ const SEScalar* SEBloodChemistrySystem::GetScalar(const std::string& name) return &GetPlasmaVolume(); if (name.compare("PulseOximetry") == 0) return &GetPulseOximetry(); + if (name.compare("RedBloodCellAcetylcholinesterase") == 0) + return &GetRedBloodCellAcetylcholinesterase(); if (name.compare("RedBloodCellCount") == 0) return &GetRedBloodCellCount(); if (name.compare("ShuntFraction") == 0) @@ -189,6 +193,8 @@ bool SEBloodChemistrySystem::Load(const CDM::BloodChemistrySystemData& in) GetPlasmaVolume().Load(in.PlasmaVolume().get()); if (in.PulseOximetry().present()) GetPulseOximetry().Load(in.PulseOximetry().get()); + if (in.RedBloodCellAcetylcholinesterase().present()) + GetRedBloodCellAcetylcholinesterase().Load(in.RedBloodCellAcetylcholinesterase().get()); if (in.RedBloodCellCount().present()) GetRedBloodCellCount().Load(in.RedBloodCellCount().get()); if(in.ShuntFraction().present()) @@ -259,6 +265,8 @@ void SEBloodChemistrySystem::Unload(CDM::BloodChemistrySystemData& data) const data.PlasmaVolume(std::unique_ptr(m_PlasmaVolume->Unload())); if (m_PulseOximetry != nullptr) data.PulseOximetry(std::unique_ptr(m_PulseOximetry->Unload())); + if (m_RedBloodCellAcetylcholinesterase != nullptr) + data.RedBloodCellAcetylcholinesterase(std::unique_ptr(m_RedBloodCellAcetylcholinesterase->Unload())); if (m_RedBloodCellCount != nullptr) data.RedBloodCellCount(std::unique_ptr(m_RedBloodCellCount->Unload())); if(m_ShuntFraction!=nullptr) @@ -496,6 +504,23 @@ double SEBloodChemistrySystem::GetPulseOximetry() const return m_PulseOximetry->GetValue(); } +bool SEBloodChemistrySystem::HasRedBloodCellAcetylcholinesterase() const +{ + return m_RedBloodCellAcetylcholinesterase == nullptr ? false : m_RedBloodCellAcetylcholinesterase->IsValid(); +} +SEScalarAmountPerVolume& SEBloodChemistrySystem::GetRedBloodCellAcetylcholinesterase() +{ + if (m_RedBloodCellAcetylcholinesterase == nullptr) + m_RedBloodCellAcetylcholinesterase = new SEScalarAmountPerVolume(); + return *m_RedBloodCellAcetylcholinesterase; +} +double SEBloodChemistrySystem::GetRedBloodCellAcetylcholinesterase(const AmountPerVolumeUnit& unit) const +{ + if (m_RedBloodCellCount == nullptr) + return SEScalar::dNaN(); + return m_RedBloodCellAcetylcholinesterase->GetValue(unit); +} + bool SEBloodChemistrySystem::HasRedBloodCellCount() const { return m_RedBloodCellCount==nullptr?false:m_RedBloodCellCount->IsValid(); diff --git a/src/cdm/cpp/system/physiology/SEBloodChemistrySystem.h b/src/cdm/cpp/system/physiology/SEBloodChemistrySystem.h index 5a8b1e3..172a5c8 100644 --- a/src/cdm/cpp/system/physiology/SEBloodChemistrySystem.h +++ b/src/cdm/cpp/system/physiology/SEBloodChemistrySystem.h @@ -146,6 +146,16 @@ class DLL_DECL SEBloodChemistrySystem : public SESystem /// Get member class, allocate if nullptr virtual SEScalarFraction& GetPulseOximetry(); virtual double GetPulseOximetry() const; + //@} + + /** @name RedBloodCellAcetylcholinesteraseConcentration + * @brief @copybrief Physiology_BloodChemistrySystemData_RedBloodCellAcetylcholinesteraseConcentration + * @{*/ + /// %Test if member has been allocated + virtual bool HasRedBloodCellAcetylcholinesterase() const; + /// Get member class, allocate if nullptr + virtual SEScalarAmountPerVolume& GetRedBloodCellAcetylcholinesterase(); + virtual double GetRedBloodCellAcetylcholinesterase(const AmountPerVolumeUnit& unit) const; //@} /** @name RedBloodCellCount @@ -312,6 +322,7 @@ class DLL_DECL SEBloodChemistrySystem : public SESystem SEScalarAmountPerVolume* m_Phosphate; SEScalarVolume* m_PlasmaVolume; SEScalarFraction* m_PulseOximetry; + SEScalarAmountPerVolume* m_RedBloodCellAcetylcholinesterase; SEScalarAmountPerVolume* m_RedBloodCellCount; SEScalarFraction* m_ShuntFraction; SEScalarAmountPerVolume* m_StrongIonDifference; diff --git a/src/cdm/cpp/system/physiology/SEEnergySystem.cpp b/src/cdm/cpp/system/physiology/SEEnergySystem.cpp index d564cd8..40eb613 100644 --- a/src/cdm/cpp/system/physiology/SEEnergySystem.cpp +++ b/src/cdm/cpp/system/physiology/SEEnergySystem.cpp @@ -35,13 +35,15 @@ specific language governing permissions and limitations under the License. SEEnergySystem::SEEnergySystem(Logger* logger) : SESystem(logger) { m_AchievedExerciseLevel = nullptr; + m_ChlorideLostToSweat = nullptr; m_CoreTemperature = nullptr; m_CreatinineProductionRate = nullptr; m_ExerciseMeanArterialPressureDelta = nullptr; m_FatigueLevel = nullptr; - m_KetoneProductionRate = nullptr; m_LactateProductionRate = nullptr; + m_PotassiumLostToSweat = nullptr; m_SkinTemperature=nullptr; + m_SodiumLostToSweat = nullptr; m_SweatRate = nullptr; m_TotalMetabolicRate = nullptr; m_TotalWorkRateLevel = nullptr; @@ -58,13 +60,15 @@ void SEEnergySystem::Clear() SESystem::Clear(); SAFE_DELETE(m_AchievedExerciseLevel); + SAFE_DELETE(m_ChlorideLostToSweat); SAFE_DELETE(m_CoreTemperature); SAFE_DELETE(m_CreatinineProductionRate); SAFE_DELETE(m_ExerciseMeanArterialPressureDelta); SAFE_DELETE(m_FatigueLevel); - SAFE_DELETE(m_KetoneProductionRate); SAFE_DELETE(m_LactateProductionRate); + SAFE_DELETE(m_PotassiumLostToSweat); SAFE_DELETE(m_SkinTemperature); + SAFE_DELETE(m_SodiumLostToSweat); SAFE_DELETE(m_SweatRate); SAFE_DELETE(m_TotalMetabolicRate); SAFE_DELETE(m_TotalWorkRateLevel); @@ -74,6 +78,8 @@ const SEScalar* SEEnergySystem::GetScalar(const std::string& name) { if (name.compare("AchievedExerciseLevel") == 0) return &GetAchievedExerciseLevel(); + if (name.compare("ChlorideLostToSweat") == 0) + return &GetChlorideLostToSweat(); if (name.compare("CoreTemperature") == 0) return &GetCoreTemperature(); if (name.compare("CreatinineProductionRate") == 0) @@ -82,12 +88,14 @@ const SEScalar* SEEnergySystem::GetScalar(const std::string& name) return &GetExerciseMeanArterialPressureDelta(); if (name.compare("FatigueLevel") == 0) return &GetFatigueLevel(); - if (name.compare("KetoneProductionRate") == 0) - return &GetKetoneProductionRate(); if (name.compare("LactateProductionRate") == 0) return &GetLactateProductionRate(); + if (name.compare("PotassiumLostToSweat") == 0) + return &GetPotassiumLostToSweat(); if (name.compare("SkinTemperature") == 0) return &GetSkinTemperature(); + if (name.compare("SodiumLostToSweat") == 0) + return &GetSodiumLostToSweat(); if (name.compare("SweatRate") == 0) return &GetSweatRate(); if (name.compare("TotalMetabolicRate") == 0) @@ -103,6 +111,8 @@ bool SEEnergySystem::Load(const CDM::EnergySystemData& in) if (in.AchievedExerciseLevel().present()) GetAchievedExerciseLevel().Load(in.AchievedExerciseLevel().get()); + if (in.ChlorideLostToSweat().present()) + GetChlorideLostToSweat().Load(in.ChlorideLostToSweat().get()); if (in.CoreTemperature().present()) GetCoreTemperature().Load(in.CoreTemperature().get()); if (in.CreatinineProductionRate().present()) @@ -111,12 +121,14 @@ bool SEEnergySystem::Load(const CDM::EnergySystemData& in) GetExerciseMeanArterialPressureDelta().Load(in.ExerciseMeanArterialPressureDelta().get()); if (in.FatigueLevel().present()) GetFatigueLevel().Load(in.FatigueLevel().get()); - if (in.KetoneProductionRate().present()) - GetKetoneProductionRate().Load(in.KetoneProductionRate().get()); if (in.LactateProductionRate().present()) GetLactateProductionRate().Load(in.LactateProductionRate().get()); + if (in.PotassiumLostToSweat().present()) + GetPotassiumLostToSweat().Load(in.PotassiumLostToSweat().get()); if (in.SkinTemperature().present()) GetSkinTemperature().Load(in.SkinTemperature().get()); + if (in.SodiumLostToSweat().present()) + GetSodiumLostToSweat().Load(in.SodiumLostToSweat().get()); if (in.SweatRate().present()) GetSweatRate().Load(in.SweatRate().get()); if (in.TotalMetabolicRate().present()) @@ -140,6 +152,8 @@ void SEEnergySystem::Unload(CDM::EnergySystemData& data) const if (m_AchievedExerciseLevel != nullptr) data.AchievedExerciseLevel(std::unique_ptr(m_AchievedExerciseLevel->Unload())); + if (m_ChlorideLostToSweat != nullptr) + data.ChlorideLostToSweat(std::unique_ptr(m_ChlorideLostToSweat->Unload())); if (m_CoreTemperature != nullptr) data.CoreTemperature(std::unique_ptr(m_CoreTemperature->Unload())); if (m_CreatinineProductionRate != nullptr) @@ -148,13 +162,15 @@ void SEEnergySystem::Unload(CDM::EnergySystemData& data) const data.ExerciseMeanArterialPressureDelta(std::unique_ptr(m_ExerciseMeanArterialPressureDelta->Unload())); if (m_FatigueLevel != nullptr) data.FatigueLevel(std::unique_ptr(m_FatigueLevel->Unload())); - if (m_KetoneProductionRate != nullptr) - data.KetoneProductionRate(std::unique_ptr(m_KetoneProductionRate->Unload())); if (m_LactateProductionRate != nullptr) data.LactateProductionRate(std::unique_ptr(m_LactateProductionRate->Unload())); + if (m_PotassiumLostToSweat != nullptr) + data.PotassiumLostToSweat(std::unique_ptr(m_PotassiumLostToSweat->Unload())); if (m_SkinTemperature != nullptr) data.SkinTemperature(std::unique_ptr(m_SkinTemperature->Unload())); - if (m_SweatRate != nullptr) + if (m_SodiumLostToSweat != nullptr) + data.SodiumLostToSweat(std::unique_ptr(m_SodiumLostToSweat->Unload())); + if (m_SweatRate != nullptr) data.SweatRate(std::unique_ptr(m_SweatRate->Unload())); if (m_TotalMetabolicRate != nullptr) data.TotalMetabolicRate(std::unique_ptr(m_TotalMetabolicRate->Unload())); @@ -179,6 +195,23 @@ double SEEnergySystem::GetAchievedExerciseLevel() const return m_AchievedExerciseLevel->GetValue(); } +bool SEEnergySystem::HasChlorideLostToSweat() const +{ + return m_ChlorideLostToSweat == nullptr ? false : m_ChlorideLostToSweat->IsValid(); +} +SEScalarMass& SEEnergySystem::GetChlorideLostToSweat() +{ + if (m_ChlorideLostToSweat == nullptr) + m_ChlorideLostToSweat = new SEScalarMass(); + return *m_ChlorideLostToSweat; +} +double SEEnergySystem::GetChlorideLostToSweat(const MassUnit& unit) const +{ + if (m_ChlorideLostToSweat == nullptr) + return SEScalar::dNaN(); + return m_ChlorideLostToSweat->GetValue(unit); +} + bool SEEnergySystem::HasCoreTemperature() const { return m_CoreTemperature == nullptr ? false : m_CoreTemperature->IsValid(); @@ -247,23 +280,6 @@ double SEEnergySystem::GetFatigueLevel() const return m_FatigueLevel->GetValue(); } -bool SEEnergySystem::HasKetoneProductionRate() const -{ - return m_KetoneProductionRate == nullptr ? false : m_KetoneProductionRate->IsValid(); -} -SEScalarAmountPerTime& SEEnergySystem::GetKetoneProductionRate() -{ - if (m_KetoneProductionRate == nullptr) - m_KetoneProductionRate = new SEScalarAmountPerTime(); - return *m_KetoneProductionRate; -} -double SEEnergySystem::GetKetoneProductionRate(const AmountPerTimeUnit& unit) const -{ - if (m_KetoneProductionRate == nullptr) - return SEScalar::dNaN(); - return m_KetoneProductionRate->GetValue(unit); -} - bool SEEnergySystem::HasLactateProductionRate() const { return m_LactateProductionRate == nullptr ? false : m_LactateProductionRate->IsValid(); @@ -281,6 +297,24 @@ double SEEnergySystem::GetLactateProductionRate(const AmountPerTimeUnit& unit) c return m_LactateProductionRate->GetValue(unit); } +bool SEEnergySystem::HasPotassiumLostToSweat() const +{ + return m_PotassiumLostToSweat == nullptr ? false : m_PotassiumLostToSweat->IsValid(); +} +SEScalarMass& SEEnergySystem::GetPotassiumLostToSweat() +{ + if (m_PotassiumLostToSweat == nullptr) + m_PotassiumLostToSweat = new SEScalarMass(); + return *m_PotassiumLostToSweat; +} +double SEEnergySystem::GetPotassiumLostToSweat(const MassUnit& unit) const +{ + if (m_PotassiumLostToSweat == nullptr) + return SEScalar::dNaN(); + return m_PotassiumLostToSweat->GetValue(unit); +} + + bool SEEnergySystem::HasSkinTemperature() const { return m_SkinTemperature == nullptr ? false : m_SkinTemperature->IsValid(); @@ -298,6 +332,23 @@ double SEEnergySystem::GetSkinTemperature(const TemperatureUnit& unit) const return m_SkinTemperature->GetValue(unit); } +bool SEEnergySystem::HasSodiumLostToSweat() const +{ + return m_SodiumLostToSweat == nullptr ? false : m_SodiumLostToSweat->IsValid(); +} +SEScalarMass& SEEnergySystem::GetSodiumLostToSweat() +{ + if (m_SodiumLostToSweat == nullptr) + m_SodiumLostToSweat = new SEScalarMass(); + return *m_SodiumLostToSweat; +} +double SEEnergySystem::GetSodiumLostToSweat(const MassUnit& unit) const +{ + if (m_SodiumLostToSweat == nullptr) + return SEScalar::dNaN(); + return m_SodiumLostToSweat->GetValue(unit); +} + bool SEEnergySystem::HasSweatRate() const { return m_SweatRate == nullptr ? false : m_SweatRate->IsValid(); diff --git a/src/cdm/cpp/system/physiology/SEEnergySystem.h b/src/cdm/cpp/system/physiology/SEEnergySystem.h index dfd6068..27a5fc0 100644 --- a/src/cdm/cpp/system/physiology/SEEnergySystem.h +++ b/src/cdm/cpp/system/physiology/SEEnergySystem.h @@ -34,6 +34,10 @@ class DLL_DECL SEEnergySystem : public SESystem virtual bool HasAchievedExerciseLevel() const; virtual SEScalarFraction& GetAchievedExerciseLevel(); virtual double GetAchievedExerciseLevel() const; + + virtual bool HasChlorideLostToSweat() const; + virtual SEScalarMass& GetChlorideLostToSweat(); + virtual double GetChlorideLostToSweat(const MassUnit& unit) const; virtual bool HasCoreTemperature() const; virtual SEScalarTemperature& GetCoreTemperature(); @@ -50,19 +54,23 @@ class DLL_DECL SEEnergySystem : public SESystem virtual bool HasFatigueLevel() const; virtual SEScalarFraction& GetFatigueLevel(); virtual double GetFatigueLevel() const; - - virtual bool HasKetoneProductionRate() const; - virtual SEScalarAmountPerTime& GetKetoneProductionRate(); - virtual double GetKetoneProductionRate(const AmountPerTimeUnit& unit) const; virtual bool HasLactateProductionRate() const; virtual SEScalarAmountPerTime& GetLactateProductionRate(); virtual double GetLactateProductionRate(const AmountPerTimeUnit& unit) const; + virtual bool HasPotassiumLostToSweat() const; + virtual SEScalarMass& GetPotassiumLostToSweat(); + virtual double GetPotassiumLostToSweat(const MassUnit& unit) const; + virtual bool HasSkinTemperature() const; virtual SEScalarTemperature& GetSkinTemperature(); virtual double GetSkinTemperature(const TemperatureUnit& unit) const; + virtual bool HasSodiumLostToSweat() const; + virtual SEScalarMass& GetSodiumLostToSweat(); + virtual double GetSodiumLostToSweat(const MassUnit& unit) const; + virtual bool HasSweatRate() const; virtual SEScalarMassPerTime& GetSweatRate(); virtual double GetSweatRate(const MassPerTimeUnit& unit) const; @@ -78,13 +86,15 @@ class DLL_DECL SEEnergySystem : public SESystem protected: SEScalarFraction* m_AchievedExerciseLevel; + SEScalarMass* m_ChlorideLostToSweat; SEScalarTemperature* m_CoreTemperature; SEScalarAmountPerTime* m_CreatinineProductionRate; SEScalarPressure* m_ExerciseMeanArterialPressureDelta; SEScalarFraction* m_FatigueLevel; - SEScalarAmountPerTime* m_KetoneProductionRate; SEScalarAmountPerTime* m_LactateProductionRate; + SEScalarMass* m_PotassiumLostToSweat; SEScalarTemperature* m_SkinTemperature; + SEScalarMass* m_SodiumLostToSweat; SEScalarMassPerTime* m_SweatRate; SEScalarPower* m_TotalMetabolicRate; SEScalarFraction* m_TotalWorkRateLevel; diff --git a/src/cdm/cpp/system/physiology/SEHepaticSystem.cpp b/src/cdm/cpp/system/physiology/SEHepaticSystem.cpp index 99c93c8..faea698 100644 --- a/src/cdm/cpp/system/physiology/SEHepaticSystem.cpp +++ b/src/cdm/cpp/system/physiology/SEHepaticSystem.cpp @@ -13,10 +13,15 @@ specific language governing permissions and limitations under the License. #include "stdafx.h" #include "system/physiology/SEHepaticSystem.h" #include "substance/SESubstanceManager.h" +#include "properties/SEScalarMassPerTime.h" +#include "bind/ScalarMassPerTimeData.hxx" +#include "properties/SEScalarAmountPerTime.h" +#include "bind/ScalarAmountPerTimeData.hxx" SEHepaticSystem::SEHepaticSystem(Logger* logger) : SESystem(logger) { - + m_KetoneProductionRate = nullptr; + m_HepaticGluconeogenesisRate = nullptr; } SEHepaticSystem::~SEHepaticSystem() @@ -28,10 +33,17 @@ void SEHepaticSystem::Clear() { SESystem::Clear(); + SAFE_DELETE(m_KetoneProductionRate); + SAFE_DELETE(m_HepaticGluconeogenesisRate); + } const SEScalar* SEHepaticSystem::GetScalar(const std::string& name) { + if (name.compare("KetoneProductionRate") == 0) + return &GetKetoneProductionRate(); + if (name.compare("HepaticGluconeogenesisRate") == 0) + return &GetHepaticGluconeogenesisRate(); return nullptr; } @@ -39,6 +51,11 @@ bool SEHepaticSystem::Load(const CDM::HepaticSystemData& in) { SESystem::Load(in); + if (in.KetoneProductionRate().present()) + GetKetoneProductionRate().Load(in.KetoneProductionRate().get()); + if (in.HepaticGluconeogenesisRate().present()) + GetHepaticGluconeogenesisRate().Load(in.HepaticGluconeogenesisRate().get()); + return true; } CDM::HepaticSystemData* SEHepaticSystem::Unload() const @@ -51,4 +68,43 @@ void SEHepaticSystem::Unload(CDM::HepaticSystemData& data) const { SESystem::Unload(data); + if (m_KetoneProductionRate != nullptr) + data.KetoneProductionRate(std::unique_ptr(m_KetoneProductionRate->Unload())); + if (m_HepaticGluconeogenesisRate != nullptr) + data.HepaticGluconeogenesisRate(std::unique_ptr(m_HepaticGluconeogenesisRate->Unload())); + +} + +bool SEHepaticSystem::HasKetoneProductionRate() const +{ + return m_KetoneProductionRate == nullptr ? false : m_KetoneProductionRate->IsValid(); +} +SEScalarAmountPerTime& SEHepaticSystem::GetKetoneProductionRate() +{ + if (m_KetoneProductionRate == nullptr) + m_KetoneProductionRate = new SEScalarAmountPerTime(); + return *m_KetoneProductionRate; +} +double SEHepaticSystem::GetKetoneProductionRate(const AmountPerTimeUnit& unit) const +{ + if (m_KetoneProductionRate == nullptr) + return SEScalar::dNaN(); + return m_KetoneProductionRate->GetValue(unit); +} + +bool SEHepaticSystem::HasHepaticGluconeogenesisRate() const +{ + return m_HepaticGluconeogenesisRate == nullptr ? false : m_HepaticGluconeogenesisRate->IsValid(); +} +SEScalarMassPerTime& SEHepaticSystem::GetHepaticGluconeogenesisRate() +{ + if (m_HepaticGluconeogenesisRate == nullptr) + m_HepaticGluconeogenesisRate = new SEScalarMassPerTime(); + return *m_HepaticGluconeogenesisRate; +} +double SEHepaticSystem::GetHepaticGluconeogenesisRate(const MassPerTimeUnit& unit) const +{ + if (m_HepaticGluconeogenesisRate == nullptr) + return SEScalar::dNaN(); + return m_HepaticGluconeogenesisRate->GetValue(unit); } diff --git a/src/cdm/cpp/system/physiology/SEHepaticSystem.h b/src/cdm/cpp/system/physiology/SEHepaticSystem.h index a7d5b1e..1312943 100644 --- a/src/cdm/cpp/system/physiology/SEHepaticSystem.h +++ b/src/cdm/cpp/system/physiology/SEHepaticSystem.h @@ -13,8 +13,6 @@ specific language governing permissions and limitations under the License. #pragma once #include "system/SESystem.h" #include "bind/HepaticSystemData.hxx" -#include "bind/enumOnOff.hxx" -#include "system/physiology/SEPupillaryResponse.h" class DLL_DECL SEHepaticSystem : public SESystem { @@ -29,12 +27,23 @@ class DLL_DECL SEHepaticSystem : public SESystem virtual bool Load(const CDM::HepaticSystemData& in); virtual CDM::HepaticSystemData* Unload() const; + protected: virtual void Unload(CDM::HepaticSystemData& data) const; + public: + virtual bool HasKetoneProductionRate() const; + virtual SEScalarAmountPerTime& GetKetoneProductionRate(); + virtual double GetKetoneProductionRate(const AmountPerTimeUnit& unit) const; + + virtual bool HasHepaticGluconeogenesisRate() const; + virtual SEScalarMassPerTime& GetHepaticGluconeogenesisRate(); + virtual double GetHepaticGluconeogenesisRate(const MassPerTimeUnit& unit) const; + protected: - + SEScalarAmountPerTime* m_KetoneProductionRate; + SEScalarMassPerTime* m_HepaticGluconeogenesisRate; }; \ No newline at end of file diff --git a/src/cdm/cpp/system/physiology/SETissueSystem.cpp b/src/cdm/cpp/system/physiology/SETissueSystem.cpp index 1da38f6..8ec0ed8 100644 --- a/src/cdm/cpp/system/physiology/SETissueSystem.cpp +++ b/src/cdm/cpp/system/physiology/SETissueSystem.cpp @@ -15,6 +15,8 @@ specific language governing permissions and limitations under the License. #include "substance/SESubstanceManager.h" #include "properties/SEScalarVolume.h" #include "bind/ScalarVolumeData.hxx" +#include "properties/SEScalarFraction.h" +#include "bind/ScalarFractionData.hxx" #include "properties/SEScalarVolumePerTime.h" #include "bind/ScalarVolumePerTimeData.hxx" #include "properties/SEScalarAmountPerVolume.h" @@ -23,14 +25,18 @@ specific language governing permissions and limitations under the License. #include "bind/ScalarMassPerVolumeData.hxx" #include "properties/SEScalarMass.h" #include "bind/ScalarMassData.hxx" +#include "properties/SEScalarElectricPotential.h" +#include "bind/ScalarElectricPotentialData.hxx" SETissueSystem::SETissueSystem(Logger* logger) : SESystem(logger) { m_CarbonDioxideProductionRate = nullptr; + m_DehydrationFraction = nullptr; m_ExtracellularFluidVolume = nullptr; m_ExtravascularFluidVolume = nullptr; m_IntracellularFluidVolume = nullptr; + m_TotalBodyFluidVolume = nullptr; m_IntracellularFluidPH = nullptr; m_OxygenConsumptionRate = nullptr; m_RespiratoryExchangeRatio = nullptr; @@ -54,9 +60,11 @@ SETissueSystem::~SETissueSystem() void SETissueSystem::Clear() { SAFE_DELETE(m_CarbonDioxideProductionRate); + SAFE_DELETE(m_DehydrationFraction); SAFE_DELETE(m_ExtracellularFluidVolume); SAFE_DELETE(m_ExtravascularFluidVolume); SAFE_DELETE(m_IntracellularFluidVolume); + SAFE_DELETE(m_TotalBodyFluidVolume); SAFE_DELETE(m_IntracellularFluidPH); SAFE_DELETE(m_OxygenConsumptionRate); SAFE_DELETE(m_RespiratoryExchangeRatio); @@ -76,6 +84,8 @@ const SEScalar* SETissueSystem::GetScalar(const std::string& name) { if (name.compare("CarbonDioxideProductionRate") == 0) return &GetCarbonDioxideProductionRate(); + if (name.compare("DehydrationFraction") == 0) + return &GetDehydrationFraction(); if (name.compare("ExtracellularFluidVolume") == 0) return &GetExtracellularFluidVolume(); if (name.compare("ExtravascularFluidVolume") == 0) @@ -84,6 +94,8 @@ const SEScalar* SETissueSystem::GetScalar(const std::string& name) return &GetIntracellularFluidPH(); if (name.compare("IntracellularFluidVolume") == 0) return &GetIntracellularFluidVolume(); + if (name.compare("TotalBodyFluidVolume") == 0) + return &GetTotalBodyFluidVolume(); if (name.compare("OxygenConsumptionRate") == 0) return &GetOxygenConsumptionRate(); if (name.compare("RespiratoryExchangeRatio") == 0) @@ -117,6 +129,8 @@ bool SETissueSystem::Load(const CDM::TissueSystemData& in) SESystem::Load(in); if (in.CarbonDioxideProductionRate().present()) GetCarbonDioxideProductionRate().Load(in.CarbonDioxideProductionRate().get()); + if (in.DehydrationFraction().present()) + GetDehydrationFraction().Load(in.DehydrationFraction().get()); if (in.ExtracellularFluidVolume().present()) GetExtracellularFluidVolume().Load(in.ExtracellularFluidVolume().get()); if (in.ExtravascularFluidVolume().present()) @@ -125,6 +139,8 @@ bool SETissueSystem::Load(const CDM::TissueSystemData& in) GetIntracellularFluidPH().Load(in.IntracellularFluidPH().get()); if (in.IntracellularFluidVolume().present()) GetIntracellularFluidVolume().Load(in.IntracellularFluidVolume().get()); + if (in.TotalBodyFluidVolume().present()) + GetTotalBodyFluidVolume().Load(in.TotalBodyFluidVolume().get()); if (in.OxygenConsumptionRate().present()) GetOxygenConsumptionRate().Load(in.OxygenConsumptionRate().get()); if (in.RespiratoryExchangeRatio().present()) @@ -164,6 +180,8 @@ void SETissueSystem::Unload(CDM::TissueSystemData& data) const { if (m_CarbonDioxideProductionRate != nullptr) data.CarbonDioxideProductionRate(std::unique_ptr(m_CarbonDioxideProductionRate->Unload())); + if (m_DehydrationFraction != nullptr) + data.DehydrationFraction(std::unique_ptr(m_DehydrationFraction->Unload())); if (m_ExtracellularFluidVolume != nullptr) data.ExtracellularFluidVolume(std::unique_ptr(m_ExtracellularFluidVolume->Unload())); if (m_ExtravascularFluidVolume != nullptr) @@ -172,6 +190,8 @@ void SETissueSystem::Unload(CDM::TissueSystemData& data) const data.IntracellularFluidPH(std::unique_ptr(m_IntracellularFluidPH->Unload())); if (m_IntracellularFluidVolume != nullptr) data.IntracellularFluidVolume(std::unique_ptr(m_IntracellularFluidVolume->Unload())); + if (m_TotalBodyFluidVolume != nullptr) + data.TotalBodyFluidVolume(std::unique_ptr(m_TotalBodyFluidVolume->Unload())); if (m_OxygenConsumptionRate != nullptr) data.OxygenConsumptionRate(std::unique_ptr(m_OxygenConsumptionRate->Unload())); if (m_RespiratoryExchangeRatio != nullptr) @@ -217,6 +237,23 @@ double SETissueSystem::GetCarbonDioxideProductionRate(const VolumePerTimeUnit& u return m_CarbonDioxideProductionRate->GetValue(unit); } +bool SETissueSystem::HasDehydrationFraction() const +{ + return m_DehydrationFraction == nullptr ? false : m_DehydrationFraction->IsValid(); +} +SEScalarFraction& SETissueSystem::GetDehydrationFraction() +{ + if (m_DehydrationFraction == nullptr) + m_DehydrationFraction = new SEScalarFraction(); + return *m_DehydrationFraction; +} +double SETissueSystem::GetDehydrationFraction() const +{ + if (m_DehydrationFraction == nullptr) + return SEScalar::dNaN(); + return m_DehydrationFraction->GetValue(); +} + bool SETissueSystem::HasExtracellularFluidVolume() const { return m_ExtracellularFluidVolume == nullptr ? false : m_ExtracellularFluidVolume->IsValid(); @@ -268,6 +305,23 @@ double SETissueSystem::GetIntracellularFluidVolume(const VolumeUnit& unit) const return m_IntracellularFluidVolume->GetValue(unit); } +bool SETissueSystem::HasTotalBodyFluidVolume() const +{ + return m_TotalBodyFluidVolume == nullptr ? false : m_TotalBodyFluidVolume->IsValid(); +} +SEScalarVolume& SETissueSystem::GetTotalBodyFluidVolume() +{ + if (m_TotalBodyFluidVolume == nullptr) + m_TotalBodyFluidVolume = new SEScalarVolume(); + return *m_TotalBodyFluidVolume; +} +double SETissueSystem::GetTotalBodyFluidVolume(const VolumeUnit& unit) const +{ + if (m_TotalBodyFluidVolume == nullptr) + return SEScalar::dNaN(); + return m_TotalBodyFluidVolume->GetValue(unit); +} + bool SETissueSystem::HasIntracellularFluidPH() const { return m_IntracellularFluidPH == nullptr ? false : m_IntracellularFluidPH->IsValid(); diff --git a/src/cdm/cpp/system/physiology/SETissueSystem.h b/src/cdm/cpp/system/physiology/SETissueSystem.h index 948c94f..85d797a 100644 --- a/src/cdm/cpp/system/physiology/SETissueSystem.h +++ b/src/cdm/cpp/system/physiology/SETissueSystem.h @@ -36,6 +36,10 @@ class DLL_DECL SETissueSystem : public SESystem virtual SEScalarVolumePerTime& GetCarbonDioxideProductionRate(); virtual double GetCarbonDioxideProductionRate(const VolumePerTimeUnit& unit) const; + virtual bool HasDehydrationFraction() const; + virtual SEScalarFraction& GetDehydrationFraction(); + virtual double GetDehydrationFraction() const; + virtual bool HasExtracellularFluidVolume() const; virtual SEScalarVolume& GetExtracellularFluidVolume(); virtual double GetExtracellularFluidVolume(const VolumeUnit& unit) const; @@ -48,6 +52,10 @@ class DLL_DECL SETissueSystem : public SESystem virtual SEScalarVolume& GetIntracellularFluidVolume(); virtual double GetIntracellularFluidVolume(const VolumeUnit& unit) const; + virtual bool HasTotalBodyFluidVolume() const; + virtual SEScalarVolume& GetTotalBodyFluidVolume(); + virtual double GetTotalBodyFluidVolume(const VolumeUnit& unit) const; + virtual bool HasIntracellularFluidPH() const; virtual SEScalar& GetIntracellularFluidPH(); virtual double GetIntracellularFluidPH() const; @@ -103,9 +111,11 @@ class DLL_DECL SETissueSystem : public SESystem protected: SEScalarVolumePerTime* m_CarbonDioxideProductionRate; - SEScalarVolume* m_ExtracellularFluidVolume;//planned - SEScalarVolume* m_ExtravascularFluidVolume;//planned - SEScalarVolume* m_IntracellularFluidVolume;//planned + SEScalarFraction* m_DehydrationFraction; + SEScalarVolume* m_ExtracellularFluidVolume; + SEScalarVolume* m_ExtravascularFluidVolume; + SEScalarVolume* m_IntracellularFluidVolume; + SEScalarVolume* m_TotalBodyFluidVolume; SEScalar* m_IntracellularFluidPH; SEScalarVolumePerTime* m_OxygenConsumptionRate; SEScalar* m_RespiratoryExchangeRatio; diff --git a/src/cdm/cpp/utils/GeneralMath.cpp b/src/cdm/cpp/utils/GeneralMath.cpp index 3e375ea..205d2e7 100644 --- a/src/cdm/cpp/utils/GeneralMath.cpp +++ b/src/cdm/cpp/utils/GeneralMath.cpp @@ -199,6 +199,25 @@ void GeneralMath::CalculateSpecificGravity(const SEScalarMass& mass, const SESca specificGravity.SetValue((totalmass_g / volume_mL) / waterDensity_g_mL); } +//-------------------------------------------------------------------------------------------------- +/// \brief +// Calculates the density of water at a given temperature using DIPPR105 equation, valid from 32-707F +//-------------------------------------------------------------------------------------------------- +void GeneralMath::CalculateWaterDensity(const SEScalarTemperature& temp, SEScalarMassPerVolume& density) +{ + //DIPPR105 equation, see http://ddbonline.ddbst.de/DIPPR105DensityCalculation/DIPPR105CalculationCGI.exe + double A = .14395; + double B = .0112; + double C = 649.727; + double D = .05107; + double temp_K = temp.GetValue(TemperatureUnit::K); + + double density_kg_Per_m3 = A / (pow(B, 1 + pow(1 - (temp_K / C), D))); + + density.SetValue(density_kg_Per_m3, MassPerVolumeUnit::kg_Per_m3); + +} + double GeneralMath::PercentDifference(double expected, double calculated) { if (calculated == 0.0&&expected == 0.0) @@ -282,8 +301,8 @@ void GeneralMath::Combinations(std::vector maxValues, std::vector maxValues, std::vector x2) // make sure input x is within bounds set by x1 and x2 - { - // Linear Interpolator requires that input x is within two other data points - yPrime = SEScalar::dNaN(); - return yPrime; - } - slope = (y2 - y1) / (x2 - x1); // linear slope equals dy/dx yInt = y1 - slope*x1; // Y = mx + b -> b = Y - mx diff --git a/src/cdm/cpp/utils/GeneralMath.h b/src/cdm/cpp/utils/GeneralMath.h index 5cb87e7..94c1052 100644 --- a/src/cdm/cpp/utils/GeneralMath.h +++ b/src/cdm/cpp/utils/GeneralMath.h @@ -28,6 +28,7 @@ class DLL_DECL GeneralMath static void CalculateOsmolarity(const SEScalarAmountPerVolume& sodiumMolarity, const SEScalarAmountPerVolume& potassiumMolarity, const SEScalarAmountPerVolume& glucoseMolarity, const SEScalarAmountPerVolume& ureaMolarity, SEScalarOsmolarity& fluidOsmolarity); static void CalculateOsmolality(const SEScalarAmountPerVolume& sodiumMolarity, const SEScalarAmountPerVolume& potassiumMolarity, const SEScalarAmountPerVolume& glucoseMolarity, const SEScalarAmountPerVolume& ureaMolarity, const SEScalar& specificGravity, SEScalarOsmolality& fluidOsmolality); static void CalculateSpecificGravity(const SEScalarMass& mass, const SEScalarVolume& volume, SEScalar& specificGravity, Logger* logger = nullptr); + static void CalculateWaterDensity(const SEScalarTemperature& temp, SEScalarMassPerVolume& density); static void Combinations(std::vector maxValues, std::vector>& permutations); static double LinearInterpolator(double x1, double x2, double y1, double y2, double xPrime); diff --git a/src/cdm/java/mil/tatrc/physiology/datamodel/compartment/SETissueCompartment.java b/src/cdm/java/mil/tatrc/physiology/datamodel/compartment/SETissueCompartment.java index 89cceb1..d2dbac3 100644 --- a/src/cdm/java/mil/tatrc/physiology/datamodel/compartment/SETissueCompartment.java +++ b/src/cdm/java/mil/tatrc/physiology/datamodel/compartment/SETissueCompartment.java @@ -28,6 +28,7 @@ import mil.tatrc.physiology.datamodel.CDMSerializer; import mil.tatrc.physiology.datamodel.bind.TissueCompartmentData; import mil.tatrc.physiology.datamodel.properties.SEScalar; +import mil.tatrc.physiology.datamodel.properties.SEScalarElectricPotential; import mil.tatrc.physiology.datamodel.properties.SEScalarFraction; import mil.tatrc.physiology.datamodel.properties.SEScalarMass; import mil.tatrc.physiology.datamodel.properties.SEScalarMassPerMass; @@ -37,6 +38,7 @@ public class SETissueCompartment extends SECompartment { protected SEScalarMassPerMass acidicPhospohlipidConcentration; protected SEScalarVolume matrixVolume; + protected SEScalarElectricPotential membranePotential; protected SEScalarFraction neutralLipidsVolumeFraction; protected SEScalarFraction neutralPhospholipidsVolumeFraction; protected SEScalar tissueToPlasmaAlbuminRatio; @@ -48,6 +50,7 @@ public SETissueCompartment() { acidicPhospohlipidConcentration = null; matrixVolume = null; + membranePotential = null; neutralLipidsVolumeFraction = null; neutralPhospholipidsVolumeFraction = null; tissueToPlasmaAlbuminRatio = null; @@ -63,6 +66,8 @@ public void reset() acidicPhospohlipidConcentration.invalidate(); if (matrixVolume != null) matrixVolume.invalidate(); + if(membranePotential != null) + membranePotential.invalidate(); if (neutralLipidsVolumeFraction != null) neutralLipidsVolumeFraction.invalidate(); if (neutralPhospholipidsVolumeFraction != null) @@ -84,6 +89,8 @@ public boolean load(TissueCompartmentData in) getAcidicPhospohlipidConcentration().load(in.getAcidicPhospohlipidConcentration()); if(in.getMatrixVolume()!=null) getMatrixVolume().load(in.getMatrixVolume()); + if(in.getMembranePotential()!=null) + getMembranePotential().load(in.getMembranePotential()); if(in.getNeutralLipidsVolumeFraction()!=null) getNeutralLipidsVolumeFraction().load(in.getNeutralLipidsVolumeFraction()); if(in.getNeutralPhospholipidsVolumeFraction()!=null) @@ -114,6 +121,8 @@ protected void unload(TissueCompartmentData data) data.setAcidicPhospohlipidConcentration(this.acidicPhospohlipidConcentration.unload()); if(hasMatrixVolume()) data.setMatrixVolume(this.matrixVolume.unload()); + if (hasMembranePotential()) + data.setMembranePotential(membranePotential.unload()); if(hasNeutralLipidsVolumeFraction()) data.setNeutralLipidsVolumeFraction(this.neutralLipidsVolumeFraction.unload()); if(hasNeutralPhospholipidsVolumeFraction()) @@ -150,6 +159,17 @@ public boolean hasMatrixVolume() return matrixVolume == null ? false : matrixVolume.isValid(); } + public boolean hasMembranePotential() + { + return membranePotential == null ? false : membranePotential.isValid(); + } + public SEScalarElectricPotential getMembranePotential() + { + if (membranePotential == null) + membranePotential = new SEScalarElectricPotential(); + return membranePotential; + } + public SEScalarFraction getNeutralLipidsVolumeFraction() { if (neutralLipidsVolumeFraction == null) diff --git a/src/cdm/java/mil/tatrc/physiology/datamodel/dataset/DataSetReader.java b/src/cdm/java/mil/tatrc/physiology/datamodel/dataset/DataSetReader.java index f43ec3d..941e491 100644 --- a/src/cdm/java/mil/tatrc/physiology/datamodel/dataset/DataSetReader.java +++ b/src/cdm/java/mil/tatrc/physiology/datamodel/dataset/DataSetReader.java @@ -502,6 +502,11 @@ protected static boolean setProperty(SESubstance substance, substance.setName(value); return true; } + if(property.equals("Classification")) + { + substance.setSubClass(EnumSubstanceClass.fromValue(value)); + return true; + } if(property.equals("State")) { substance.setState(EnumSubstanceState.fromValue(value)); @@ -528,6 +533,11 @@ protected static boolean setProperty(SESubstance substance, substance.getMichaelisCoefficient().setValue(Double.parseDouble(value),unit); return true; } + if(property.equals("MembraneResistance")) + { + substance.getMembraneResistance().setValue(Double.parseDouble(value),unit); + return true; + } // Aerosolization if(property.equals("BronchioleModifier")) { diff --git a/src/cdm/java/mil/tatrc/physiology/datamodel/patient/SEPatient.java b/src/cdm/java/mil/tatrc/physiology/datamodel/patient/SEPatient.java index 8637ef7..5af57ac 100644 --- a/src/cdm/java/mil/tatrc/physiology/datamodel/patient/SEPatient.java +++ b/src/cdm/java/mil/tatrc/physiology/datamodel/patient/SEPatient.java @@ -29,6 +29,7 @@ public class SEPatient protected SEScalarMassPerVolume bodyDensity; protected SEScalarFraction bodyFatFraction; protected SEScalarMass leanBodyMass; + protected SEScalarMass muscleMass; protected SEScalarArea alveoliSurfaceArea; protected SEScalarFraction rightLungRatio; @@ -76,6 +77,8 @@ public void reset() this.bodyFatFraction.invalidate(); if(leanBodyMass != null) this.leanBodyMass.invalidate(); + if(muscleMass != null) + this.muscleMass.invalidate(); if(alveoliSurfaceArea != null) alveoliSurfaceArea.invalidate(); @@ -143,6 +146,8 @@ public boolean load(PatientData data) this.getBodyFatFraction().load(data.getBodyFatFraction()); if(data.getLeanBodyMass()!=null) this.getLeanBodyMass().load(data.getLeanBodyMass()); + if(data.getMuscleMass()!=null) + this.getMuscleMass().load(data.getMuscleMass()); if(data.getAlveoliSurfaceArea() != null) getAlveoliSurfaceArea().load(data.getAlveoliSurfaceArea()); @@ -215,6 +220,8 @@ protected void unload(PatientData to) to.setBodyFatFraction(this.bodyFatFraction.unload()); if(hasLeanBodyMass()) to.setLeanBodyMass(this.leanBodyMass.unload()); + if(hasMuscleMass()) + to.setMuscleMass(this.muscleMass.unload()); if(hasAlveoliSurfaceArea()) to.setAlveoliSurfaceArea(alveoliSurfaceArea.unload()); @@ -423,6 +430,14 @@ public SEScalarMass getLeanBodyMass() } public boolean hasLeanBodyMass() {return this.leanBodyMass==null?false:this.leanBodyMass.isValid();} + public SEScalarMass getMuscleMass() + { + if(this.muscleMass==null) + this.muscleMass=new SEScalarMass(); + return this.muscleMass; + } + public boolean hasMuscleMass() {return this.muscleMass==null?false:this.muscleMass.isValid();} + public SEScalarPressure getMeanArterialPressureBaseline() { if(this.meanArterialPressureBaseline==null) diff --git a/src/cdm/java/mil/tatrc/physiology/datamodel/patient/actions/SEHemorrhage.java b/src/cdm/java/mil/tatrc/physiology/datamodel/patient/actions/SEHemorrhage.java index e6e2cd2..b63eb9b 100644 --- a/src/cdm/java/mil/tatrc/physiology/datamodel/patient/actions/SEHemorrhage.java +++ b/src/cdm/java/mil/tatrc/physiology/datamodel/patient/actions/SEHemorrhage.java @@ -16,20 +16,22 @@ import java.util.*; import mil.tatrc.physiology.datamodel.CDMSerializer; import mil.tatrc.physiology.datamodel.bind.HemorrhageData; -import mil.tatrc.physiology.datamodel.bind.IntegerArray; +import mil.tatrc.physiology.datamodel.properties.SEScalar0To1; public class SEHemorrhage extends SEPatientAction { + //Required to specify a hemorrhage action protected String compartment; - protected IntegerArray mcis; - protected String bleedname; - Map,String> organMap = new HashMap,String>(); + protected SEScalar0To1 severity; + protected List mcis; + Map> organMap = new HashMap>(); + public SEHemorrhage() { compartment = null; mcis = null; - bleedname=null; + severity = null; makeOrganMap(organMap); } @@ -39,28 +41,33 @@ public void copy(SEHemorrhage other) return; super.copy(other); mcis = other.mcis; - + compartment = other.compartment; + severity = other.severity; } public void reset() { super.reset(); - compartment = null; - mcis = null; - + if(severity!=null) + this.severity.invalidate(); + this.compartment=null; + this.mcis = null; } public boolean isValid() { - return hasMCIS(); + return hasCompartment() && hasSeverity(); } public boolean load(HemorrhageData in) { super.load(in); - mcis = in.getMCIS(); - setCompartment(); - return isValid(); + this.compartment=in.getCompartment(); + this.getSeverity().load(in.getSeverity()); + + this.setMCIS(); + + return isValid(); } public HemorrhageData unload() @@ -73,74 +80,104 @@ public HemorrhageData unload() protected void unload(HemorrhageData data) { super.unload(data); - if(mcis!=null) - data.setMCIS(getMCIS()); + if(this.severity!=null) + data.setSeverity(severity.unload()); + if(this.compartment!=null) + data.setCompartment(compartment); } - public IntegerArray getMCIS() +// Standard Get/Has and ToString methods needed for all BioGears Patient Actions + public String getCompartment() { - if(mcis==null) - mcis = new IntegerArray(); - return mcis; + return compartment; + } + public boolean hasCompartment() + { + return compartment==null?false:true; } - public boolean hasMCIS() + public void setCompartment(String compartment) { - return mcis == null ? false : !mcis.getIntegerList().isEmpty(); + this.compartment = compartment; + this.setMCIS(); } - public String getCompartment() + public SEScalar0To1 getSeverity() { - return compartment; + if (severity==null) + { + severity = new SEScalar0To1(); + } + return severity; + } + public boolean hasSeverity() + { + return severity==null?false:true; } - public void setCompartment() + + + + private void setMCIS() { - int region = mcis.getIntegerList().get(1); + int sev = (int)Math.ceil(5.0*this.severity.getValue()); + + this.mcis = new ArrayList(); + this.mcis.add(0,sev); - switch(region){ - case 1: - if(mcis.getIntegerList().get(3)==1) - compartment = "Head"; - else - compartment = "Major Artery"; + switch(compartment){ + case "Arm": + this.mcis.addAll(new ArrayList(Arrays.asList(3,0,0,0))); break; - case 2: - if(organMap.containsKey(mcis.getIntegerList().subList(2, 4))) - compartment=organMap.get(mcis.getIntegerList().subList(2, 4)); - else - compartment="Major Artery"; + case "Leg": + this.mcis.addAll(new ArrayList(Arrays.asList(4,0,0,0))); break; - case 3: - compartment = "Arm"; + case "Major Artery": + this.mcis.addAll(new ArrayList(Arrays.asList(2,6,4,0))); break; - case 4: - compartment = "Leg"; + case "Head": + this.mcis.addAll(new ArrayList(Arrays.asList(2,6,1,0))); break; default: - compartment = "Major Artery"; - break; - } + //If we get in here, then we it's one of the comparments in the torso and it's easier to just map the rest + this.mcis.add(2); + this.mcis.addAll(organMap.get(compartment)); + this.mcis.add(0); + break; + } + if(mcis.size()<5) + { + //If we can't find it, assign it the code for aorta + this.mcis.addAll(new ArrayList(Arrays.asList(2,6,4,0))); + } + } + + private void makeOrganMap(Map> organs) + { + organs.put("Vena Cava", new ArrayList(Arrays.asList(6,6))); + organs.put("Lung", new ArrayList(Arrays.asList(7,1))); + organs.put("Heart", new ArrayList(Arrays.asList(7,2))); + organs.put("Liver", new ArrayList(Arrays.asList(8,1))); + organs.put("Spleen", new ArrayList(Arrays.asList(8,2))); + organs.put("Splanchnic", new ArrayList(Arrays.asList(8,3))); + organs.put("Kidney", new ArrayList(Arrays.asList(8,4))); + organs.put("Small Intestine", new ArrayList(Arrays.asList(8,5))); + organs.put("Large Intestine", new ArrayList(Arrays.asList(8,6))); } - private void makeOrganMap(Map, String> organs) - { - organs.put(new ArrayList(Arrays.asList(6,4)), "Major Artery"); - organs.put(new ArrayList(Arrays.asList(6,6)), "Vena Cava"); - organs.put(new ArrayList(Arrays.asList(6,5)), "Major Artery"); - organs.put(new ArrayList(Arrays.asList(7,1)), "Lungs"); - organs.put(new ArrayList(Arrays.asList(7,2)), "Heart"); - organs.put(new ArrayList(Arrays.asList(8,1)), "Liver"); - organs.put(new ArrayList(Arrays.asList(8,2)), "Spleen"); - organs.put(new ArrayList(Arrays.asList(8,3)), "Splanchnic"); - organs.put(new ArrayList(Arrays.asList(8,4)), "Kidney"); - organs.put(new ArrayList(Arrays.asList(8,5)), "Small Intestine"); - organs.put(new ArrayList(Arrays.asList(8,6)), "Large Intestine"); + private String mcisToString() + { + String str = ""; + for (int i=0; i compare(String expectedFilePath, String computedFilePath) suite.startCase(this.name); // Set up the Compare Options SEEqualOptions opts = suite.getCaseEqualOptions(); + this.limit = 5.0; //changing this a percentage point TODO: fix issues with oxygen production/cO2 consumption opts.setPercentDifference(this.limit); opts.trackError(true); opts.trackDifferences(false); diff --git a/src/engine/cpp/Controller/BioGears.cpp b/src/engine/cpp/Controller/BioGears.cpp index 2817902..0bf012d 100644 --- a/src/engine/cpp/Controller/BioGears.cpp +++ b/src/engine/cpp/Controller/BioGears.cpp @@ -395,6 +395,23 @@ bool BioGears::SetupPatient() ss << "Patient lean body mass computed and set to " << leanBodyMass_kg << " kg."; Info(ss); + //Muscle Mass --------------------------------------------------------------- + // \cite janssen2000skeletal + if (m_Patient->HasMuscleMass()) + { + ss << "Patient muscle mass cannot be set directly. It is determined by a percentage of weight."; + Error(ss); + err = true; + } + + if (m_Patient->GetSex() == CDM::enumSex::Female) + m_Patient->GetMuscleMass().SetValue(weight_kg * .306, MassUnit::kg); + else + m_Patient->GetMuscleMass().SetValue(weight_kg * .384, MassUnit::kg); + + ss << "Patient muscle mass computed and set to " << m_Patient->GetMuscleMass().GetValue(MassUnit::kg) << " kg."; + Info(ss); + //Body Density --------------------------------------------------------------- if (m_Patient->HasBodyDensity()) { @@ -1494,7 +1511,7 @@ void BioGears::SetupCardiovascular() LiverBleed.GetResistanceBaseline().SetValue(m_Config->GetDefaultOpenFlowResistance(FlowResistanceUnit::mmHg_s_Per_mL), FlowResistanceUnit::mmHg_s_Per_mL); SEFluidCircuitPath& PortalBleed = cCardiovascular.CreatePath(PortalVein, Ground, BGE::CardiovascularPath::PortalBleed); PortalBleed.GetResistanceBaseline().SetValue(m_Config->GetDefaultOpenFlowResistance(FlowResistanceUnit::mmHg_s_Per_mL), FlowResistanceUnit::mmHg_min_Per_L); - //Set up kidney hemorrhage path in renal circuit + //Note: kidney hemorrhage path is set below in renal circuit SEFluidCircuitPath& LegBleed = cCardiovascular.CreatePath(RightLeg1, Ground, BGE::CardiovascularPath::LegBleed); LegBleed.GetResistanceBaseline().SetValue(m_Config->GetDefaultOpenFlowResistance(FlowResistanceUnit::mmHg_s_Per_mL), FlowResistanceUnit::mmHg_s_Per_mL); @@ -2035,8 +2052,9 @@ void BioGears::SetupRenal() ///// Circuit Parameters////// double openSwitch_mmHg_s_Per_mL = m_Config->GetDefaultOpenFlowResistance(FlowResistanceUnit::mmHg_s_Per_mL); //Resistances with some tuning multipliers - double urineTuningMultiplier = 0.50; - double arteryTuningMultiplier = 1.2; + double urineTuningMultiplier = 0.58; + double arteryTuningMultiplier = 0.8; + double reabsorptionTuningMultiplier = 0.8; double renalArteryResistance_mmHg_s_Per_mL = Convert(0.0250 * arteryTuningMultiplier, FlowResistanceUnit::mmHg_min_Per_mL, FlowResistanceUnit::mmHg_s_Per_mL); double afferentResistance_mmHg_s_Per_mL = Convert(0.0417, FlowResistanceUnit::mmHg_min_Per_mL, FlowResistanceUnit::mmHg_s_Per_mL); @@ -2046,9 +2064,9 @@ void BioGears::SetupRenal() double renalVeinResistance_mmHg_s_Per_mL = Convert(0.0066, FlowResistanceUnit::mmHg_min_Per_mL, FlowResistanceUnit::mmHg_s_Per_mL); double glomerularFilterResistance_mmHg_s_Per_mL = Convert(0.1600 * urineTuningMultiplier, FlowResistanceUnit::mmHg_min_Per_mL, FlowResistanceUnit::mmHg_s_Per_mL); double tubulesResistance_mmHg_s_Per_mL = Convert(0.1920 * urineTuningMultiplier, FlowResistanceUnit::mmHg_min_Per_mL, FlowResistanceUnit::mmHg_s_Per_mL); - double reabsoprtionResistance_mmHg_s_Per_mL = Convert(0.1613 * urineTuningMultiplier, FlowResistanceUnit::mmHg_min_Per_mL, FlowResistanceUnit::mmHg_s_Per_mL); + double reabsoprtionResistance_mmHg_s_Per_mL = Convert(0.1613 * reabsorptionTuningMultiplier, FlowResistanceUnit::mmHg_min_Per_mL, FlowResistanceUnit::mmHg_s_Per_mL); //This one is tuned - double ureterTuningMultiplier = 0.48; + double ureterTuningMultiplier = 0.59; double ureterResistance_mmHg_s_Per_mL = Convert(30.0*ureterTuningMultiplier, FlowResistanceUnit::mmHg_min_Per_mL, FlowResistanceUnit::mmHg_s_Per_mL); double urethraResistance_mmHg_s_Per_mL = openSwitch_mmHg_s_Per_mL; //Compliances @@ -2995,6 +3013,7 @@ void BioGears::SetupTissue() FatTissue.GetTissueToPlasmaAlphaAcidGlycoproteinRatio().SetValue(AdiposeAAGRatio); FatTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(AdiposeLRatio); FatTissue.GetTotalMass().SetValue(AdiposeTissueMass, MassUnit::kg); + FatTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& FatVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::Fat), FatExtracellular, BGE::VascularLink::FatVascularToTissue); @@ -3042,6 +3061,7 @@ void BioGears::SetupTissue() BoneTissue.GetTissueToPlasmaAlbuminRatio().SetValue(BoneARatio); BoneTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(BoneLRatio); BoneTissue.GetTotalMass().SetValue(BoneTissueMass, MassUnit::kg); + BoneTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& BoneVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::Bone), BoneExtracellular, BGE::VascularLink::BoneVascularToTissue); @@ -3089,6 +3109,7 @@ void BioGears::SetupTissue() BrainTissue.GetTissueToPlasmaAlbuminRatio().SetValue(BrainARatio); BrainTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(BrainLRatio); BrainTissue.GetTotalMass().SetValue(BrainTissueMass, MassUnit::kg); + BrainTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& BrainVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::Brain), BrainExtracellular, BGE::VascularLink::BrainVascularToTissue); @@ -3142,6 +3163,7 @@ void BioGears::SetupTissue() GutTissue.GetTissueToPlasmaAlbuminRatio().SetValue(GutARatio); GutTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(GutLRatio); GutTissue.GetTotalMass().SetValue(GutTissueMass, MassUnit::kg); + GutTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& SmallIntestineVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::SmallIntestine), GutExtracellular, BGE::VascularLink::SmallIntestineVascularToTissue); @@ -3202,6 +3224,7 @@ void BioGears::SetupTissue() LeftKidneyTissue.GetTissueToPlasmaAlbuminRatio().SetValue(LKidneyARatio); LeftKidneyTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(LKidneyLRatio); LeftKidneyTissue.GetTotalMass().SetValue(LKidneyTissueMass, MassUnit::kg); + LeftKidneyTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& LeftKidneyVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::LeftKidney), LeftKidneyExtracellular, BGE::VascularLink::LeftKidneyVascularToTissue); @@ -3249,6 +3272,7 @@ void BioGears::SetupTissue() LeftLungTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(LLungLRatio); LeftLungTissue.GetTissueToPlasmaAlphaAcidGlycoproteinRatio().SetValue(LLungAAGRatio); LeftLungTissue.GetTotalMass().SetValue(LLungTissueMass, MassUnit::kg); + LeftLungTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& LeftLungVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::LeftLung), LeftLungExtracellular, BGE::VascularLink::LeftLungVascularToTissue); @@ -3296,6 +3320,7 @@ void BioGears::SetupTissue() LiverTissue.GetTissueToPlasmaAlbuminRatio().SetValue(LiverARatio); LiverTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(LiverLRatio); LiverTissue.GetTotalMass().SetValue(LiverTissueMass, MassUnit::kg); + LiverTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& LiverVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::Liver), LiverExtracellular, BGE::VascularLink::LiverVascularToTissue); @@ -3343,6 +3368,7 @@ void BioGears::SetupTissue() MuscleTissue.GetTissueToPlasmaAlbuminRatio().SetValue(MuscleARatio); MuscleTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(MuscleLRatio); MuscleTissue.GetTotalMass().SetValue(MuscleTissueMass, MassUnit::kg); + MuscleTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& MuscleVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::Muscle), MuscleExtracellular, BGE::VascularLink::MuscleVascularToTissue); @@ -3390,6 +3416,7 @@ void BioGears::SetupTissue() MyocardiumTissue.GetTissueToPlasmaAlbuminRatio().SetValue(MyocardiumARatio); MyocardiumTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(MyocardiumLRatio); MyocardiumTissue.GetTotalMass().SetValue(MyocardiumTissueMass, MassUnit::kg); + MyocardiumTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& MyocardiumVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::Myocardium), MyocardiumExtracellular, BGE::VascularLink::MyocardiumVascularToTissue); @@ -3442,6 +3469,7 @@ void BioGears::SetupTissue() RightKidneyTissue.GetTissueToPlasmaAlbuminRatio().SetValue(RKidneyARatio); RightKidneyTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(RKidneyLRatio); RightKidneyTissue.GetTotalMass().SetValue(RKidneyTissueMass, MassUnit::kg); + RightKidneyTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& RightKidneyVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::RightKidney), RightKidneyExtracellular, BGE::VascularLink::RightKidneyVascularToTissue); @@ -3489,6 +3517,7 @@ void BioGears::SetupTissue() RightLungTissue.GetTissueToPlasmaAlbuminRatio().SetValue(RLungARatio); RightLungTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(RLungLRatio); RightLungTissue.GetTotalMass().SetValue(RLungTissueMass, MassUnit::kg); + RightLungTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& RightLungVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::RightLung), RightLungExtracellular, BGE::VascularLink::RightLungVascularToTissue); @@ -3539,6 +3568,7 @@ void BioGears::SetupTissue() SkinTissue.GetTissueToPlasmaAlbuminRatio().SetValue(SkinARatio); SkinTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(SkinLRatio); SkinTissue.GetTotalMass().SetValue(SkinTissueMass, MassUnit::kg); + SkinTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& SkinVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::Skin), SkinExtracellular, BGE::VascularLink::SkinVascularToTissue); @@ -3586,6 +3616,7 @@ void BioGears::SetupTissue() SpleenTissue.GetTissueToPlasmaAlbuminRatio().SetValue(SpleenARatio); SpleenTissue.GetTissueToPlasmaLipoproteinRatio().SetValue(SpleenLRatio); SpleenTissue.GetTotalMass().SetValue(SpleenTissueMass, MassUnit::kg); + SpleenTissue.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); SELiquidCompartmentLink& SpleenVascularToTissue = m_Compartments->CreateLiquidLink(*m_Compartments->GetLiquidCompartment(BGE::VascularCompartment::Spleen), SpleenExtracellular, BGE::VascularLink::SpleenVascularToTissue); @@ -3609,11 +3640,11 @@ void BioGears::SetupRespiratory() cRespiratory.AddReferenceNode(*Ambient); //Tuning parameters - double AlveoliCompliance = 0.037; - double DeadSpaceCompliance = 0.014; + double AlveoliCompliance = 0.037; //0.025 + double DeadSpaceCompliance = 0.014;//0.016; //This is the min compliance when the volume is the baseline volume, since it scales with volume double ChestWallCompliance = 0.004; - double TotalAirwayResistance = 1.5; + double TotalAirwayResistance = 1.5;//0.95; double UnstressedDeadSpaceVolume = 0.001; //Should add up to 100% of total airway resistance @@ -3627,8 +3658,8 @@ void BioGears::SetupRespiratory() double AlveoliDuctResistance = 2 * (TotalAirwayResistance - TracheaResistance) - BronchiResistance; //Values from standard - double FunctionalResidualCapacity_L = 2.313; - double LungResidualVolume_L = 1.234; + double FunctionalResidualCapacity_L = 2.313; //2.5; + double LungResidualVolume_L = 1.234;//1.45; double DefaultRespDrivePressure = -55.0; //This shouldn't really matter, since the pressure source is set in the Respiratory System double AmbientPresure = 1033.23; // = 1 atm diff --git a/src/engine/cpp/Controller/BioGearsConfiguration.cpp b/src/engine/cpp/Controller/BioGearsConfiguration.cpp index 5f1b891..390cd6e 100644 --- a/src/engine/cpp/Controller/BioGearsConfiguration.cpp +++ b/src/engine/cpp/Controller/BioGearsConfiguration.cpp @@ -411,7 +411,7 @@ void BioGearsConfiguration::Initialize() GetMolarMassOfDryAir().SetValue(0.028964, MassPerAmountUnit::kg_Per_mol); GetMolarMassOfWaterVapor().SetValue(0.018016, MassPerAmountUnit::kg_Per_mol); GetInitialEnvironmentalConditions().LoadFile("./environments/Standard.xml"); - GetWaterDensity().SetValue(1000, MassPerVolumeUnit::kg_Per_m3); + GetWaterDensity().SetValue(1000, MassPerVolumeUnit::kg_Per_m3); //Because water density changes with temperature, and this refers to room temperature water, you should use GeneralMath::CalculateWaterDensity() instead // Gastrointestinal GetCalciumAbsorptionFraction().SetValue(0.25);// Net fractional calcium absorption is 24.9 ± 12.4% (Hunt and Johnson 2007) @@ -628,8 +628,6 @@ bool BioGearsConfiguration::Load(const CDM::BioGearsConfigurationData& in) const CDM::EnergyConfigurationData& config = in.EnergyConfiguration().get(); if (config.BodySpecificHeat().present()) GetBodySpecificHeat().Load(config.BodySpecificHeat().get()); - if (config.CarbonDioxideProductionFromOxygenConsumptionConstant().present()) - GetCarbondDioxideProductionFromOxygenConsumptionConstant().Load(config.CarbonDioxideProductionFromOxygenConsumptionConstant().get()); if (config.CoreTemperatureLow().present()) GetCoreTemperatureLow().Load(config.CoreTemperatureLow().get()); if (config.CoreTemperatureHigh().present()) @@ -924,8 +922,6 @@ void BioGearsConfiguration::Unload(CDM::BioGearsConfigurationData& data) const CDM::EnergyConfigurationData* energy(new CDM::EnergyConfigurationData()); if (HasBodySpecificHeat()) energy->BodySpecificHeat(std::unique_ptr(m_BodySpecificHeat->Unload())); - if (HasCarbondDioxideProductionFromOxygenConsumptionConstant()) - energy->CarbonDioxideProductionFromOxygenConsumptionConstant(std::unique_ptr(m_CarbondDioxideProductionFromOxygenConsumptionConstant->Unload())); if (HasCoreTemperatureLow()) energy->CoreTemperatureLow(std::unique_ptr(m_CoreTemperatureLow->Unload())); if (HasCoreTemperatureHigh()) diff --git a/src/engine/cpp/Controller/BioGearsSubstances.cpp b/src/engine/cpp/Controller/BioGearsSubstances.cpp index 09ce1d4..3abe332 100644 --- a/src/engine/cpp/Controller/BioGearsSubstances.cpp +++ b/src/engine/cpp/Controller/BioGearsSubstances.cpp @@ -480,15 +480,19 @@ void BioGearsSubstances::InitializeLiquidCompartmentNonGases() concentration.SetValue(50.0, MassPerVolumeUnit::mg_Per_dL); SetSubstanceConcentration(*m_aminoAcids, vascular, concentration); // Tissue - molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L)/m_aminoAcids->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); + molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L)/ m_aminoAcids->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); SetSubstanceMolarity(*m_aminoAcids, tissue, molarity1, molarity1); // BICARBONATE IS SET IN GASES SECTION // // CALCIUM // - //concentration.SetValue(48.1, MassPerVolumeUnit::mg_Per_L); - concentration.SetValue(88.1, MassPerVolumeUnit::mg_Per_L); + concentration.SetValue(48.1, MassPerVolumeUnit::mg_Per_L); SetSubstanceConcentration(*m_calcium, vascular, concentration); + + // Tissue + molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L) / m_calcium->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); + molarity2.SetValue(0.0001, AmountPerVolumeUnit::mmol_Per_L); + SetSubstanceMolarity(*m_calcium, tissue, molarity1, molarity2); // Set Urine concentration.SetValue(98.1, MassPerVolumeUnit::mg_Per_L); subQ = leftBowmansCapsules->GetSubstanceQuantity(*m_calcium); @@ -511,12 +515,8 @@ void BioGearsSubstances::InitializeLiquidCompartmentNonGases() subQ->Balance(BalanceLiquidBy::Concentration); subQ = bladder->GetSubstanceQuantity(*m_calcium); subQ->GetConcentration().Set(concentration); - // Tissue - molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L) / m_aminoAcids->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); - //molarity1.SetValue(1.2, AmountPerVolumeUnit::mmol_Per_L); - //molarity2.SetValue(1.2, AmountPerVolumeUnit::mmol_Per_L); - //molarity2.SetValue(0.0001, AmountPerVolumeUnit::mmol_Per_L); - SetSubstanceMolarity(*m_calcium, tissue, molarity1, molarity1); + subQ->Balance(BalanceLiquidBy::Concentration); + // CHLORIDE // concentration.SetValue(0.362, MassPerVolumeUnit::g_Per_dL); @@ -524,8 +524,7 @@ void BioGearsSubstances::InitializeLiquidCompartmentNonGases() SetSubstanceConcentration(*m_chloride, urine, concentration); // Tissue molarity1.SetValue(116, AmountPerVolumeUnit::mmol_Per_L); - molarity2.SetValue(116, AmountPerVolumeUnit::mmol_Per_L); - //molarity2.SetValue(20, AmountPerVolumeUnit::mmol_Per_L); + molarity2.SetValue(20, AmountPerVolumeUnit::mmol_Per_L); SetSubstanceMolarity(*m_chloride, tissue, molarity1, molarity2); // CREATININE // @@ -598,7 +597,7 @@ void BioGearsSubstances::InitializeLiquidCompartmentNonGases() SetSubstanceMolarity(*m_glucose, tissue, molarity1, molarity1); // INSULIN // - concentration.SetValue(0.85, MassPerVolumeUnit::ug_Per_L); //118.1 pmol/L is desired (.6859 ug/L), but it dips during stabilization, so start higher + concentration.SetValue(0.85, MassPerVolumeUnit::ug_Per_L); //118.1 pmol/L is desired (.6859 ug/L), was .85 because of stabilization dip, but it seems okay now SetSubstanceConcentration(*m_insulin, vascular, concentration); // None in Urine leftBowmansCapsules->GetSubstanceQuantity(*m_insulin)->SetToZero(); @@ -661,18 +660,6 @@ void BioGearsSubstances::InitializeLiquidCompartmentNonGases() SetSubstanceConcentration(*m_potassium, vascular, concentration); // Set Urine concentration.SetValue(4.5, MassPerVolumeUnit::g_Per_L); - subQ = leftBowmansCapsules->GetSubstanceQuantity(*m_potassium); - subQ->GetConcentration().Set(concentration); - subQ->Balance(BalanceLiquidBy::Concentration); - subQ = rightBowmansCapsules->GetSubstanceQuantity(*m_potassium); - subQ->GetConcentration().Set(concentration); - subQ->Balance(BalanceLiquidBy::Concentration); - subQ = leftTubules->GetSubstanceQuantity(*m_potassium); - subQ->GetConcentration().Set(concentration); - subQ->Balance(BalanceLiquidBy::Concentration); - subQ = rightTubules->GetSubstanceQuantity(*m_potassium); - subQ->GetConcentration().Set(concentration); - subQ->Balance(BalanceLiquidBy::Concentration); subQ = leftUreter->GetSubstanceQuantity(*m_potassium); subQ->GetConcentration().Set(concentration); subQ->Balance(BalanceLiquidBy::Concentration); @@ -683,27 +670,14 @@ void BioGearsSubstances::InitializeLiquidCompartmentNonGases() subQ->GetConcentration().Set(concentration); // Tissue molarity1.SetValue(4.5, AmountPerVolumeUnit::mmol_Per_L); - //molarity2.SetValue(120, AmountPerVolumeUnit::mmol_Per_L); - molarity2.SetValue(4.5, AmountPerVolumeUnit::mmol_Per_L); + molarity2.SetValue(120, AmountPerVolumeUnit::mmol_Per_L); SetSubstanceMolarity(*m_potassium, tissue, molarity1, molarity2); // SODIUM // concentration.SetValue(0.323, MassPerVolumeUnit::g_Per_dL); SetSubstanceConcentration(*m_sodium, vascular, concentration); // Set Urine - subQ = leftBowmansCapsules->GetSubstanceQuantity(*m_sodium); - subQ->GetConcentration().Set(concentration); - subQ->Balance(BalanceLiquidBy::Concentration); - subQ = rightBowmansCapsules->GetSubstanceQuantity(*m_sodium); - subQ->GetConcentration().Set(concentration); - subQ->Balance(BalanceLiquidBy::Concentration); concentration.SetValue(0.375, MassPerVolumeUnit::g_Per_dL); - subQ = leftTubules->GetSubstanceQuantity(*m_sodium); - subQ->GetConcentration().Set(concentration); - subQ->Balance(BalanceLiquidBy::Concentration); - subQ = rightTubules->GetSubstanceQuantity(*m_sodium); - subQ->GetConcentration().Set(concentration); - subQ->Balance(BalanceLiquidBy::Concentration); subQ = leftUreter->GetSubstanceQuantity(*m_sodium); subQ->GetConcentration().Set(concentration); subQ->Balance(BalanceLiquidBy::Concentration); @@ -714,8 +688,7 @@ void BioGearsSubstances::InitializeLiquidCompartmentNonGases() subQ->GetConcentration().Set(concentration); // Tissue molarity1.SetValue(145, AmountPerVolumeUnit::mmol_Per_L); - molarity2.SetValue(145, AmountPerVolumeUnit::mmol_Per_L); - //molarity2.SetValue(15, AmountPerVolumeUnit::mmol_Per_L); + molarity2.SetValue(15, AmountPerVolumeUnit::mmol_Per_L); SetSubstanceMolarity(*m_sodium, tissue, molarity1, molarity2); // TRIACYLGLYCEROL // @@ -772,6 +745,129 @@ void BioGearsSubstances::InitializeLiquidCompartmentNonGases() SetSubstanceMolarity(*m_urea, tissue, molarity1); } +//-------------------------------------------------------------------------------------------------- +/// \brief +/// Sets the status of blood concentrations to appropriate starved values +/// +/// \details +/// The blood concentrations of glucose and ketones are set to match literature values. Insulin and +/// glucagon are not set because they react to set glucose quickly. Other metabolites are not set, +/// but they could be in the future if appropriate validation data is found. +//-------------------------------------------------------------------------------------------------- +void BioGearsSubstances::SetLiquidCompartmentNonGasesForStarvation(double time_h) +{ + //This function copies InitializeLiquidCompartmentNonGases() in form and is called + //from Tissue::SetStarvationState() to configure blood and tissue concentrations during + //the Starvation condition (urine compartments are not currently considered) + + const std::vector& vascular = m_data.GetCompartments().GetVascularLeafCompartments(); + const std::vector& tissue = m_data.GetCompartments().GetTissueLeafCompartments(); + + // Initialize Substances throughout the body + SEScalarMassPerVolume concentration; + SEScalarAmountPerVolume molarity1; + SEScalarAmountPerVolume molarity2; + + SETissueCompartment* brain = m_data.GetCompartments().GetTissueCompartment(BGE::TissueCompartment::Brain); + + // AMINOACIDS // + //Probably sholdn't be messed with; see elia1984mineral that says total protein stays ~constant + /* + concentration.SetValue(50.0, MassPerVolumeUnit::mg_Per_dL); + SetSubstanceConcentration(*m_aminoAcids, vascular, concentration); + // Tissue + molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L) / m_aminoAcids->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); + SetSubstanceMolarity(*m_aminoAcids, tissue, molarity1, molarity1); + */ + + // GLUCAGON // + //Not modified since it will react to glucose quickly + /* + concentration.SetValue(0.079, MassPerVolumeUnit::ug_Per_L); //We want 70 pg/mL, but it dips in stabilization, so set it higher + SetSubstanceConcentration(*m_glucagon, vascular, concentration); + // Tissue + molarity1.SetValue(0.0, AmountPerVolumeUnit::mmol_Per_L); + molarity2.SetValue(0.0, AmountPerVolumeUnit::mmol_Per_L); + SetSubstanceMolarity(*m_glucagon, tissue, molarity1, molarity2); + */ + + // GLUCOSE // + // \cite garber1974hepatic and \cite owen1971human show glucose concentrations from 0-3 days of fasting and then at 24 days + // https://www.wolframalpha.com/input/?i=y%3D84.3105+-+.39147x+-+.00000434x%5E2+from+0+%3C+x+%3C+80 + // It's very nearly linear up to 3 days, where it stays hovering around 61 mg/dL + double conc = 0; + if (time_h < 72) + conc = 84.3105 - .39147*time_h - .00000434*time_h*time_h; + else + conc = 61.25; + concentration.SetValue(conc, MassPerVolumeUnit::mg_Per_dL); + SetSubstanceConcentration(*m_glucose, vascular, concentration); + + // Tissue + molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L) / m_glucose->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); + SetSubstanceMolarity(*m_glucose, tissue, molarity1, molarity1); + + // INSULIN // + //Not modified since it reacts to glucose quickly + /* + concentration.SetValue(0.85, MassPerVolumeUnit::ug_Per_L); //118.1 pmol/L is desired (.6859 ug/L), but it dips during stabilization, so start higher + SetSubstanceConcentration(*m_insulin, vascular, concentration); + + // Tissue + molarity1.SetValue(0.0, AmountPerVolumeUnit::mmol_Per_L); + molarity2.SetValue(0.0, AmountPerVolumeUnit::mmol_Per_L); + SetSubstanceMolarity(*m_insulin, tissue, molarity1, molarity2); + */ + + // KETONES // + // \cite garber1974hepatic shows ketone concentrations from 0-3 days, mentioning that the peak is around 3 days + // https://www.wolframalpha.com/input/?i=y%3D2.705%2B.0276875x%2B.00398698x%5E2+from+0%3Cx%3C80 + // We'll hold constant after 3 days, though according to Garber, it might decrease a bit after that + conc = 0; + if (time_h < 72) + conc = 2.705 + .0276875*time_h + .00398698*time_h*time_h; + else + conc = 25.52; + concentration.SetValue(conc, MassPerVolumeUnit::mg_Per_dL); + SetSubstanceConcentration(*m_ketones, vascular, concentration); + + // Tissue + molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L) / m_ketones->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); + SetSubstanceMolarity(*m_ketones, tissue, molarity1, molarity1); + + // LACTATE // + //Modified to match engine state in order to provide adequate substrate for gluconeogenesis + + concentration.SetValue(32.5, MassPerVolumeUnit::mg_Per_dL); + molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L) / m_lactate->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); + SetSubstanceConcentration(*m_lactate, vascular, concentration); + + // Tissue + SetSubstanceMolarity(*m_lactate, tissue, molarity1, molarity1); + + + // TRIACYLGLYCEROL // + //Not modified. \cite zauner2000resting shows it not changing much from basal levels, but since we don't model fatty acids, we'll see it rise over time. + /* + concentration.SetValue(75.0, MassPerVolumeUnit::mg_Per_dL); + SetSubstanceConcentration(*m_triacylglycerol, vascular, concentration); + + // Tissue + molarity1.SetValue(concentration.GetValue(MassPerVolumeUnit::g_Per_L) / m_triacylglycerol->GetMolarMass(MassPerAmountUnit::g_Per_mol), AmountPerVolumeUnit::mol_Per_L); + SetSubstanceMolarity(*m_triacylglycerol, tissue, molarity1, molarity1); + // TAG can't cross blood-brain barrier, so no TAG there + molarity1.SetValue(0, AmountPerVolumeUnit::mmol_Per_L); + m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::BrainExtracellular)->GetSubstanceQuantity(*m_triacylglycerol)->GetMolarity().Set(molarity1); + m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::BrainExtracellular)->GetSubstanceQuantity(*m_triacylglycerol)->Balance(BalanceLiquidBy::Molarity); + m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::BrainIntracellular)->GetSubstanceQuantity(*m_triacylglycerol)->GetMolarity().Set(molarity1); + m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::BrainIntracellular)->GetSubstanceQuantity(*m_triacylglycerol)->Balance(BalanceLiquidBy::Molarity); + */ + + // IONS // + //Not modified, but \cite elia1984mineral has good data for Na, K, Ca, and Cl (they don't change much during 4 day starvation) + +} + bool BioGearsSubstances::LoadSubstanceDirectory() { if (!SESubstanceManager::LoadSubstanceDirectory()) diff --git a/src/engine/cpp/Controller/BioGearsSubstances.h b/src/engine/cpp/Controller/BioGearsSubstances.h index 0d7333a..5b9bd73 100644 --- a/src/engine/cpp/Controller/BioGearsSubstances.h +++ b/src/engine/cpp/Controller/BioGearsSubstances.h @@ -85,6 +85,7 @@ class BIOGEARS_API BioGearsSubstances : public SESubstanceManager void CalculateGenericClearance(double volumeCleared_mL, SELiquidCompartment& cmpt, SESubstance& sub, SEScalarMass* cleared = nullptr); void CalculateGenericClearance(double volumeCleared_mL, SETissueCompartment& cmpt, SESubstance& sub, SEScalarMass* cleared = nullptr); void CalculateGenericExcretion(double VascularFlow_mL_Per_s, SETissueCompartment& cmpt, SESubstance& sub, double FractionExcreted, double timestep_s, SEScalarMass* excreted = nullptr); + void SetLiquidCompartmentNonGasesForStarvation(double time_h); const SizeIndependentDepositionEfficencyCoefficient& GetSizeIndependentDepositionEfficencyCoefficient(SESubstance& substance); diff --git a/src/engine/cpp/Systems/BloodChemistry.cpp b/src/engine/cpp/Systems/BloodChemistry.cpp index 696b612..709859b 100644 --- a/src/engine/cpp/Systems/BloodChemistry.cpp +++ b/src/engine/cpp/Systems/BloodChemistry.cpp @@ -96,6 +96,7 @@ void BloodChemistry::Initialize() GetWhiteBloodCellCount().SetValue(7000, AmountPerVolumeUnit::ct_Per_uL); GetPhosphate().SetValue(1.1, AmountPerVolumeUnit::mmol_Per_L); GetStrongIonDifference().SetValue(40.5, AmountPerVolumeUnit::mmol_Per_L); + //Note that RedBloodCellAcetylcholinesterase is initialized in Drugs file because Drugs is processed before Blood Chemistry m_ArterialOxygen_mmHg.Sample(m_aortaO2->GetPartialPressure(PressureUnit::mmHg)); m_ArterialCarbonDioxide_mmHg.Sample(m_aortaCO2->GetPartialPressure(PressureUnit::mmHg)); @@ -446,7 +447,7 @@ void BloodChemistry::CheckBloodSubstanceLevels() patient.SetEvent(CDM::enumPatientEvent::HypoglycemicComa, false, m_data.GetSimulationTime()); } - //hypoglycemic coma + //hyperglycemia if (m_venaCavaGlucose->GetConcentration(MassPerVolumeUnit::mg_Per_dL) > hyperglycemiaLevel_mg_Per_dL) { patient.SetEvent(CDM::enumPatientEvent::Hyperglycemia, true, m_data.GetSimulationTime()); @@ -475,6 +476,81 @@ void BloodChemistry::CheckBloodSubstanceLevels() { patient.SetEvent(CDM::enumPatientEvent::Ketoacidosis, false, m_data.GetSimulationTime()); } + + //sodium check + //Mild and severe hyponatremia + if (m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) < 136.0) + { + patient.SetEvent(CDM::enumPatientEvent::MildHyponatremia, true, m_data.GetSimulationTime()); + } + else if (patient.IsEventActive(CDM::enumPatientEvent::MildHyponatremia) && m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) > 137.0) + { + patient.SetEvent(CDM::enumPatientEvent::MildHyponatremia, false, m_data.GetSimulationTime()); + } + + if (m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) < 120.0) + { + patient.SetEvent(CDM::enumPatientEvent::SevereHyponatremia, true, m_data.GetSimulationTime()); + } + else if (patient.IsEventActive(CDM::enumPatientEvent::SevereHyponatremia) && m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) > 121.0) + { + patient.SetEvent(CDM::enumPatientEvent::SevereHyponatremia, false, m_data.GetSimulationTime()); + } + //Mild and severe hypernatremia + if (m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) > 148.0 ) + { + patient.SetEvent(CDM::enumPatientEvent::MildHypernatremia, true, m_data.GetSimulationTime()); + } + else if (patient.IsEventActive(CDM::enumPatientEvent::MildHypernatremia) && m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) < 147.0) + { + patient.SetEvent(CDM::enumPatientEvent::MildHypernatremia, false, m_data.GetSimulationTime()); + } + if (m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) > 160.0) + { + patient.SetEvent(CDM::enumPatientEvent::SevereHypernatremia, true, m_data.GetSimulationTime()); + } + else if (patient.IsEventActive(CDM::enumPatientEvent::SevereHypernatremia) && m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) < 159.0) + { + patient.SetEvent(CDM::enumPatientEvent::SevereHypernatremia, false, m_data.GetSimulationTime()); + } + + + //potassium check + //mild and severe hypokalemia + if (m_venaCavaPotassium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) < 3.2) + { + patient.SetEvent(CDM::enumPatientEvent::MildHypokalemia, true, m_data.GetSimulationTime()); + } + else if (patient.IsEventActive(CDM::enumPatientEvent::MildHypokalemia) && m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) > 3.3) + { + patient.SetEvent(CDM::enumPatientEvent::MildHypokalemia, false, m_data.GetSimulationTime()); + } + if (m_venaCavaPotassium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) < 2.5) + { + patient.SetEvent(CDM::enumPatientEvent::SevereHypokalemia, true, m_data.GetSimulationTime()); + } + else if (patient.IsEventActive(CDM::enumPatientEvent::SevereHypokalemia) && m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) > 2.6) + { + patient.SetEvent(CDM::enumPatientEvent::SevereHypokalemia, false, m_data.GetSimulationTime()); + } + //mild and severe hyperkalemia + if (m_venaCavaPotassium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) > 5.5) + { + patient.SetEvent(CDM::enumPatientEvent::MildHyperkalemia, true, m_data.GetSimulationTime()); + } + else if (patient.IsEventActive(CDM::enumPatientEvent::MildHyperkalemia) && m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) < 5.6) + { + patient.SetEvent(CDM::enumPatientEvent::MildHyperkalemia, false, m_data.GetSimulationTime()); + } + if (m_venaCavaPotassium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) > 6.2) + { + patient.SetEvent(CDM::enumPatientEvent::SevereHyperkalemia, true, m_data.GetSimulationTime()); + } + else if (patient.IsEventActive(CDM::enumPatientEvent::SevereHyperkalemia) && m_venaCavaSodium->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) < 6.1) + { + patient.SetEvent(CDM::enumPatientEvent::SevereHyperkalemia, false, m_data.GetSimulationTime()); + } + } m_ArterialOxygen_mmHg.Reset(); diff --git a/src/engine/cpp/Systems/Cardiovascular.cpp b/src/engine/cpp/Systems/Cardiovascular.cpp index 31c3167..222c6cb 100644 --- a/src/engine/cpp/Systems/Cardiovascular.cpp +++ b/src/engine/cpp/Systems/Cardiovascular.cpp @@ -795,7 +795,7 @@ void Cardiovascular::CalculateVitalSigns() double hypovolemicShock = 0.5*m_patient->GetBloodVolumeBaseline(VolumeUnit::mL); if (GetBloodVolume().GetValue(VolumeUnit::mL) <= hypovolemicShock) { - m_ss << "50% of the patients blood volume has been lost. The patient is now in an irreversible state."; + m_ss << "50% of the patient's blood volume has been lost. The patient is now in an irreversible state."; Warning(m_ss); /// \irreversible Over half the patients blood volume has been lost. m_patient->SetEvent(CDM::enumPatientEvent::IrreversibleState, true, m_data.GetSimulationTime()); @@ -1029,7 +1029,7 @@ void Cardiovascular::Hemorrhage() double resistanceFunMax = 1000.0; //Need to read in severity from MCIS code - int MCIS1_severity = 0; + double severity = 0; //Values for tracking physiological metrics double resistance = 0.0; double TotalLossRate_mL_Per_s=0.0; @@ -1044,11 +1044,11 @@ void Cardiovascular::Hemorrhage() for (auto hem : hems) { h = hem.second; - MCIS1_severity = h->GetMCIS()[0]; + severity = h->GetSeverity().GetValue(); targetPath = m_CirculatoryCircuit->GetPath(h->GetBleedName()); //We need to adjust the resistance functions for main aorta and vena cava because they have very high/low pressures relative to other compartments - if (h->GetCompartment() == "VenaCava") + if (h->GetCompartment() == "Vena Cava") { resistanceFunMin = 0.5; resistanceFunMax = 50.0; @@ -1062,8 +1062,8 @@ void Cardiovascular::Hemorrhage() //The values for this function were chosen empirically to produce following severity->resistance map with flow rates that seem reasonable //with data in @cite lawnick2013combat and @cite guitierrez2004clinical. Values can be adjusted as needed to incorporate more data - //Severity->Resistance: 1->383, 2->122, 3->39, 4->12.5, 5->4 (not for aorta or vena cava) - resistance = GeneralMath::ResistanceFunction(10.0, resistanceFunMin, resistanceFunMax, MCIS1_severity / 5.0); + //Severity->Resistance: 0.2->383, 0.4->122, 0.6->39, 0.8->12.5, 1.0->4 (not for aorta or vena cava) + resistance = GeneralMath::ResistanceFunction(10.0, resistanceFunMin, resistanceFunMax, severity); targetPath->GetNextResistance().SetValue(resistance, FlowResistanceUnit::mmHg_s_Per_mL); @@ -1072,11 +1072,13 @@ void Cardiovascular::Hemorrhage() bleedoutTime = (bloodVolume_mL - (0.5*baselineBloodVolume_mL)) / TotalLossRate_mL_Per_s*(1.0 / 60.0); } - + /* + Stub to try to calculate a probability of survival based on the bleeding rate and approximate time to bleed out. if (bleedoutTime!=0) probabilitySurvival = 100.0-100.0*(0.9127*exp(-0.008*bleedoutTime)); //relationship from Table 5 in champion2003profile - + */ + double bloodDensity_kg_Per_mL = m_data.GetBloodChemistry().GetBloodDensity(MassPerVolumeUnit::kg_Per_mL); double massLost_kg = TotalLossRate_mL_Per_s*bloodDensity_kg_Per_mL*m_dT_s; double patientMass_kg = m_patient->GetWeight(MassUnit::kg); diff --git a/src/engine/cpp/Systems/Drugs.cpp b/src/engine/cpp/Systems/Drugs.cpp index 938b888..6551468 100644 --- a/src/engine/cpp/Systems/Drugs.cpp +++ b/src/engine/cpp/Systems/Drugs.cpp @@ -26,6 +26,7 @@ specific language governing permissions and limitations under the License. #include "substance/SESubstanceConcentration.h" #include "bind/SubstanceConcentrationData.hxx" #include "properties/SEScalarPressure.h" +#include "properties/SEScalarAmountPerVolume.h" #include "properties/SEScalarMassPerVolume.h" #include "properties/SEScalarVolumePerTime.h" #include "properties/SEScalarFraction.h" @@ -64,6 +65,8 @@ void Drugs::Clear() m_liverVascular = nullptr; m_liverTissue = nullptr; m_IVToVenaCava = nullptr; + m_Sarin = nullptr; + m_Pralidoxime = nullptr; DELETE_MAP_SECOND(m_BolusAdministrations); @@ -89,14 +92,11 @@ void Drugs::Initialize() GetTidalVolumeChange().SetValue(0.0, VolumeUnit::mL); GetTubularPermeabilityChange().SetValue(0); GetCentralNervousResponse().SetValue(0.0); + m_data.GetBloodChemistry().GetRedBloodCellAcetylcholinesterase().SetValue(8.0*1e-9, AmountPerVolumeUnit::mol_Per_L); //Need to initialize here since Drugs processed before BloodChemistry - //Loop over substances and initialize effect site concentration to 0 for substances with PD effects (i.e. drugs) - for (SESubstance* sub : m_data.GetSubstances().GetSubstances()) - { - if (sub->HasPD()) - sub->GetEffectSiteConcentration().SetValue(0.0, MassPerVolumeUnit::ug_Per_mL); - - } + + m_SarinRbcAcetylcholinesteraseComplex_nM = 0.0; + m_AgedRbcAcetylcholinesterase_nM = 0.0; } @@ -105,6 +105,9 @@ bool Drugs::Load(const CDM::BioGearsDrugSystemData& in) if (!SEDrugSystem::Load(in)) return false; + m_SarinRbcAcetylcholinesteraseComplex_nM = in.SarinRbcAcetylcholinesteraseComplex_nM(); + m_AgedRbcAcetylcholinesterase_nM = in.AgedRbcAcetylcholinesterase_nM(); + BioGearsSystem::LoadState(); for (const CDM::SubstanceBolusStateData& bData : in.BolusAdministration()) @@ -135,6 +138,8 @@ CDM::BioGearsDrugSystemData* Drugs::Unload() const void Drugs::Unload(CDM::BioGearsDrugSystemData& data) const { SEDrugSystem::Unload(data); + data.SarinRbcAcetylcholinesteraseComplex_nM(m_SarinRbcAcetylcholinesteraseComplex_nM); + data.AgedRbcAcetylcholinesterase_nM(m_AgedRbcAcetylcholinesterase_nM); for (auto itr : m_BolusAdministrations) { @@ -154,6 +159,7 @@ void Drugs::Unload(CDM::BioGearsDrugSystemData& data) const void Drugs::SetUp() { m_dt_s = m_data.GetTimeStep().GetValue(TimeUnit::s); + m_RbcAcetylcholinesteraseFractionInhibited = 0.0; m_muscleIntracellular = m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::MuscleIntracellular); m_aortaVascular = m_data.GetCompartments().GetLiquidCompartment(BGE::VascularCompartment::Aorta); m_venaCavaVascular = m_data.GetCompartments().GetLiquidCompartment(BGE::VascularCompartment::VenaCava); @@ -161,6 +167,9 @@ void Drugs::SetUp() m_liverVascular = m_data.GetCompartments().GetLiquidCompartment(BGE::VascularCompartment::Liver); m_liverTissue = m_data.GetCompartments().GetTissueCompartment(BGE::TissueCompartment::Liver); m_IVToVenaCava = m_data.GetCircuits().GetCardiovascularCircuit().GetPath(BGE::CardiovascularPath::IVToVenaCava); + //Need to set up pointers for Sarin and Pralidoxime to handle nerve agent events since they use a different method to calculate effects + m_Sarin = m_data.GetSubstances().GetSubstance("Sarin"); + m_Pralidoxime = m_data.GetSubstances().GetSubstance("Pralidoxime"); DELETE_MAP_SECOND(m_BolusAdministrations); @@ -306,16 +315,23 @@ void Drugs::AdministerSubstanceBolus() //-------------------------------------------------------------------------------------------------- void Drugs::AdministerSubstanceInfusion() { + //Note: Currently, user removes state by setting the infusion rate of the drug in question to 0.0 const std::map& infusions = m_data.GetActions().GetPatientActions().GetSubstanceInfusions(); if (infusions.empty()) return; SESubstanceInfusion* infusion; SESubstance* sub; - SELiquidSubstanceQuantity* subQ; - double concentration_ug_Per_mL; - double rate_mL_Per_s; - double massIncrement_ug = 0; + SELiquidSubstanceQuantity* subQ; + double patientMass_kg = m_data.GetPatient().GetWeight(MassUnit::kg); + SEScalarTemperature& ambientTemp = m_data.GetEnvironment().GetConditions().GetAmbientTemperature(); + SEScalarMassPerVolume densityFluid; + GeneralMath::CalculateWaterDensity(ambientTemp, densityFluid); + + double concentration_ug_Per_mL = 0.0; + double rate_mL_Per_s = 0.0; + double totalRate_mL_Per_s = 0.0; + double massIncrement_ug = 0.0; for (auto i : infusions) { @@ -323,23 +339,34 @@ void Drugs::AdministerSubstanceInfusion() infusion = i.second; concentration_ug_Per_mL = infusion->GetConcentration().GetValue(MassPerVolumeUnit::ug_Per_mL); rate_mL_Per_s = infusion->GetRate().GetValue(VolumePerTimeUnit::mL_Per_s); - massIncrement_ug = rate_mL_Per_s*concentration_ug_Per_mL*m_dt_s; - subQ = m_venaCavaVascular->GetSubstanceQuantity(*sub); - subQ->GetMass().IncrementValue(massIncrement_ug, MassUnit::ug); - /// \todo Enforce limits and remove the fatal error - /// \error Fatal: Titration administration cannot be negative - if (massIncrement_ug<0) + if (rate_mL_Per_s < 0) { - std::stringstream AdministeredTitrationDoseError; - AdministeredTitrationDoseError << "Cannot specify a dose of less than 0. Current dose is: " << massIncrement_ug << " ug"; - Fatal(AdministeredTitrationDoseError); - return; + std::stringstream InfusionRateError; + InfusionRateError << "Cannot specify a rate less than 0, setting to a default of 1 mL/min"; + Info(InfusionRateError); + rate_mL_Per_s = 1.0 / 60.0; + infusion->GetRate().SetValue(rate_mL_Per_s, VolumePerTimeUnit::mL_Per_s); + } + + if (concentration_ug_Per_mL < 0) + { + std::stringstream InfusionConcentrationError; + InfusionConcentrationError << "Cannot specify a concentration less than 0, setting to a default of 100 ug/mL"; + Info(InfusionConcentrationError); + concentration_ug_Per_mL = 100.0; + infusion->GetConcentration().SetValue(concentration_ug_Per_mL, MassPerVolumeUnit::ug_Per_mL); } - /// \todo Need to add fluid amount to fluid system - /// \todo Support state, and how would a user remove this action? + massIncrement_ug = rate_mL_Per_s*concentration_ug_Per_mL*m_dt_s; + subQ = m_venaCavaVascular->GetSubstanceQuantity(*sub); + subQ->GetMass().IncrementValue(massIncrement_ug, MassUnit::ug); + totalRate_mL_Per_s += rate_mL_Per_s; + patientMass_kg += rate_mL_Per_s*m_dt_s*densityFluid.GetValue(MassPerVolumeUnit::kg_Per_mL); } + m_data.GetPatient().GetWeight().SetValue(patientMass_kg, MassUnit::kg); + m_IVToVenaCava->GetNextFlowSource().SetValue(totalRate_mL_Per_s, VolumePerTimeUnit::mL_Per_s); + } //-------------------------------------------------------------------------------------------------- @@ -364,9 +391,22 @@ void Drugs::AdministerSubstanceCompoundInfusion() double rate_mL_Per_s = 0; double totalRate_mL_Per_s = 0; double massIncrement_ug=0; + double volumeRemaining_mL = 0.0; + double volumeToAdminister_mL = 0.0; double patientMass_kg = m_data.GetPatient().GetWeight(MassUnit::kg); double densityFluid_kg_Per_mL = 0.0; + //The maximum rate of IV fluid administration is not well defined. Guidelines in cases of hypovolemic shock suggest that + //resuscitation should begin with 4.0 mL/kg bolus administered over 10-15 minutes. Boluses of 1-2 L are referenced in extreme + //cases, but the time over which they are administered is not firm. If we take a worse case bolus of 1.5 L delivered over + //15 minutes, we get a max suggested rate of 100 mL/min. This causes O2 partial pressure in BioGears to drop somewhat, but + //does not change O2 saturation. Physically, it is possible to exceed this rate up to the flow limitations imposed by the IV tubing + //(suggested to be 1000 mL/3.5 min ~= 285 mL/min from http://emedsa.org.au/EDHandbook/resuscitation/IVCannula.htm + //Thus we will allow a rate up to 285 mL/min but will issue a warning if provided rate exceeds recommended level (this is done + //in the SESubstanceCompoundInfusion file). In case of septic shock, the recommended fluid administration changes and will need to be revisited. + ///\@cite malbrain2014fluid, @cite wise2017strategies + ///\ToDo: We should consider a variable maxRate when septic shock is introduced to BioGears. ///\@cite malbrain2014fluid, @cite wise2017strategies + std::vector emptyBags; for(auto i : infusions) @@ -376,43 +416,40 @@ void Drugs::AdministerSubstanceCompoundInfusion() rate_mL_Per_s = infusion->GetRate().GetValue(VolumePerTimeUnit::mL_Per_s); totalRate_mL_Per_s += rate_mL_Per_s; - /// \todo Enforce limits and remove the fatal error - /// \error Fatal: rate cannot exceed 285 mL/min - if (rate_mL_Per_s>285) // from http://emedsa.org.au/EDHandbook/resuscitation/IVCannula.htm... 1000mL/3.5 min ~= 285 mL/min - { - m_ss<<"Cannot specify an Infusion rate greater than 285 mL/min. Current administration rate is: "<< infusion->GetRate(); - Fatal(m_ss); - return; - } - - infusion->GetBagVolume().IncrementValue(-rate_mL_Per_s*m_dt_s, VolumeUnit::mL); - double total_mL = infusion->GetBagVolume().GetValue(VolumeUnit::mL); - if (total_mL <= 0) - { /// \todo correct the mass based on what we have left in the bag + volumeRemaining_mL = infusion->GetBagVolume().GetValue(VolumeUnit::mL); + volumeToAdminister_mL = rate_mL_Per_s*m_dt_s; + if (volumeRemaining_mL < volumeToAdminister_mL) + { + volumeToAdminister_mL = volumeRemaining_mL; emptyBags.push_back(compound); - continue; } + infusion->GetBagVolume().IncrementValue(-volumeToAdminister_mL, VolumeUnit::mL); for (const SESubstanceConcentration* component : compound->GetComponents()) { subQ = m_venaCavaVascular->GetSubstanceQuantity(component->GetSubstance()); - double massIncrement_ug = rate_mL_Per_s*component->GetConcentration(MassPerVolumeUnit::ug_Per_mL)*m_dt_s; - subQ->GetMass().IncrementValue(massIncrement_ug, MassUnit::ug); - subQ->Balance(BalanceLiquidBy::Mass); + double massIncrement_ug = volumeToAdminister_mL*component->GetConcentration(MassPerVolumeUnit::ug_Per_mL); + subQ->GetMass().IncrementValue(massIncrement_ug, MassUnit::ug); + subQ->Balance(BalanceLiquidBy::Mass); } - if (compound->GetName().compare("Saline") == 0) - densityFluid_kg_Per_mL = m_data.GetConfiguration().GetWaterDensity(MassPerVolumeUnit::kg_Per_mL); + if ((compound->GetName().compare("Saline") == 0) || (compound->GetName().compare("RingersLactate") == 0)) //Note: Saline and ringers lactate have different densities than pure water + { + SEScalarTemperature& ambientTemp = m_data.GetEnvironment().GetConditions().GetAmbientTemperature(); + SEScalarMassPerVolume densityFluid; + GeneralMath::CalculateWaterDensity(ambientTemp, densityFluid); + densityFluid_kg_Per_mL = densityFluid.GetValue(MassPerVolumeUnit::kg_Per_mL); + } else if (compound->GetName().compare("Blood") == 0) densityFluid_kg_Per_mL = m_data.GetBloodChemistry().GetBloodDensity(MassPerVolumeUnit::kg_Per_mL); - patientMass_kg -= rate_mL_Per_s*densityFluid_kg_Per_mL*m_dt_s; + patientMass_kg += volumeToAdminister_mL*densityFluid_kg_Per_mL; } for (const SESubstanceCompound* c : emptyBags) m_data.GetActions().GetPatientActions().RemoveSubstanceCompoundInfusion(*c); m_data.GetPatient().GetWeight().SetValue(patientMass_kg, MassUnit::kg); - m_IVToVenaCava->GetNextFlowSource().SetValue(totalRate_mL_Per_s, VolumePerTimeUnit::mL_Per_s); + m_IVToVenaCava->GetNextFlowSource().SetValue(totalRate_mL_Per_s, VolumePerTimeUnit::mL_Per_s); } //-------------------------------------------------------------------------------------------------- @@ -462,7 +499,7 @@ void Drugs::CalculatePartitionCoefficients() continue; if(!sub->GetPK().HasPhysicochemicals()) continue; - + SESubstancePhysicochemicals& pk = sub->GetPK().GetPhysicochemicals(); CDM::enumSubstanceIonicState::value IonicState = pk.GetIonicState(); double AcidDissociationConstant = pk.GetAcidDissociationConstant().GetValue(); @@ -562,42 +599,62 @@ void Drugs::CalculateDrugEffects() double neuromuscularBlockLevel = 0; double sedationLevel = 0; double bronchodilationLevel = 0; - double plasmaConcentration_ug_Per_mL = 0; double concentrationEffects_unitless = 0; double deltaTubularPermeability = 0.0; double pupilSizeResponseLevel = 0; double pupilReactivityResponseLevel = 0; double shapeParameter = 1.; + double ec50_ug_Per_mL = 0.0; SEPatient& patient = m_data.GetPatient(); double HRBaseline_per_min = patient.GetHeartRateBaseline(FrequencyUnit::Per_min); double effectSiteConcentration_ug_Per_mL = 0.0; double centralNervousResponseLevel = 0.0; - - //Loop over substances + //Testing naloxone reversal + SESubstance* m_Naloxone = m_data.GetSubstances().GetSubstance("Naloxone"); + double inhibitorConcentration_ug_Per_mL = 0.0; + double inhibitorConstant_ug_Per_mL = 1.0; //Can't initialize to 0 lest we divide by 0. Won't matter what it is when there is no inhibitor because this will get mulitplied by 0 anyway + + + //Loop over substances for (SESubstance* sub : m_data.GetCompartments().GetLiquidCompartmentSubstances()) { if (!sub->HasPD()) continue; SESubstancePharmacodynamics& pd = sub->GetPD(); - ///\TODO Replace all plasma concentrations with effect site concentrations (if any left over) - plasmaConcentration_ug_Per_mL = sub->GetPlasmaConcentration(MassPerVolumeUnit::ug_Per_mL); shapeParameter = pd.GetEMaxShapeParameter().GetValue(); + ec50_ug_Per_mL = pd.GetEC50().GetValue(MassPerVolumeUnit::ug_Per_mL); //Get effect site concentration and use it to calculate unitless drug effects. - //Currently, effect site concentration is same as plasma concentration for all drugs except morphine + //Currently, effect site concentration is same as plasma concentration for all drugs except morphine and sarin effectSiteConcentration_ug_Per_mL = sub->GetEffectSiteConcentration(MassPerVolumeUnit::ug_Per_mL); - if (shapeParameter == 1) // Avoiding using pow if we don't have to. I don't know if this is good practice or not, but seems legit. - { - concentrationEffects_unitless = effectSiteConcentration_ug_Per_mL / (pd.GetEC50().GetValue(MassPerVolumeUnit::ug_Per_mL) + effectSiteConcentration_ug_Per_mL); + if (sub->GetClassification() == CDM::enumSubstanceClass::Opioid) + { + if (m_data.GetSubstances().IsActive(*m_Naloxone)) + { + inhibitorConstant_ug_Per_mL = m_Naloxone->GetPD().GetEC50().GetValue(MassPerVolumeUnit::ug_Per_mL); + inhibitorConcentration_ug_Per_mL = m_Naloxone->GetEffectSiteConcentration(MassPerVolumeUnit::ug_Per_mL); + } + concentrationEffects_unitless = pow(effectSiteConcentration_ug_Per_mL, shapeParameter) / (pow(ec50_ug_Per_mL, shapeParameter)*pow(1 + inhibitorConcentration_ug_Per_mL / inhibitorConstant_ug_Per_mL, shapeParameter) + pow(effectSiteConcentration_ug_Per_mL, shapeParameter)); + } + else if (sub->GetName() == "Sarin") + { + concentrationEffects_unitless = m_RbcAcetylcholinesteraseFractionInhibited; } else { - concentrationEffects_unitless = pow(effectSiteConcentration_ug_Per_mL, shapeParameter) / (pow(pd.GetEC50().GetValue(MassPerVolumeUnit::ug_Per_mL), shapeParameter) + pow(effectSiteConcentration_ug_Per_mL, shapeParameter)); + if (shapeParameter == 1) // Avoiding using pow if we don't have to. I don't know if this is good practice or not, but seems legit. + { + concentrationEffects_unitless = effectSiteConcentration_ug_Per_mL / (ec50_ug_Per_mL + effectSiteConcentration_ug_Per_mL); + + } + else + { + concentrationEffects_unitless = pow(effectSiteConcentration_ug_Per_mL, shapeParameter) / (pow(ec50_ug_Per_mL, shapeParameter) + pow(effectSiteConcentration_ug_Per_mL, shapeParameter)); + } } - /// \todo The drug effect is being applied to the baseline, so if the baseline changes the delta heart rate changes. // This would be a problem for something like a continuous infusion of a drug or an environmental drug // where we need to establish a new homeostatic point. Once the patient stabilizes with the drug effect included, a new baseline is @@ -631,9 +688,9 @@ void Drugs::CalculateDrugEffects() neuromuscularBlockLevel += pd.GetNeuromuscularBlock().GetValue() * concentrationEffects_unitless; bronchodilationLevel += pd.GetBronchodilation().GetValue() * concentrationEffects_unitless; + pupilSizeResponseLevel += pd.GetPupillaryResponse().GetSizeModifier().GetValue() * concentrationEffects_unitless; + pupilReactivityResponseLevel += pd.GetPupillaryResponse().GetReactivityModifier().GetValue() * concentrationEffects_unitless; - pupilSizeResponseLevel += pd.GetPupillaryResponse().GetSizeModifier().GetValue() * concentrationEffects_unitless; - pupilReactivityResponseLevel += pd.GetPupillaryResponse().GetReactivityModifier().GetValue() * concentrationEffects_unitless; } //Translate Diastolic and Systolic Pressure to pulse pressure and mean pressure @@ -653,8 +710,19 @@ void Drugs::CalculateDrugEffects() GetTubularPermeabilityChange().SetValue(deltaTubularPermeability); GetCentralNervousResponse().SetValue(centralNervousResponseLevel); + //Pupil effects - // Bound the pupil modifiers + //We need to handle Sarin pupil effects (if Sarin is active) separately because technically they stem from contact and not systemic levels, meaning that they + //do not depend on the Sarin plasma concentration in the same way as other PD effects. We still perform the calculation here because + //we cannot "contact" the eye, but scale them differently. Sarin pupil effects are large and fast, so it's reasonable to + //overwrite other drug pupil effects (and we probably aren't modeling opioid addicts inhaling Sarin) + if (m_data.GetSubstances().IsActive(*m_data.GetSubstances().GetSubstance("Sarin"))) + { + pupilSizeResponseLevel = GeneralMath::LogisticFunction(-1, 0.0475, 250, m_data.GetSubstances().GetSubstance("Sarin")->GetPlasmaConcentration(MassPerVolumeUnit::ug_Per_L)); + pupilReactivityResponseLevel = pupilSizeResponseLevel; + } + + //Bound pupil modifiers BLIM(pupilSizeResponseLevel, -1, 1); BLIM(pupilReactivityResponseLevel, -1, 1); GetPupillaryResponse().GetSizeModifier().SetValue(pupilSizeResponseLevel); @@ -690,6 +758,12 @@ void Drugs::CalculatePlasmaSubstanceConcentration() { SESubstancePharmacodynamics& pd = sub->GetPD(); rate_Per_s = pd.GetEffectSiteRateConstant(FrequencyUnit::Per_s); + if (!sub->HasEffectSiteConcentration()) + { + effectConcentration = 0.0; + sub->GetEffectSiteConcentration().SetValue(effectConcentration, MassPerVolumeUnit::ug_Per_mL); + } + effectConcentration = sub->GetEffectSiteConcentration(MassPerVolumeUnit::ug_Per_mL); //If a substance has rate constant set to 0, no effect concentration is needed. Just use plasma concentration as before @@ -706,6 +780,9 @@ void Drugs::CalculatePlasmaSubstanceConcentration() //Store effect site concentration for use in CalculateDrugEffects function sub->GetEffectSiteConcentration().SetValue(effectConcentration, MassPerVolumeUnit::ug_Per_mL); } + + if((sub->GetName()=="Sarin")&&(m_data.GetSubstances().IsActive(*m_Sarin))) + SarinKinetics(); } } @@ -762,5 +839,73 @@ void Drugs::CalculateSubstanceClearance() //Hepatic Excretion m_data.GetSubstances().CalculateGenericExcretion(LiverVascularFlow_mL_Per_s, *m_liverTissue, *sub, clearance.GetFractionExcretedInFeces().GetValue(), m_dt_s); } +} + +//-------------------------------------------------------------------------------------------------- +/// \brief +/// Calculates the inhibition of erythrocyte bound acetylcholinesterase by the nerve agent Sarin using reaction kinetics +/// +/// \details +/// Unlike other drugs in BioGears, the PD effects of Sarin are not based directly on agent plasma concentration. Instead, we model +/// Sarin binding to red blood cell acetylcholinesterase (Rbc-Ache) and link the inhibition of this enzyme to Sarin pharmacodynamics. +/// This calculation utilizies a kinetic model that incorporates irreversible aging of Rbc-Ache and the salvaging effect of Pralidoxime. +/// As such, this strategy represents a more mechanistic, receptor-target approach to pharmacodynamics +//-------------------------------------------------------------------------------------------------- +void Drugs::SarinKinetics() +{ + //Get Sarin and Pralidoxime (if active) concentrations and convert units to nM (units that all the rate equations are in) + double SarinConcentration_g_Per_L = m_Sarin->GetPlasmaConcentration(MassPerVolumeUnit::g_Per_L); + double SarinMolarMass_g_Per_umol = m_Sarin->GetMolarMass(MassPerAmountUnit::g_Per_umol); + double SarinConcentration_nM = SarinConcentration_g_Per_L / SarinMolarMass_g_Per_umol * 1000; + double PralidoximeConcentration_nM = 0.0; + double PralidoximeMolarMass_g_Per_umol = m_Pralidoxime->GetMolarMass(MassPerAmountUnit::g_Per_umol); + double PralidoximeConcentration_g_Per_L = 0.0; + if ((m_data.GetSubstances().IsActive(*m_Pralidoxime))&&(m_Pralidoxime->HasPlasmaConcentration())) //Substance can get flagged active w/ null plasma concentration so need to check both for stabilization step + { + PralidoximeConcentration_g_Per_L = m_Pralidoxime->GetPlasmaConcentration(MassPerVolumeUnit::g_Per_L); + PralidoximeConcentration_nM = PralidoximeConcentration_g_Per_L / PralidoximeMolarMass_g_Per_umol * 1000; + } + //Get RBC-AChE concentration and create copies of all concentrations so that they are not overwritten in the rate equations + double RbcAcetylcholinesterase_nM = m_data.GetBloodChemistry().GetRedBloodCellAcetylcholinesterase(AmountPerVolumeUnit::mol_Per_L) * 1e9; + double RbcAche_nM = RbcAcetylcholinesterase_nM; + double SarinRbcAche_nM = m_SarinRbcAcetylcholinesteraseComplex_nM; + double AgedSarin_nM = m_AgedRbcAcetylcholinesterase_nM; + double BaselineRbcAcetylcholinesterase_nM = 8.0; + + //Rate constants + double RateRbcAcheInhibition_per_nM_s = 4.50e-4; /// \cite gupta2009handbook \cite rodriguez2015model + double RateRbcAcheAging_per_s = 4.83e-5; /// \cite gupta2009handbook \cite rodriguez2015model + double RateRbcAcheSynthesis_nM_per_s = 9.33e-7; /// \cite grob1958effects + double RateRbcAcheDegredation_per_s = 1.17e-7; /// \cite rodriguez2015model + double RatePralidoximeReversal_per_s = 4.22e-3; /// \cite rodriguez2015model + double PralidoximeDissociationConstant_nM = 27630.0; /// \cite rodriguez2015model + + //Michaelis-Menten expression for rate of pralidoxime interaction with bound sarin/rbc-ache complex + double PralidoximeReversal = RatePralidoximeReversal_per_s*m_SarinRbcAcetylcholinesteraseComplex_nM*PralidoximeConcentration_nM / + (PralidoximeDissociationConstant_nM + PralidoximeConcentration_nM); ///\cite rodriguez2015model + + + //Kinetic model equations adapted from + ///\cite rodriguez2015model and \cite gupta2009handbook + RbcAche_nM += m_dt_s*(-RateRbcAcheInhibition_per_nM_s*SarinConcentration_nM*RbcAcetylcholinesterase_nM + - RateRbcAcheDegredation_per_s*RbcAcetylcholinesterase_nM + + RateRbcAcheSynthesis_nM_per_s + + PralidoximeReversal); + SarinRbcAche_nM += m_dt_s*(RateRbcAcheInhibition_per_nM_s*SarinConcentration_nM*RbcAcetylcholinesterase_nM + - RateRbcAcheAging_per_s*m_SarinRbcAcetylcholinesteraseComplex_nM + - PralidoximeReversal); + AgedSarin_nM += m_dt_s*(RateRbcAcheAging_per_s*m_SarinRbcAcetylcholinesteraseComplex_nM); + SarinConcentration_nM += m_dt_s*(-RateRbcAcheInhibition_per_nM_s*SarinConcentration_nM*RbcAcetylcholinesterase_nM); + PralidoximeConcentration_nM = -PralidoximeReversal; + m_RbcAcetylcholinesteraseFractionInhibited = 1-RbcAche_nM / BaselineRbcAcetylcholinesterase_nM; + + //Update values for next time step + m_Sarin->GetPlasmaConcentration().SetValue(SarinConcentration_nM / 1000.0 * SarinMolarMass_g_Per_umol, MassPerVolumeUnit::g_Per_L); + m_data.GetBloodChemistry().GetRedBloodCellAcetylcholinesterase().SetValue(RbcAche_nM * 1e-9, AmountPerVolumeUnit::mol_Per_L); + m_SarinRbcAcetylcholinesteraseComplex_nM = SarinRbcAche_nM; + m_AgedRbcAcetylcholinesterase_nM = AgedSarin_nM; + + if (m_data.GetSubstances().IsActive(*m_Pralidoxime)) + m_Pralidoxime->GetPlasmaConcentration().SetValue(PralidoximeConcentration_nM / 1000 * PralidoximeMolarMass_g_Per_umol, MassPerVolumeUnit::g_Per_L); } \ No newline at end of file diff --git a/src/engine/cpp/Systems/Drugs.h b/src/engine/cpp/Systems/Drugs.h index d857ddf..0f6dee0 100644 --- a/src/engine/cpp/Systems/Drugs.h +++ b/src/engine/cpp/Systems/Drugs.h @@ -64,12 +64,16 @@ class BIOGEARS_API Drugs : public SEDrugSystem, public BioGearsSystem void CalculateSubstanceClearance(); void CalculatePlasmaSubstanceConcentration(); void CalculateDrugEffects(); + void SarinKinetics(); // Serializable member variables (Set in Initialize and in schema) std::map m_BolusAdministrations; + double m_SarinRbcAcetylcholinesteraseComplex_nM; + double m_AgedRbcAcetylcholinesterase_nM; // Stateless member variable (Set in SetUp()) double m_dt_s; + double m_RbcAcetylcholinesteraseFractionInhibited; SELiquidCompartment* m_aortaVascular; SELiquidCompartment* m_venaCavaVascular; SELiquidCompartment* m_muscleIntracellular; @@ -77,4 +81,6 @@ class BIOGEARS_API Drugs : public SEDrugSystem, public BioGearsSystem SETissueCompartment* m_liverTissue; SEFluidCircuitPath* m_IVToVenaCava; SETissueCompartment* m_fatTissue; + SESubstance* m_Sarin; + SESubstance* m_Pralidoxime; }; \ No newline at end of file diff --git a/src/engine/cpp/Systems/Endocrine.cpp b/src/engine/cpp/Systems/Endocrine.cpp index 6facd81..18bc1d5 100644 --- a/src/engine/cpp/Systems/Endocrine.cpp +++ b/src/engine/cpp/Systems/Endocrine.cpp @@ -15,16 +15,15 @@ specific language governing permissions and limitations under the License. #include "system/physiology/SEDrugSystem.h" #include "system/physiology/SECardiovascularSystem.h" #include "system/physiology/SEEnergySystem.h" - -#include "patient/SEMeal.h" #include "patient/SENutrition.h" -#include "patient/conditions/SEConsumeMeal.h" - +#include "patient/conditions/SEDiabetesType1.h" +#include "patient/conditions/SEDiabetesType2.h" #include "properties/SEScalarPower.h" #include "properties/SEScalarPressure.h" #include "properties/SEScalarMass.h" #include "properties/SEScalarMassPerVolume.h" #include "properties/SEScalarAmountPerTime.h" +#include "properties/SEScalarAmountPerVolume.h" #include "properties/SEScalarMassPerAmount.h" #include "properties/SEScalarVolume.h" #include "properties/SEScalarTemperature.h" @@ -96,7 +95,28 @@ void Endocrine::SetUp() void Endocrine::AtSteadyState() { + if (m_data.GetState() == EngineState::AtInitialStableState) + { + double diabetesScale = 1; + if (m_data.GetConditions().HasDiabetesType1()) + { + if (m_data.GetConditions().GetDiabetesType1()->HasInsulinProductionSeverity()) + diabetesScale = 1 - m_data.GetConditions().GetDiabetesType1()->GetInsulinProductionSeverity().GetValue(); + + const std::vector& vascular = m_data.GetCompartments().GetVascularLeafCompartments(); + SESubstance& insulin = m_data.GetSubstances().GetInsulin(); + + SELiquidSubstanceQuantity* subQ; + + for (SELiquidCompartment* cmpt : vascular) + { + subQ = cmpt->GetSubstanceQuantity(insulin); + subQ->GetConcentration().SetValue(diabetesScale * .6859, MassPerVolumeUnit::ug_Per_L); + subQ->Balance(BalanceLiquidBy::Concentration); + } + } + } } //-------------------------------------------------------------------------------------------------- @@ -146,10 +166,37 @@ void Endocrine::PostProcess() /// \details /// The insulin production rate is calculated based on the relevant range of glucose and instantaneous concentration of glucose in the aorta /// (representative of the body). The equation for insulin production is from \cite tolic2000insulin +/// Patients with diabetes mellitus may have a reduced capacity for insulin production or insulin resistance, based on severity. //-------------------------------------------------------------------------------------------------- void Endocrine::SynthesizeInsulin() { double bloodGlucoseConcentration_g_Per_L = m_aortaGlucose->GetConcentration(MassPerVolumeUnit::g_Per_L); + + double diabetesScale = 1; + + //In type 1 diabetes, the ability to produce insulin is lessened + if (m_data.GetConditions().HasDiabetesType1()) + { + if (m_data.GetConditions().GetDiabetesType1()->HasInsulinProductionSeverity()) + diabetesScale = 1 - m_data.GetConditions().GetDiabetesType1()->GetInsulinProductionSeverity().GetValue(); + } + + //In type 2 diabetes, the beta cells can have impaired production like in type 1, + //but also there is the potential for increased production due to elevated blood glucose. + //Since our normal production curve tops out at a lower glucose concentration, we use a multiplier + //based on the plot in Guyton p 991 to up our production maximum + else if (m_data.GetConditions().HasDiabetesType2()) + { + if (m_data.GetConditions().GetDiabetesType2()->HasInsulinProductionSeverity()) + diabetesScale = 1 - m_data.GetConditions().GetDiabetesType2()->GetInsulinProductionSeverity().GetValue(); + + //Non-symmetric sigmoid roughly fit to Guyton curve + //https://www.wolframalpha.com/input/?i=y+%3D+20.61421+%2B+(0.5089411+-+20.61421)%2F(1+%2B+(x%2F1.349053)%5E6.362276)%5E0.32948+from+0%3Cy%3C25+and+0%3Cx%3C7 + double multiplier = 20.61421 + (.5089411 - 20.61421) / pow((1 + pow((bloodGlucoseConcentration_g_Per_L / 1.349053), 6.362276)), .32948); + + diabetesScale *= multiplier; + } + // 2.0 = upperConcentration_g_Per_L // 0.3 = lowerConcentration_g_Per_l // 65.421 = amplitudeRate_mU_Per_min @@ -157,11 +204,12 @@ void Endocrine::SynthesizeInsulin() // Note: Guyton says insulin production at 90 mg/dL glucose concentration should be // 25 ng/min/kg, which is about 300 pmol/min, double what we have using this curve from Tolic. - // Because of this, we won't capture insulin behavior for very high glucose concentrations, see Guyton p 991 - // If we ever want to capture diabetes behavior, we'll need to change this curve. + // Because of this, we won't capture insulin behavior for very high glucose concentrations in non-diabetics, see Guyton p 991 // Also, since we only key off of the instantaneous aorta glucose, we miss out on any parasympathetic // signals that affect Beta cells, meaning if we implement stress response, we might also see hyperinsulinemia (see Boron p 1222) - double insulinSynthesisRate_pmol_Per_min = 6.67 * 65.421 / (1.0 + exp((2.0 - 2.0*bloodGlucoseConcentration_g_Per_L) / 0.3)); + double insulinSynthesisRate_pmol_Per_min = diabetesScale * 6.67 * 65.421 / (1.0 + exp((2.0 - 2.0*bloodGlucoseConcentration_g_Per_L) / 0.3)); + + //m_data.GetDataTrack().Probe("DiabetesScalePercent", diabetesScale * 100); GetInsulinSynthesisRate().SetValue(insulinSynthesisRate_pmol_Per_min, AmountPerTimeUnit::pmol_Per_min); @@ -183,8 +231,34 @@ void Endocrine::SynthesizeInsulin() //-------------------------------------------------------------------------------------------------- void Endocrine::SynthesizeGlucagon() { + //https://www.wolframalpha.com/input/?i=y%3D21.3-(21.3%2F(1%2Bexp((2-2x)%2F.3)))+from+.8%3Cx%3C1.2+and+0%3Cy%3C16 + double bloodGlucoseConcentration_g_Per_L = m_aortaGlucose->GetConcentration(MassPerVolumeUnit::g_Per_L); - double glucagonSynthesisRate_pmol_Per_min = 21.3 - (21.3 / (1.0 + exp((2 - 2 * bloodGlucoseConcentration_g_Per_L) / .3))); + double glucagonSynthesisRate_pmol_Per_min = 21.3 - (21.3 / (1.0 + exp((2 - 2 * bloodGlucoseConcentration_g_Per_L) / .3))); //should be ~14.07 pmol/min at .9 blood glucose (normal) + + //Diabetic patients see glucagon abnormalities chronically, but even in short time-scales we expect to see glucagon levels + //of ~40 ng/L, so we need to make sure glucagon is being produced even if blood sugar gets very high \cite brown2008too + if (m_data.GetConditions().HasDiabetesType1()) + { + double minimumGlucagonRate_pmol_Per_min = 0; + if (m_data.GetConditions().GetDiabetesType1()->HasInsulinProductionSeverity()) + minimumGlucagonRate_pmol_Per_min = GeneralMath::LinearInterpolator(0,1,0,.57*14.07,m_data.GetConditions().GetDiabetesType1()->GetInsulinProductionSeverity().GetValue()); + glucagonSynthesisRate_pmol_Per_min = MAX(glucagonSynthesisRate_pmol_Per_min, minimumGlucagonRate_pmol_Per_min); + } + else if (m_data.GetConditions().HasDiabetesType2()) + { + double minimumGlucagonRate_pmol_Per_min = 0; + double totalEffect = 0; + if (m_data.GetConditions().GetDiabetesType2()->HasInsulinProductionSeverity()) + totalEffect += m_data.GetConditions().GetDiabetesType2()->GetInsulinProductionSeverity().GetValue(); + if (m_data.GetConditions().GetDiabetesType2()->HasInsulinResistanceSeverity()) + totalEffect += m_data.GetConditions().GetDiabetesType2()->GetInsulinResistanceSeverity().GetValue(); + + BLIM(totalEffect, 0, 1); + + minimumGlucagonRate_pmol_Per_min = GeneralMath::LinearInterpolator(0, 1, 0, .57*14.07, totalEffect); + glucagonSynthesisRate_pmol_Per_min = MAX(glucagonSynthesisRate_pmol_Per_min, minimumGlucagonRate_pmol_Per_min); + } GetGlucagonSynthesisRate().SetValue(glucagonSynthesisRate_pmol_Per_min, AmountPerTimeUnit::pmol_Per_min); diff --git a/src/engine/cpp/Systems/Energy.cpp b/src/engine/cpp/Systems/Energy.cpp index 72baba0..180205a 100644 --- a/src/engine/cpp/Systems/Energy.cpp +++ b/src/engine/cpp/Systems/Energy.cpp @@ -19,9 +19,7 @@ specific language governing permissions and limitations under the License. #include "bind/RunningAverageData.hxx" #include "patient/SEPatient.h" -#include "patient/SEMeal.h" #include "patient/SENutrition.h" -#include "patient/conditions/SEConsumeMeal.h" #include "circuit/thermal/SEThermalCircuit.h" #include "circuit/fluid/SEFluidCircuit.h" #include "compartment/fluid/SELiquidCompartment.h" @@ -52,6 +50,7 @@ specific language governing permissions and limitations under the License. #include "properties/SEScalarAmountPerTime.h" #include "properties/SEScalarTime.h" #include "properties/SEScalarVolumePerTimeMass.h" +#include "properties/SEScalarMassPerAmount.h" Energy::Energy(BioGears& bg) : SEEnergySystem(bg.GetLogger()), m_data(bg), m_circuitCalculator(GetLogger()) { @@ -69,6 +68,9 @@ void Energy::Clear() m_Patient = nullptr; m_PatientActions = nullptr; m_AortaHCO3 = nullptr; + m_SkinSodium = nullptr; + m_SkinChloride = nullptr; + m_SkinPotassium = nullptr; m_coreNode = nullptr; m_skinNode = nullptr; m_temperatureGroundToCorePath = nullptr; @@ -96,32 +98,23 @@ void Energy::Initialize() GetSkinTemperature().SetValue(33.0, TemperatureUnit::C); /// \cite phypers2006lactate GetLactateProductionRate().SetValue(1.3, AmountPerTimeUnit::mol_Per_day); - /// \cite garber1974ketone - GetKetoneProductionRate().SetValue(300.0, AmountPerTimeUnit::umol_Per_min); /// \cite guyton2006medical GetExerciseMeanArterialPressureDelta().SetValue(0.0, PressureUnit::mmHg); GetTotalWorkRateLevel().SetValue(0.0); GetFatigueLevel().SetValue(0.0); + GetChlorideLostToSweat().SetValue(0.0, MassUnit::mg); + GetPotassiumLostToSweat().SetValue(0.0, MassUnit::mg); + GetSodiumLostToSweat().SetValue(0.0, MassUnit::mg); //Running average quantities used to trigger events m_BloodpH.Sample(7.4); //Initialize m_BicarbonateMolarity_mmol_Per_L.Sample(24.0); //Initialize - - // Energy buckets for fatigue - m_UsableEnergyStore_J = 2600.0; - m_PeakPowerEnergyStore_J = 4200.0; - m_MediumPowerEnergyStore_J = 35000.0; - m_EnduranceEnergyStore_J = 400000.0; } bool Energy::Load(const CDM::BioGearsEnergySystemData& in) { if (!SEEnergySystem::Load(in)) return false; - m_UsableEnergyStore_J = in.UsableEnergyStore_J(); - m_PeakPowerEnergyStore_J = in.PeakPowerEnergyStore_J(); - m_MediumPowerEnergyStore_J = in.MediumPowerEnergyStore_J(); - m_EnduranceEnergyStore_J = in.EnduranceEnergyStore_J(); m_BloodpH.Load(in.BloodpH()); m_BicarbonateMolarity_mmol_Per_L.Load(in.BicarbonateMolarity_mmol_Per_L()); @@ -137,10 +130,6 @@ CDM::BioGearsEnergySystemData* Energy::Unload() const void Energy::Unload(CDM::BioGearsEnergySystemData& data) const { SEEnergySystem::Unload(data); - data.UsableEnergyStore_J(m_UsableEnergyStore_J); - data.PeakPowerEnergyStore_J(m_PeakPowerEnergyStore_J); - data.MediumPowerEnergyStore_J(m_MediumPowerEnergyStore_J); - data.EnduranceEnergyStore_J(m_EnduranceEnergyStore_J); data.BloodpH(std::unique_ptr(m_BloodpH.Unload())); data.BicarbonateMolarity_mmol_Per_L(std::unique_ptr(m_BicarbonateMolarity_mmol_Per_L.Unload())); @@ -163,7 +152,9 @@ void Energy::SetUp() m_Patient = &m_data.GetPatient(); m_AortaHCO3 = m_data.GetCompartments().GetLiquidCompartment(BGE::VascularCompartment::Aorta)->GetSubstanceQuantity(m_data.GetSubstances().GetHCO3()); - + m_SkinSodium = m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::SkinExtracellular)->GetSubstanceQuantity(m_data.GetSubstances().GetSodium()); + m_SkinChloride = m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::SkinExtracellular)->GetSubstanceQuantity(m_data.GetSubstances().GetChloride()); + m_SkinPotassium = m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::SkinExtracellular)->GetSubstanceQuantity(m_data.GetSubstances().GetPotassium()); //Circuit elements //Circuits m_TemperatureCircuit = &m_data.GetCircuits().GetTemperatureCircuit(); @@ -216,61 +207,11 @@ void Energy::SetUp() // m_Patient->GetWeight().IncrementValue(-patientMassLost_kg, MassUnit::kg); //} -//-------------------------------------------------------------------------------------------------- -/// \brief -/// Applies an imposed starvation time to the current nutrient stores -/// -/// \details -/// Starvation uses a coarse time-step approach with an assumed time-step of one day. This coarse time-step -/// is used to to manually decrement all of the nutrient stores which represents the loss due to metabolic consumption. -/// For time durations greater than one day, there is a decrease in the basal metabolic rate due to mass loss from increased -/// starvation times. -//-------------------------------------------------------------------------------------------------- -// Starvation currently disabled -//void Energy::Starvation(double time) -//{ -// if (time < 1.0) //Less than one day without food. Decrement according to consumption/production methodology -// { -// //CalculateNutrientConsumptionAndProduction(Convert(time, TimeUnit::day, TimeUnit::s)); //TODO:: Add in clearances if they are needed -// //BalanceSubstances(); -// } -// else -// { -// unsigned int numTimeSteps = (int)time; -// double deltaT_days = 1.0; -// for (unsigned int iStep = 0; iStep < numTimeSteps; iStep++) -// { -// CalculateNutrientConsumptionAndProduction(Convert(deltaT_days, TimeUnit::day, TimeUnit::s)); -// CalculateBasalMetabolicRate(); -// GetTotalMetabolicRate().SetValue(m_PatientBMR_kcal_Per_day, PowerUnit::kcal_Per_day); //Metabolic rate decrease due to reduction in body mass -// //BalanceSubstances(); -// } -// } -//} - - void Energy::AtSteadyState() { if (m_data.GetState() == EngineState::AtInitialStableState) { - //TODO: Get consume meal files - // Consume meal condition currently disabled - if (m_data.GetConditions().HasConsumeMeal()) - { - Warning("Consume Meal Condition is currently disabled."); - // double elapsedTime = m_data.GetConditions().GetConsumeMeal()->GetMeal().GetElapsedTime(TimeUnit::day); - // Dehydration(elapsedTime); - // m_AdjustCO2 = false;// Don't mess with CO2 (in the blood) during this Starvation call - // Starvation(elapsedTime); - // m_AdjustCO2 = true; - // //Systemic clearance of calcium occurs over the elapsed time period - // double patientWeight_kg = m_Patient->GetWeight(MassUnit::kg); - // double renalVolumeCleared = m_calcium->GetClearance().GetRenalClearance(VolumePerTimeMassUnit::mL_Per_s_kg)*patientWeight_kg*elapsedTime; - // double systemicVolumeCleared = m_calcium->GetClearance().GetSystemicClearance(VolumePerTimeMassUnit::mL_Per_s_kg)*patientWeight_kg*elapsedTime - renalVolumeCleared; - // SEScalarVolume* integratedVolume = new SEScalarVolume(); - // integratedVolume->SetValue(systemicVolumeCleared, VolumeUnit::mL); - // m_data.GetCircuits().BalanceBloodMassByClearedVolume(*m_calcium, *integratedVolume); - } + } } @@ -401,9 +342,9 @@ void Energy::CalculateVitalSigns() m_Patient->SetEvent(CDM::enumPatientEvent::Hypothermia, false, m_data.GetSimulationTime()); } //Hyperthermia check - if (coreTemperature_degC > 38.8) /// \cite mallet2001hypothermia + if (coreTemperature_degC > 38.8) // Note: Hyperthermia threshold varies; we'll use 38.8 { - /// \event Patient: Core temperature has exceeded 38.3 degrees Celsius. Patient is hyperthermic. + /// \event Patient: Core temperature has exceeded 38.8 degrees Celsius. Patient is hyperthermic. m_Patient->SetEvent(CDM::enumPatientEvent::Hyperthermia, true, m_data.GetSimulationTime()); } else if (m_Patient->IsEventActive(CDM::enumPatientEvent::Hyperthermia) && coreTemperature_degC < 38.0) @@ -496,13 +437,13 @@ void Energy::CalculateMetabolicHeatGeneration() } else if (coreTemperature_degC >= 34.0 && coreTemperature_degC < 36.8) //Patient is increasing heat generation via shivering. This caps out at the summit metabolism { - //Add an event for shivering + //Todo: Add an event for shivering double basalMetabolicRate_W = m_Patient->GetBasalMetabolicRate(PowerUnit::W); totalMetabolicRateNew_W = basalMetabolicRate_W + (summitMetabolism_W - basalMetabolicRate_W)*(coreTemperatureLow_degC - coreTemperature_degC) / coreTemperatureLowDelta_degC; totalMetabolicRateNew_W = MIN(totalMetabolicRateNew_W, summitMetabolism_W); //Bounded at the summit metabolism so further heat generation doesn't continue for continue drops below 34 C. GetTotalMetabolicRate().SetValue(totalMetabolicRateNew_W, PowerUnit::W); } - else if (coreTemperature_degC >= 36.8 && coreTemperature_degC < 42.5 && !m_PatientActions->HasExercise()) //Basic Metabolic rate + else if (coreTemperature_degC >= 36.8 && coreTemperature_degC < 40 && !m_PatientActions->HasExercise()) //Basic Metabolic rate { double TotalMetabolicRateSetPoint_kcal_Per_day = basalMetabolicRate_kcal_Per_day; double MetabolicRateGain = 0.0001; //Used to ramp the metabolic rate from its current value to the basal value if the patient meets the basal criteria @@ -533,27 +474,60 @@ void Energy::CalculateSweatRate() double coreTemperatureHigh_degC = config.GetCoreTemperatureHigh(TemperatureUnit::C); double sweatHeatTranferCoefficient_W_Per_K = config.GetSweatHeatTransfer(HeatConductanceUnit::W_Per_K); double vaporizationEnergy_J_Per_kg = config.GetVaporizationEnergy(EnergyPerMassUnit::J_Per_kg); + double sweatSodiumConcentration_mM = 51.0; /// \cite shirreffs1997whole + double sweatPotassiumConcentration_mM = 6.0; /// \cite shirreffs1997whole + double sweatChlorideConcentration_mM = 48.0; /// \cite shirreffs1997whole + static double totalSweatLost_mL = 0; + + /// \todo Convert to sweat density once specific gravity calculation is in + SEScalarMassPerVolume sweatDensity; + GeneralMath::CalculateWaterDensity(m_skinNode->GetTemperature(), sweatDensity); + double dehydrationFraction = m_data.GetTissue().GetDehydrationFraction().GetValue(); + + //m_data.GetDataTrack().Probe("DehydrationPercentage", dehydrationFraction*100); //Calculate sweat rate (in kg/s) from core temperature feedback. //The sweat rate heat transfer is determined from a control equation that attempts to keep the core temperature in line /// \cite herman2008physics - double sweatRate_kg_Per_s = (0.25*sweatHeatTranferCoefficient_W_Per_K / vaporizationEnergy_J_Per_kg)*(coreTemperature_degC - coreTemperatureHigh_degC); - sweatRate_kg_Per_s = MAX(sweatRate_kg_Per_s, 0.0); + //Sweat rate decreases as dehydration becomes more severe, with max reduction seen at 10% dehydration + double dehydrationScalingFactor = GeneralMath::LinearInterpolator(0, .1, 1, 0, dehydrationFraction); + BLIM(dehydrationScalingFactor, 0, 1); + + //m_data.GetDataTrack().Probe("DehydrationScalingFactor", dehydrationScalingFactor); + + double sweatRate_kg_Per_s = dehydrationScalingFactor*(0.25*sweatHeatTranferCoefficient_W_Per_K / vaporizationEnergy_J_Per_kg)*(coreTemperature_degC - coreTemperatureHigh_degC); + double maxSweatRate_kg_Per_s = 12.5 * m_Patient->GetSkinSurfaceArea().GetValue(AreaUnit::m2) / 60 / 1000; //10 - 15 g/min/m2 + BLIM(sweatRate_kg_Per_s, 0.0, maxSweatRate_kg_Per_s); + //m_data.GetDataTrack().Probe("SweatRate_g_Per_s", sweatRate_kg_Per_s*1000); - //Account for mass lost by substracting from the current patient mass + //Account for mass lost by subtracting from the current patient mass double massLost_kg = sweatRate_kg_Per_s*m_dT_s; m_Patient->GetWeight().IncrementValue(-massLost_kg, MassUnit::kg); - GetSweatRate().SetValue(sweatRate_kg_Per_s, MassPerTimeUnit::kg_Per_s); - double sweatDensity_kg_Per_m3 = config.GetWaterDensity(MassPerVolumeUnit::kg_Per_m3); /// \todo Convert to sweat density once specific gravity calculation is in + //Calculate mass of ions lost in sweat (sodium, potassium, and chloride): Converts kg sweat lost -> L sweat lost -> mmol ion lost -> mg ion lost + double sodiumLost_mg = massLost_kg / sweatDensity.GetValue(MassPerVolumeUnit::kg_Per_L) * sweatSodiumConcentration_mM * m_data.GetSubstances().GetSodium().GetMolarMass(MassPerAmountUnit::mg_Per_mmol); + double potassiumLost_mg = massLost_kg / sweatDensity.GetValue(MassPerVolumeUnit::kg_Per_L) * sweatPotassiumConcentration_mM * m_data.GetSubstances().GetPotassium().GetMolarMass(MassPerAmountUnit::mg_Per_mmol); + double chlorideLost_mg = massLost_kg / sweatDensity.GetValue(MassPerVolumeUnit::kg_Per_L) * sweatChlorideConcentration_mM * m_data.GetSubstances().GetChloride().GetMolarMass(MassPerAmountUnit::mg_Per_mmol); + + //Decrement amount of each ion in the skin extracellular compartment, track the cumulative amount removed for output, and balance (i.e. update concentration) remaining levels + m_SkinSodium->GetMass().IncrementValue(-sodiumLost_mg, MassUnit::mg); + GetSodiumLostToSweat().IncrementValue(sodiumLost_mg, MassUnit::mg); + m_SkinPotassium->GetMass().IncrementValue(-potassiumLost_mg, MassUnit::mg); + GetPotassiumLostToSweat().IncrementValue(potassiumLost_mg, MassUnit::mg); + m_SkinChloride->GetMass().IncrementValue(-chlorideLost_mg, MassUnit::mg); + GetChlorideLostToSweat().IncrementValue(chlorideLost_mg, MassUnit::mg); + m_SkinSodium->Balance(BalanceLiquidBy::Mass); + m_SkinPotassium->Balance(BalanceLiquidBy::Mass); + m_SkinChloride->Balance(BalanceLiquidBy::Mass); //Set the flow source on the extravascular circuit to begin removing the fluid that is excreted - double sweatRate_mL_Per_s = sweatRate_kg_Per_s / sweatDensity_kg_Per_m3 * 1.e6; - //m_data.GetDataTrack().Probe("sweatRate_mL_Per_s", sweatRate_mL_Per_s); - //m_data.GetDataTrack().Probe("sweatRate_mg_Per_min", sweatRate_kg_Per_s*60.*1000.*1000.); - m_skinExtravascularToSweatingGroundPath->GetNextFlowSource().SetValue(sweatRate_mL_Per_s, VolumePerTimeUnit::mL_Per_s); + double sweatRate_mL_Per_s = sweatRate_kg_Per_s / sweatDensity.GetValue(MassPerVolumeUnit::kg_Per_mL); + m_skinExtravascularToSweatingGroundPath->GetNextFlowSource().SetValue(sweatRate_mL_Per_s, VolumePerTimeUnit::mL_Per_s); + totalSweatLost_mL += sweatRate_mL_Per_s * m_dT_s; + + //m_data.GetDataTrack().Probe("CumulativeSweatLost_mL", totalSweatLost_mL); } //-------------------------------------------------------------------------------------------------- @@ -570,7 +544,7 @@ void Energy::UpdateHeatResistance() double bloodDensity_kg_Per_m3 = m_data.GetBloodChemistry().GetBloodDensity().GetValue(MassPerVolumeUnit::kg_Per_m3); double bloodSpecificHeat_J_Per_K_kg = m_data.GetBloodChemistry().GetBloodSpecificHeat().GetValue(HeatCapacitancePerMassUnit::J_Per_K_kg); - double alphaScale = 0.5; //Scaling factor for convective heat transfer from core to skin + double alphaScale = .5; //Scaling factor for convective heat transfer from core to skin (35 seems to be near the upper limit before non-stabilization) //The heat transfer resistance from the core to the skin is inversely proportional to the skin blood flow. //When skin blood flow increases, then heat transfer resistance decreases leading to more heat transfer from core to skin. @@ -578,6 +552,7 @@ void Energy::UpdateHeatResistance() double coreToSkinResistance_K_Per_W = 1.0 / (alphaScale*bloodDensity_kg_Per_m3*bloodSpecificHeat_J_Per_K_kg*skinBloodFlow_m3_Per_s); coreToSkinResistance_K_Per_W = BLIM(coreToSkinResistance_K_Per_W, 0.0001, 20.0); + //m_data.GetDataTrack().Probe("CoreToSkinResistance", coreToSkinResistance_K_Per_W); m_coreToSkinPath->GetNextResistance().SetValue(coreToSkinResistance_K_Per_W, HeatResistanceUnit::K_Per_W); } diff --git a/src/engine/cpp/Systems/Energy.h b/src/engine/cpp/Systems/Energy.h index b0cdb9b..1ef57e1 100644 --- a/src/engine/cpp/Systems/Energy.h +++ b/src/engine/cpp/Systems/Energy.h @@ -44,7 +44,7 @@ class BIOGEARS_API Energy : public SEEnergySystem, public BioGearsSystem protected: virtual void Unload(CDM::BioGearsEnergySystemData& data) const; - // Set pointers and other member varialbes common to both homeostatic initialization and loading a state + // Set pointers and other member variables common to both homeostatic initialization and loading a state void SetUp(); public: @@ -61,10 +61,10 @@ class BIOGEARS_API Energy : public SEEnergySystem, public BioGearsSystem void UpdateHeatResistance(); // Actions/Conditions void Exercise(); - void OldExercise(); + // These are both part of the consume meal condition. //void Dehydration(double time); // Need to revisit - //void Starvation(double time); // Need to revisit + // Used in Reset & Starvation void CalculateBasalMetabolicRate(); @@ -72,10 +72,6 @@ class BIOGEARS_API Energy : public SEEnergySystem, public BioGearsSystem void CalculateVitalSigns(); // Serializable member variables (Set in Initialize and in schema) - double m_UsableEnergyStore_J; - double m_PeakPowerEnergyStore_J; - double m_MediumPowerEnergyStore_J; - double m_EnduranceEnergyStore_J; RunningAverage m_BloodpH; RunningAverage m_BicarbonateMolarity_mmol_Per_L; @@ -84,7 +80,10 @@ class BIOGEARS_API Energy : public SEEnergySystem, public BioGearsSystem SEPatient* m_Patient; SEPatientActionCollection* m_PatientActions; // Cmpts,Substance, and nodes - SELiquidSubstanceQuantity* m_AortaHCO3; + SELiquidSubstanceQuantity* m_AortaHCO3; + SELiquidSubstanceQuantity* m_SkinSodium; + SELiquidSubstanceQuantity* m_SkinChloride; + SELiquidSubstanceQuantity* m_SkinPotassium; //Nodes SEThermalCircuitNode* m_coreNode; SEThermalCircuitNode* m_skinNode; diff --git a/src/engine/cpp/Systems/Environment.cpp b/src/engine/cpp/Systems/Environment.cpp index 8ec3130..b27cad3 100644 --- a/src/engine/cpp/Systems/Environment.cpp +++ b/src/engine/cpp/Systems/Environment.cpp @@ -242,7 +242,7 @@ void Environment::PreProcess() //Set clothing resistor double dClothingResistance_rsi = GetConditions().GetClothingResistance(HeatResistanceAreaUnit::rsi); //1 rsi = 1 m^2-K/W double dSurfaceArea_m2 = m_Patient->GetSkinSurfaceArea(AreaUnit::m2); - m_SkinToClothing->GetNextResistance().SetValue(dClothingResistance_rsi / dSurfaceArea_m2, HeatResistanceUnit::K_Per_W); + m_SkinToClothing->GetNextResistance().SetValue(MAX((dClothingResistance_rsi / dSurfaceArea_m2), m_data.GetConfiguration().GetDefaultClosedHeatResistance(HeatResistanceUnit::K_Per_W)), HeatResistanceUnit::K_Per_W); //Set the skin heat loss double dSkinHeatLoss_W = 0.0; @@ -598,12 +598,13 @@ void Environment::CalculateConvection() //Submerged - therefore, convection is most important double dClothingTemperature_K = m_ClothingNode->GetTemperature().GetValue(TemperatureUnit::K); double dWaterTemperature_K = GetConditions().GetAmbientTemperature(TemperatureUnit::K); - double dWaterDensity_kg_Per_m3 = m_data.GetConfiguration().GetWaterDensity(MassPerVolumeUnit::kg_Per_m3); + SEScalarMassPerVolume dWaterDensity; + GeneralMath::CalculateWaterDensity(GetConditions().GetAmbientTemperature(), dWaterDensity); double dGravity_m_Per_s2 = 9.81; //Calculate the coefficient //Heat transfer coefficient for submerged water convection. C. Boutelier et al. Experimental study of convective heat transfer coefficient for the human body in water. Journal of Applied Physiology. 1977. Vol. 42. p.93-100 - double dGrashofNumber = dGravity_m_Per_s2*m_ThermalExpansion_Per_K*(std::abs(dClothingTemperature_K - dWaterTemperature_K))*pow(m_PatientEquivalentDiameter_m, 3.0) / (m_WaterViscosity_N_s_Per_m2 / dWaterDensity_kg_Per_m3); + double dGrashofNumber = dGravity_m_Per_s2*m_ThermalExpansion_Per_K*(std::abs(dClothingTemperature_K - dWaterTemperature_K))*pow(m_PatientEquivalentDiameter_m, 3.0) / (m_WaterViscosity_N_s_Per_m2 / dWaterDensity.GetValue(MassPerVolumeUnit::kg_Per_m3)); double dPrandtlNumber = m_WaterSpecificHeat_J_Per_kg_K*m_WaterViscosity_N_s_Per_m2 / m_WaterThermalConductivity_W_Per_m_K; dConvectiveHeatTransferCoefficient_WPerM2_K = 0.09*(dGrashofNumber - dPrandtlNumber)*0.275; } diff --git a/src/engine/cpp/Systems/Gastrointestinal.cpp b/src/engine/cpp/Systems/Gastrointestinal.cpp index fe3fe98..5ac2bd5 100644 --- a/src/engine/cpp/Systems/Gastrointestinal.cpp +++ b/src/engine/cpp/Systems/Gastrointestinal.cpp @@ -15,9 +15,7 @@ specific language governing permissions and limitations under the License. #include "system/physiology/SEBloodChemistrySystem.h" #include "system/physiology/SECardiovascularSystem.h" #include "substance/SESubstance.h" -#include "patient/SEMeal.h" #include "patient/SENutrition.h" -#include "patient/conditions/SEConsumeMeal.h" #include "patient/actions/SEConsumeNutrients.h" #include "circuit/fluid/SEFluidCircuit.h" #include "properties/SEScalar.h" @@ -86,10 +84,8 @@ void Gastrointestinal::Initialize() // We are going to initialize the body with 2 meals so we process the default meal twice // 1 meal about 5hrs ago, and one meal at the start of the scenario CDM_COPY((m_data.GetConfiguration().GetDefaultStomachContents()), (&GetStomachContents())); - m_data.GetPatient().GetWeight().IncrementValue(2 * m_StomachContents->GetWeight(MassUnit::g), MassUnit::g); - // Now digest the contents - // DigestStomachNutrients(5 * 60 * 60);//hrs to seconds (note decrement is off, so the stomach will stay full) - // TODO Should I be getting the weight here? After we digest? + m_data.GetPatient().GetWeight().IncrementValue( m_StomachContents->GetWeight(MassUnit::g), MassUnit::g); + } // Cache off the initial Gut masses so we can reset back to them after stabilization m_InitialSubstanceMasses_ug[m_SmallIntestineChymeAminoAcids] = m_SmallIntestineChymeAminoAcids->GetMass(MassUnit::ug); @@ -146,6 +142,7 @@ void Gastrointestinal::SetUp() m_SmallIntestineVascularSodium = m_vSmallIntestine->GetSubstanceQuantity(m_data.GetSubstances().GetSodium()); m_smallIntestineVascularGlucose = m_vSmallIntestine->GetSubstanceQuantity(m_data.GetSubstances().GetGlucose()); m_smallIntestineVascularAminoAcids = m_vSmallIntestine->GetSubstanceQuantity(m_data.GetSubstances().GetAminoAcids()); + m_SmallIntestineVascularCalcium = m_vSmallIntestine->GetSubstanceQuantity(m_data.GetSubstances().GetCalcium()); m_LymphTriacylglycerol = m_Lymph->GetSubstanceQuantity(m_data.GetSubstances().GetTriacylglycerol()); m_smallintestineVAscularTriacylglycerol = m_vSmallIntestine->GetSubstanceQuantity(m_data.GetSubstances().GetTriacylglycerol()); } @@ -162,7 +159,9 @@ void Gastrointestinal::SetUp() void Gastrointestinal::AtSteadyState() { if (m_data.GetState() == EngineState::AtInitialStableState) - {// Apply our conditions + { + /* + // Apply our conditions if (m_data.GetConditions().HasConsumeMeal()) { SEMeal& meal = m_data.GetConditions().GetConsumeMeal()->GetMeal(); @@ -248,6 +247,7 @@ void Gastrointestinal::AtSteadyState() m_InitialSubstanceMasses_ug[m_SmallIntestineChymeSodium] = m_SmallIntestineChymeSodium->GetMass(MassUnit::ug); m_InitialSubstanceMasses_ug[m_SmallIntestineChymeUrea] = m_SmallIntestineChymeUrea->GetMass(MassUnit::ug); } + */ } if (m_data.GetState() == EngineState::AtSecondaryStableState) { @@ -327,126 +327,6 @@ void Gastrointestinal::GastricSecretion(double duration_s) m_StomachContents->GetWater().IncrementValue(m_secretionRate_mL_Per_s * duration_s, VolumeUnit::mL); } -//-------------------------------------------------------------------------------------------------- -/// \brief -/// Digest substances based on our time step and transfer them to the Gut -/// -/// \details -/// Each substance can provide a digestion rate, if none is provided a default will be used -/// A portion of each substance is removed from the stomach based on time step and digestion rate -/// The substance is then incremented into the Gut. Each substance can have its own movement logic -/// Water is also transfered to the Gut based on a constant rate -//-------------------------------------------------------------------------------------------------- -void Gastrointestinal::DigestStomachNutrients(double duration_s) -{ - if (duration_s <= 0) - return; // Nothing to do then - if (!m_StomachContents->HasWater()) - Fatal("There is no water in the Stomach??");// This is not good... what is going on? - - double digestedAmount;// either in g or mL - - if (m_StomachContents->HasSodium()) - {// Sodium rate is a function of the concentration of sodium in the stomach, so do this before we do water - double totalNa_g = m_StomachContents->GetSodium(MassUnit::g); - double digestedNa_g = (totalNa_g / m_StomachContents->GetWater(VolumeUnit::mL)) - * m_WaterDigestionRate.GetValue(VolumePerTimeUnit::mL_Per_s) * duration_s; - if (totalNa_g <= digestedNa_g) - { - digestedNa_g = totalNa_g; - if (m_DecrementNutrients) - { - m_StomachContents->GetSodium().Invalidate(); - Info("Stomach is out of Sodium"); - } - } - else - { - if (m_DecrementNutrients) - m_StomachContents->GetSodium().IncrementValue(-digestedNa_g, MassUnit::g); - } -#ifdef logDigest - m_ss << "Digested " << digestedNa_g << "(g) of Sodium"; - Info(m_ss); -#endif - m_SmallIntestineChymeSodium->GetMass().IncrementValue(digestedNa_g, MassUnit::g); - // Wait till the water volume is correct on the chyme before we balance - } - - digestedAmount = DigestNutrient(m_StomachContents->GetWater(), m_WaterDigestionRate, false, duration_s); - if (digestedAmount > 0) - { -#ifdef logDigest - m_ss << "Digested " << digestedAmount << "(mL) of Water"; - Info(m_ss); -#endif - m_SmallIntestineChyme->GetVolume().IncrementValue(digestedAmount, VolumeUnit::mL); - } - // Balance Sodium, now that we have proper volume on the gut - m_SmallIntestineChymeSodium->Balance(BalanceLiquidBy::Mass); - - m_ConsumeRate = true;// We are digesting nutrient rates provided by our consume action, if we run out of a substance, we invalidate this rate - if (m_StomachContents->HasCarbohydrate()) - { - digestedAmount = DigestNutrient(m_StomachContents->GetCarbohydrate(), m_StomachContents->GetCarbohydrateDigestionRate(), true, duration_s); - digestedAmount *= m_data.GetConfiguration().GetCarbohydrateAbsorptionFraction(); // Take off percent that usually passes through the body - if (digestedAmount != 0) - { -#ifdef logDigest - m_ss << "Digested " << digestedAmount << "(g) of Carbs"; - Info(m_ss); -#endif - m_SmallIntestineChymeGlucose->GetMass().IncrementValue(digestedAmount, MassUnit::g); - m_SmallIntestineChymeGlucose->Balance(BalanceLiquidBy::Mass); - } - } - if (m_StomachContents->HasFat()) - { - digestedAmount = DigestNutrient(m_StomachContents->GetFat(), m_StomachContents->GetFatDigestionRate(), true, duration_s); - digestedAmount *= m_data.GetConfiguration().GetFatAbsorptionFraction(); // Take off percent that usually passes through the body - if (digestedAmount != 0) - { -#ifdef logDigest - m_ss << "Digested " << digestedAmount << "(g) of Fat"; - Info(m_ss); -#endif - m_SmallIntestineChymeTriacylglycerol->GetMass().IncrementValue(digestedAmount, MassUnit::g); - m_SmallIntestineChymeTriacylglycerol->Balance(BalanceLiquidBy::Mass); - } - } - if (m_StomachContents->HasProtein()) //If this functionality is being kept with the GI overhaul, use AminoAcids - { - digestedAmount = DigestNutrient(m_StomachContents->GetProtein(), m_StomachContents->GetProteinDigestionRate(), true, duration_s); - digestedAmount *= m_data.GetConfiguration().GetProteinToUreaFraction(); // How much gets turned into Urea - if (digestedAmount != 0) - { -#ifdef logDigest - m_ss << "Digested " << digestedAmount << "(g) of Urea"; - Info(m_ss); -#endif - double tuningFactor = 1.0; /// \todo Remove tuning factor and adjust protein to urea fraction following investigation - m_SmallIntestineChymeUrea->GetMass().IncrementValue(digestedAmount * tuningFactor, MassUnit::g); - m_SmallIntestineChymeUrea->Balance(BalanceLiquidBy::Mass); - } - } - m_ConsumeRate = false; // Other rates are specified by configuration and should never be invalidated - - if (m_StomachContents->HasCalcium()) - { - digestedAmount = DigestNutrient(m_StomachContents->GetCalcium(), m_CalciumDigestionRate, true, duration_s); - digestedAmount *= m_data.GetConfiguration().GetCalciumAbsorptionFraction(); // Take off percent that usually passes through the body - if (digestedAmount != 0) - { -#ifdef logDigest - m_ss << "Digested " << digestedAmount << "(g) of Calcium"; - Info(m_ss); -#endif - m_SmallIntestineChymeCalcium->GetMass().IncrementValue(digestedAmount, MassUnit::g); - m_SmallIntestineChymeCalcium->Balance(BalanceLiquidBy::Mass); - } - } -} - // -------------------------------------------------------------------------------------------------- /// \brief /// Digestion code to convert nutrients into appropriate product due to enzyme interaction @@ -677,7 +557,7 @@ void Gastrointestinal::AbsorbNutrients() double sodiumAbsorption_g_Per_h = 0.0; double sodiumAbsorbed_g = 0.0; - double smallIntestineVolume_mL; + double smallIntestineVolume_mL = m_SmallIntestineChyme->GetVolume(VolumeUnit::mL); //parameters for nutrient absorption: double glucoseAbsorbed_g = 0.0; @@ -742,31 +622,59 @@ void Gastrointestinal::AbsorbNutrients() } } - double absorptionRate_mL_Per_min = 3.3; // Average water absorption rate Peronnet + //compute absorption rate as a function of volume in intestine + double absorptionRate_mL_Per_min = 0.0; //3.3; // Average water absorption rate Peronnet + absorptionRate_mL_Per_min = GeneralMath::LogisticFunction(13, 500, 0.007, smallIntestineVolume_mL); double absorbedVolume_mL = absorptionRate_mL_Per_min / 60.0 * m_dT_s; //move fluid if (m_data.GetState() == EngineState::Active) {// Don't Remove volume while stabilizing - smallIntestineVolume_mL = m_vSmallIntestine->GetVolume().GetValue(VolumeUnit::mL); - if (m_SmallIntestineChyme->GetVolume(VolumeUnit::mL) > absorbedVolume_mL) + if (smallIntestineVolume_mL > absorbedVolume_mL) { m_GItoCVPath->GetNextFlowSource().SetValue(absorptionRate_mL_Per_min, VolumePerTimeUnit::mL_Per_min); m_GItoCVPath->GetSourceNode().GetNextVolume().IncrementValue(-absorbedVolume_mL, VolumeUnit::mL); - - // Calculate new concentrations for everything based on new volume - for (SELiquidSubstanceQuantity* subQ : m_SmallIntestineChyme->GetSubstanceQuantities()) - { - if (subQ->HasMass()) - subQ->Balance(BalanceLiquidBy::Mass); - } - for (SELiquidSubstanceQuantity* subQ : m_vSmallIntestine->GetSubstanceQuantities()) - { - if (subQ->HasMass()) - subQ->Balance(BalanceLiquidBy::Mass); - } } } + + //only move sodium independently if it wasn't moved through the co-transporter + if (sodiumAbsorbed_g < ZERO_APPROX) + { + double ionMassMoved_mg = m_SmallIntestineChymeSodium->GetConcentration().GetValue(MassPerVolumeUnit::mg_Per_mL)*absorbedVolume_mL; + + //substance connect ions to flow rate when nutrients aren't present, needs to have volume present + if (m_SmallIntestineChymeSodium->GetMass().GetValue(MassUnit::mg) > ionMassMoved_mg && m_SmallIntestineChyme->GetVolume(VolumeUnit::mL) > absorbedVolume_mL) + { + m_SmallIntestineChymeSodium->GetMass().IncrementValue(-ionMassMoved_mg, MassUnit::mg); + m_SmallIntestineVascularSodium->GetMass().IncrementValue(ionMassMoved_mg, MassUnit::mg); + m_SmallIntestineChymeSodium->Balance(BalanceLiquidBy::Mass); + m_SmallIntestineVascularSodium->Balance(BalanceLiquidBy::Mass); + } + } + + double ionCalciumMassMoved_mg = m_SmallIntestineChymeCalcium->GetConcentration().GetValue(MassPerVolumeUnit::mg_Per_mL)*absorbedVolume_mL; + //Move calcium regardless + if (m_SmallIntestineChymeCalcium->GetMass().GetValue(MassUnit::mg) > ionCalciumMassMoved_mg) + { + m_SmallIntestineChymeCalcium->GetMass().IncrementValue(-ionCalciumMassMoved_mg, MassUnit::mg); + m_SmallIntestineVascularCalcium->GetMass().IncrementValue(ionCalciumMassMoved_mg, MassUnit::mg); + m_SmallIntestineChymeCalcium->Balance(BalanceLiquidBy::Mass); + m_SmallIntestineVascularCalcium->Balance(BalanceLiquidBy::Mass); + } + + + // Calculate new concentrations for everything based on new volume + for (SELiquidSubstanceQuantity* subQ : m_SmallIntestineChyme->GetSubstanceQuantities()) + { + if (subQ->HasMass()) + subQ->Balance(BalanceLiquidBy::Mass); + } + for (SELiquidSubstanceQuantity* subQ : m_vSmallIntestine->GetSubstanceQuantities()) + { + if (subQ->HasMass()) + subQ->Balance(BalanceLiquidBy::Mass); + } + GetChymeAbsorptionRate().SetValue(absorbedVolume_mL/m_dT_s, VolumePerTimeUnit::mL_Per_s); } diff --git a/src/engine/cpp/Systems/Gastrointestinal.h b/src/engine/cpp/Systems/Gastrointestinal.h index b44e4dc..dd8c804 100644 --- a/src/engine/cpp/Systems/Gastrointestinal.h +++ b/src/engine/cpp/Systems/Gastrointestinal.h @@ -57,7 +57,6 @@ class BIOGEARS_API Gastrointestinal : public SEGastrointestinalSystem, public Bi void GastricSecretion(double duration_s); void DefaultNutritionRates(SENutrition& n); void AbsorbNutrients(); - void DigestStomachNutrients(double duration_s); double DigestNutrient(SEUnitScalar& totalAmt, SEUnitScalar& rate, bool mass, double duration_s); void DigestNutrient(); void AbsorbMeal(double duration_s); @@ -80,6 +79,7 @@ class BIOGEARS_API Gastrointestinal : public SEGastrointestinalSystem, public Bi SELiquidSubstanceQuantity* m_SmallIntestineVascularSodium; SELiquidSubstanceQuantity* m_smallIntestineVascularGlucose; SELiquidSubstanceQuantity* m_smallIntestineVascularAminoAcids; + SELiquidSubstanceQuantity* m_SmallIntestineVascularCalcium; SELiquidSubstanceQuantity* m_LymphTriacylglycerol; SELiquidSubstanceQuantity* m_smallintestineVAscularTriacylglycerol; diff --git a/src/engine/cpp/Systems/Hepatic.cpp b/src/engine/cpp/Systems/Hepatic.cpp index 5838d6f..a5e324b 100644 --- a/src/engine/cpp/Systems/Hepatic.cpp +++ b/src/engine/cpp/Systems/Hepatic.cpp @@ -13,10 +13,13 @@ specific language governing permissions and limitations under the License. #include "stdafx.h" #include "Hepatic.h" +#include "patient/conditions/SEDiabetesType1.h" +#include "patient/conditions/SEDiabetesType2.h" #include "properties/SEScalarMassPerVolume.h" #include "properties/SEScalarAmountPerVolume.h" #include "properties/SEScalarMassPerAmount.h" #include "properties/SEScalarAmountPerTime.h" +#include "properties/SEScalar0To1.h" Hepatic::Hepatic(BioGears& bg) : SEHepaticSystem(bg.GetLogger()), m_data(bg) { @@ -67,6 +70,11 @@ void Hepatic::Clear() void Hepatic::Initialize() { BioGearsSystem::Initialize(); + + /// \cite garber1974ketone + GetKetoneProductionRate().SetValue(300.0, AmountPerTimeUnit::umol_Per_min); + /// \cite boron2012medical + GetHepaticGluconeogenesisRate().SetValue(180, MassPerTimeUnit::g_Per_day); } bool Hepatic::Load(const CDM::BioGearsHepaticSystemData& in) @@ -134,9 +142,8 @@ void Hepatic::SetUp() //Glycogen can make up 5-8% of liver's weight, and average liver is 1.5 kg, so max glycogen should be around 97.5 g (guyton) m_maxLiverGlycogen_g = .065 * m_data.GetCompartments().GetTissueCompartment(BGE::TissueCompartment::Liver)->GetTotalMass(MassUnit::g); - //Glycogen in muscles can make up 1-3% of their weight, but this glycogen can't diffuse out of the muscle (guyton); Average male has ~35% muscle mass, giving ~539g glycogen - //\todo get muscle mass from patient file directly - m_maxMuscleGlycogen_g = .02 * m_Patient->GetWeight(MassUnit::g) * .35; + //Glycogen in muscles can make up 1-3% of their weight, but this glycogen can't diffuse out of the muscle (guyton); + m_maxMuscleGlycogen_g = .02 * m_Patient->GetMuscleMass(MassUnit::g); m_AlbuminProdutionRate_g_Per_s = 1.5e-4; /// \cite jarnum1972plasma } @@ -220,7 +227,7 @@ void Hepatic::ProduceAlbumin(double duration_s) //-------------------------------------------------------------------------------------------------- void Hepatic::Glycogenesis() { - double hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_liverInsulin, m_liverGlucagon); + double hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_liverInsulin, m_liverGlucagon, m_data); //Useful debugging information /* @@ -230,6 +237,10 @@ void Hepatic::Glycogenesis() m_data.GetDataTrack().Probe("LiverHormoneFactor", hormoneFactor); m_data.GetDataTrack().Probe("LiverInsulinDeviation", insulinDeviation); m_data.GetDataTrack().Probe("LiverGlucagonDeviation", glucagonDeviation); + m_data.GetDataTrack().Probe("LiverGlucagonSetPoint_pg_Per_mL", m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9); + m_data.GetDataTrack().Probe("LiverInsulinSetPoint_pmol_Per_L", m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9); + m_data.GetDataTrack().Probe("LiverGlucagon_pg_Per_mL", m_liverGlucagon->GetConcentration(MassPerVolumeUnit::mg_Per_mL)*1e9); + m_data.GetDataTrack().Probe("LiverInsulin_pmol_Per_L", m_liverInsulin->GetMolarity(AmountPerVolumeUnit::mmol_Per_L)*1e9); */ @@ -258,7 +269,7 @@ void Hepatic::Glycogenesis() } //Now check the hormone factor in the muscle for storage of muscle glycogen - hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetMuscleInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetMuscleGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_muscleInsulin, m_muscleGlucagon); + hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetMuscleInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetMuscleGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_muscleInsulin, m_muscleGlucagon, m_data); //Useful debugging information /* @@ -298,7 +309,7 @@ void Hepatic::Glycogenesis() //-------------------------------------------------------------------------------------------------- void Hepatic::Glycogenolysis() { - double hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_liverInsulin, m_liverGlucagon); + double hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_liverInsulin, m_liverGlucagon, m_data); //Glycogenolysis rate double glycogenolysisLowerRate_g_Per_s = .000926; //\cite rothman1991quantitation @@ -338,9 +349,12 @@ void Hepatic::Gluconeogenesis() double ketoneProductionRate_mol_Per_s = 0; double totalO2Consumed_mol = 0; double totalGlucoseFromGluconeogenesis_mol = 0; + double glucoseFromLactate_mol = 0; + double glucoseFromGlycerol_mol = 0; + double glucoseFromAA_mol = 0; //Determine hormone factor for activity control - double hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_liverInsulin, m_liverGlucagon); + double hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_liverInsulin, m_liverGlucagon, m_data); //Handle lactate conversion //TODO should this only be done when hormone factor is negative? This would allow for some lactate excretion and maybe make it so that the blood maintains the expected 2-23 mg/dL @@ -358,6 +372,7 @@ void Hepatic::Gluconeogenesis() m_liverExtracellularLactate->GetMass().IncrementValue(-rateLimitingTuningFactor*liverLactate_mol*m_Lactate->GetMolarMass(MassPerAmountUnit::g_Per_mol), MassUnit::g); m_liverExtracellularGlucose->GetMass().IncrementValue(rateLimitingTuningFactor*reconvertedGlucose_mol*m_Glucose->GetMolarMass(MassPerAmountUnit::g_Per_mol), MassUnit::g); totalGlucoseFromGluconeogenesis_mol += rateLimitingTuningFactor*reconvertedGlucose_mol; + glucoseFromLactate_mol += rateLimitingTuningFactor*reconvertedGlucose_mol; //Handle fat conversion (glycerol to glucose and ketogenesis) //One TAG breaks down to one glycerol (which converts to glucose) and 3 palmitic acids @@ -367,10 +382,10 @@ void Hepatic::Gluconeogenesis() //A fasting person will have low levels of oxaloacetate, meaning it can't aid in converting acetyl-COA for Citric Acid Cycle //Thus, Acetyl-COA converts to ketones instead double TAGBreakdownLowerRate_g_Per_s = .0001; - double TAGBreakdownUpperRate_g_Per_s = .006; + double TAGBreakdownUpperRate_g_Per_s = .001; - //https://www.wolframalpha.com/input/?i=y%3D.0001%2B.0059%2F(1%2Be%5E(-8(x-.5)))+from+0%3Cy%3C.006+and+0%3Cx%3C2 - double TAGBreakdownRate_g_Per_s = TAGBreakdownLowerRate_g_Per_s + GeneralMath::LogisticFunction(TAGBreakdownUpperRate_g_Per_s - TAGBreakdownLowerRate_g_Per_s, .5, 8, -hormoneFactor); + //https://www.wolframalpha.com/input/?i=y%3D.0001%2B.0009%2F(1%2Be%5E(-8(x-.4)))+from+0%3Cy%3C.001and+0%3Cx%3C1 + double TAGBreakdownRate_g_Per_s = TAGBreakdownLowerRate_g_Per_s + GeneralMath::LogisticFunction(TAGBreakdownUpperRate_g_Per_s - TAGBreakdownLowerRate_g_Per_s, .4, 8, -hormoneFactor); double glucosePerTAG_mol = 1; //one glycerol backbone double ketonesPerTAG_mol = 12; //8*3 acetyl-COAs -> 12 acetoacetate (in reality, there's also acetone and B-hydroxybutyrate) @@ -391,6 +406,7 @@ void Hepatic::Gluconeogenesis() ketoneProductionRate_mol_Per_s += TAGBrokenDown_mol * ketonesPerTAG_mol / m_dt_s; totalO2Consumed_mol += TAGBrokenDown_mol * O2ConsumedToMakeKetones; totalGlucoseFromGluconeogenesis_mol += TAGBrokenDown_mol * glucosePerTAG_mol; + glucoseFromGlycerol_mol += TAGBrokenDown_mol * glucosePerTAG_mol; } //Else we're running out of O2, it limits else @@ -403,6 +419,7 @@ void Hepatic::Gluconeogenesis() ketoneProductionRate_mol_Per_s += TAGActuallyBrokenDown_mol * ketonesPerTAG_mol / m_dt_s; totalO2Consumed_mol += TAGActuallyBrokenDown_mol * O2ConsumedToMakeKetones; totalGlucoseFromGluconeogenesis_mol += TAGActuallyBrokenDown_mol * glucosePerTAG_mol; + glucoseFromGlycerol_mol += TAGActuallyBrokenDown_mol * glucosePerTAG_mol; } } //If we don't have enough TAG in the liver, break down what's there @@ -420,6 +437,7 @@ void Hepatic::Gluconeogenesis() ketoneProductionRate_mol_Per_s += TAGActuallyBrokenDown_mol * ketonesPerTAG_mol / m_dt_s; totalO2Consumed_mol += TAGActuallyBrokenDown_mol * O2ConsumedToMakeKetones; totalGlucoseFromGluconeogenesis_mol += TAGActuallyBrokenDown_mol * glucosePerTAG_mol; + glucoseFromGlycerol_mol += TAGActuallyBrokenDown_mol * glucosePerTAG_mol; } //Otherwise, we don't have enough O2 for even this limited amount of TAG else @@ -432,6 +450,7 @@ void Hepatic::Gluconeogenesis() ketoneProductionRate_mol_Per_s += TAGReallyActuallyBrokenDown_mol * ketonesPerTAG_mol / m_dt_s; totalO2Consumed_mol += TAGReallyActuallyBrokenDown_mol * O2ConsumedToMakeKetones; totalGlucoseFromGluconeogenesis_mol += TAGReallyActuallyBrokenDown_mol * glucosePerTAG_mol; + glucoseFromGlycerol_mol += TAGReallyActuallyBrokenDown_mol * glucosePerTAG_mol; } } @@ -442,7 +461,7 @@ void Hepatic::Gluconeogenesis() //TODO figure out O2 consumption, if any if (hormoneFactor < 0) { - double AAConversionRate_g_Per_s = .001; //May need to be tuned to maintain brain glucose levels + double AAConversionRate_g_Per_s = .0003; //May need to be tuned to maintain brain glucose levels double glucosePerAA_mol = .5; double ureaPerAA_mol = .5; @@ -458,6 +477,7 @@ void Hepatic::Gluconeogenesis() m_liverExtracellularUrea->GetMass().IncrementValue(AAConverted_mol * ureaPerAA_mol * m_Urea->GetMolarMass(MassPerAmountUnit::g_Per_mol), MassUnit::g); //O2 decrement here, don't forget to track totalO2Consumed totalGlucoseFromGluconeogenesis_mol += AAConverted_mol * glucosePerAA_mol; + glucoseFromAA_mol += AAConverted_mol * glucosePerAA_mol; } //If O2 is limiting else @@ -476,6 +496,7 @@ void Hepatic::Gluconeogenesis() m_liverExtracellularUrea->GetMass().IncrementValue(AAActuallyConverted_mol * ureaPerAA_mol * m_Urea->GetMolarMass(MassPerAmountUnit::g_Per_mol), MassUnit::g); //O2 decrement here, don't forget to track totalO2Consumed totalGlucoseFromGluconeogenesis_mol += AAActuallyConverted_mol * glucosePerAA_mol; + glucoseFromAA_mol += AAActuallyConverted_mol * glucosePerAA_mol; } //O2 is limiting else @@ -485,10 +506,21 @@ void Hepatic::Gluconeogenesis() } } m_data.GetCompartments().GetLiquidCompartment(BGE::ExtravascularCompartment::LiverExtracellular)->Balance(BalanceLiquidBy::Mass); - m_energy->GetKetoneProductionRate().SetValue(ketoneProductionRate_mol_Per_s, AmountPerTimeUnit::mol_Per_s); + if(ketoneProductionRate_mol_Per_s < .0001138) //don't record values greater than 1000 g/day to eliminate initial spikes in ketogenesis and gluconeogenesis + GetKetoneProductionRate().SetValue(ketoneProductionRate_mol_Per_s, AmountPerTimeUnit::mol_Per_s); + if(totalGlucoseFromGluconeogenesis_mol * m_Glucose->GetMolarMass(MassPerAmountUnit::g_Per_mol) * (1 / m_dt_s) * 3600 * 24 < 1000) + GetHepaticGluconeogenesisRate().SetValue(totalGlucoseFromGluconeogenesis_mol * m_Glucose->GetMolarMass(MassPerAmountUnit::g_Per_mol) * (1 / m_dt_s) * 3600 * 24, MassPerTimeUnit::g_Per_day); Tissue::m_hepaticO2Consumed_mol += totalO2Consumed_mol; - //TODO Add Gluconeogenesis rate to CDM - //m_data.GetDataTrack().Probe("GluconeogenesisRate_g_Per_day", totalGlucoseFromGluconeogenesis_mol * m_Glucose->GetMolarMass(MassPerAmountUnit::g_Per_mol) * (1 / m_dt_s) * 3600 * 24); + + /* + if (totalGlucoseFromGluconeogenesis_mol * m_Glucose->GetMolarMass(MassPerAmountUnit::g_Per_mol) * (1 / m_dt_s) * 3600 * 24 < 1000) + m_data.GetDataTrack().Probe("GluconeogenesisRate_g_Per_day", totalGlucoseFromGluconeogenesis_mol * m_Glucose->GetMolarMass(MassPerAmountUnit::g_Per_mol) * (1 / m_dt_s) * 3600 * 24); + if (ketoneProductionRate_mol_Per_s < .0001138) + m_data.GetDataTrack().Probe("KetogenesisRate_g_Per_day", ketoneProductionRate_mol_Per_s * m_Ketones->GetMolarMass(MassPerAmountUnit::g_Per_mol) * 3600 * 24); + m_data.GetDataTrack().Probe("GluconeogenesisLactateFraction", glucoseFromLactate_mol/totalGlucoseFromGluconeogenesis_mol); + m_data.GetDataTrack().Probe("GluconeogenesisGlycerolFraction", glucoseFromGlycerol_mol / totalGlucoseFromGluconeogenesis_mol); + m_data.GetDataTrack().Probe("GluconeogenesisAAFraction", glucoseFromAA_mol / totalGlucoseFromGluconeogenesis_mol); + */ } //-------------------------------------------------------------------------------------------------- @@ -501,11 +533,21 @@ void Hepatic::Gluconeogenesis() /// did, and vice versa. A zero value means insulin and glucagon didn't change or stayed the same /// relative to each other. //-------------------------------------------------------------------------------------------------- -double Hepatic::CalculateRelativeHormoneChange(double insulinSetPoint_pmol_Per_L, double glucagonSetPoint_pg_Per_mL, SELiquidSubstanceQuantity* currentInsulin, SELiquidSubstanceQuantity* currentGlucagon) +double Hepatic::CalculateRelativeHormoneChange(double insulinSetPoint_pmol_Per_L, double glucagonSetPoint_pg_Per_mL, SELiquidSubstanceQuantity* currentInsulin, SELiquidSubstanceQuantity* currentGlucagon, BioGears& m_data) { double currentInsulin_pmol_Per_L = currentInsulin->GetMolarity(AmountPerVolumeUnit::mmol_Per_L)*1e9; double currentGlucagon_pg_Per_mL = currentGlucagon->GetConcentration(MassPerVolumeUnit::mg_Per_mL)*1e9; + //Patients with diabetes type 2 can have insulin resistance, which in essence means they require more insulin to achieve a normal effect + if (m_data.GetConditions().HasDiabetesType2()) + { + if (m_data.GetConditions().GetDiabetesType2()->HasInsulinResistanceSeverity()) + { + double insulinScale = 1 - (.986189 - .9820614 * exp(-6.808106 * m_data.GetConditions().GetDiabetesType2()->GetInsulinResistanceSeverity().GetValue())); + currentInsulin_pmol_Per_L *= insulinScale; + } + } + // Insulin can deviate a lot from set point, even as high as 20 times, and still be in acceptable ranges // Glucagon doesn't vary as much, expect a max of about 7 times // Because of our production sigmoids plateauing, we don't see such large deviations @@ -526,7 +568,7 @@ double Hepatic::CalculateRelativeHormoneChange(double insulinSetPoint_pmol_Per_L //-------------------------------------------------------------------------------------------------- void Hepatic::Lipogenesis() { - double hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_liverInsulin, m_liverGlucagon); + double hormoneFactor = CalculateRelativeHormoneChange(m_tsu->GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, m_tsu->GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_liverInsulin, m_liverGlucagon, m_data); double TAGGenerated_g = 0; double CO2Generated_mol = 0; diff --git a/src/engine/cpp/Systems/Hepatic.h b/src/engine/cpp/Systems/Hepatic.h index 75461e9..11d82bc 100644 --- a/src/engine/cpp/Systems/Hepatic.h +++ b/src/engine/cpp/Systems/Hepatic.h @@ -49,7 +49,7 @@ class BIOGEARS_API Hepatic : public SEHepaticSystem, public BioGearsSystem void PreProcess(); void Process(); void PostProcess(); - static double CalculateRelativeHormoneChange(double insulinSetPoint_pmol_Per_L, double glucagonSetPoint_pg_Per_mL, SELiquidSubstanceQuantity* currentInsulin, SELiquidSubstanceQuantity* currentGlucagon); + static double CalculateRelativeHormoneChange(double insulinSetPoint_pmol_Per_L, double glucagonSetPoint_pg_Per_mL, SELiquidSubstanceQuantity* currentInsulin, SELiquidSubstanceQuantity* currentGlucagon, BioGears& m_data); protected: //Pre-Process methods diff --git a/src/engine/cpp/Systems/Nervous.cpp b/src/engine/cpp/Systems/Nervous.cpp index 7212c0c..2cfae7b 100644 --- a/src/engine/cpp/Systems/Nervous.cpp +++ b/src/engine/cpp/Systems/Nervous.cpp @@ -17,6 +17,7 @@ specific language governing permissions and limitations under the License. #include "system/physiology/SECardiovascularSystem.h" #include "system/physiology/SEPupillaryResponse.h" #include "system/physiology/SEDrugSystem.h" +#include "substance/SESubstance.h" #include "properties/SEScalarFlowCompliance.h" #include "properties/SEScalarFlowElastance.h" #include "properties/SEScalarFlowResistance.h" @@ -28,6 +29,8 @@ specific language governing permissions and limitations under the License. #include "properties/SEScalarFraction.h" #include "properties/SEScalarNeg1To1.h" #include "properties/SEScalar0To1.h" +#include "properties/SEScalarAmountPerVolume.h" +#include "properties/SEScalarMassPerVolume.h" #pragma warning(disable:4786) #pragma warning(disable:4275) @@ -57,6 +60,7 @@ void Nervous::Initialize() { BioGearsSystem::Initialize(); m_FeedbackActive = false; + m_blockActive = false; GetBaroreceptorHeartRateScale().SetValue(1.0); GetBaroreceptorHeartElastanceScale().SetValue(1.0); GetBaroreceptorResistanceScale().SetValue(1.0); @@ -65,6 +69,7 @@ void Nervous::Initialize() GetLeftEyePupillaryResponse().GetReactivityModifier().SetValue(0); GetRightEyePupillaryResponse().GetSizeModifier().SetValue(0); GetRightEyePupillaryResponse().GetReactivityModifier().SetValue(0); + } bool Nervous::Load(const CDM::BioGearsNervousSystemData& in) @@ -110,6 +115,8 @@ void Nervous::SetUp() m_normalizedAlphaCompliance = m_data.GetConfiguration().GetNormalizedComplianceParasympatheticSlope(); m_normalizedAlphaResistance = m_data.GetConfiguration().GetNormalizedResistanceSympatheticSlope(); m_normalizedBetaHeartRate = m_data.GetConfiguration().GetNormalizedHeartRateParasympatheticSlope(); + m_Succinylcholine = m_data.GetSubstances().GetSubstance("Succinylcholine"); + m_Sarin = m_data.GetSubstances().GetSubstance("Sarin"); // Set when feedback is turned on m_ArterialOxygenSetPoint_mmHg = 0; @@ -154,7 +161,7 @@ void Nervous::PreProcess() //-------------------------------------------------------------------------------------------------- void Nervous::Process() { - CheckBrainStatus(); + CheckNervousStatus(); SetPupilEffects(); } @@ -190,8 +197,21 @@ void Nervous::BaroreceptorFeedback() double meanArterialPressure_mmHg = m_data.GetCardiovascular().GetMeanArterialPressure(PressureUnit::mmHg); //Adjusting the mean arterial pressure set-point to account for cardiovascular drug effects double meanArterialPressureSetPoint_mmHg = m_data.GetPatient().GetMeanArterialPressureBaseline(PressureUnit::mmHg) //m_MeanArterialPressureNoFeedbackBaseline_mmHg - + m_data.GetDrugs().GetMeanBloodPressureChange(PressureUnit::mmHg) - + m_data.GetEnergy().GetExerciseMeanArterialPressureDelta(PressureUnit::mmHg); + + m_data.GetEnergy().GetExerciseMeanArterialPressureDelta(PressureUnit::mmHg); + + //Adjust the MAP set-point for baroreceptors for anesthetics, opioids, and sedatives. Other drugs should leave set-point as is. + for (SESubstance* sub : m_data.GetCompartments().GetLiquidCompartmentSubstances()) + { + if (!sub->HasPD()) + continue; + if ((sub->GetClassification() == CDM::enumSubstanceClass::Anesthetic) || (sub->GetClassification() == CDM::enumSubstanceClass::Sedative) || (sub->GetClassification() == CDM::enumSubstanceClass::Opioid)) + { + meanArterialPressureSetPoint_mmHg += m_data.GetDrugs().GetMeanBloodPressureChange(PressureUnit::mmHg); + break; + //Only want to apply the blood pressure change ONCE (In case there are multiple sedative/opioids/etc) + ///\TODO: Look into a better way to implement drug classification search + } + } double sympatheticFraction = 1.0 / (1.0 + pow(meanArterialPressure_mmHg / meanArterialPressureSetPoint_mmHg, nu)); double parasympatheticFraction = 1.0 / (1.0 + pow(meanArterialPressure_mmHg / meanArterialPressureSetPoint_mmHg, -nu)); @@ -234,38 +254,96 @@ void Nervous::BaroreceptorFeedback() //-------------------------------------------------------------------------------------------------- /// \brief -/// Checks metrics in the brain to determine events to be thrown +/// Checks metrics in the nervous system to determine events to be thrown. Currently includes brain status +/// and presence of fasciculation. /// /// \details /// Intracranial pressure is checked to determine if the patient has Intracranial Hyper/hypotension +/// Fasciculation can occur as a result of calcium/magnesium deficiency +/// (or other electrolyte imbalances),succinylcholine, nerve agents, ALS +/// Currently, only fasciculations due to the nerve agent Sarin are active. Other causes are a subject of model improvement +//------------------------------------------------------------------------------------------ //-------------------------------------------------------------------------------------------------- -void Nervous::CheckBrainStatus() +void Nervous::CheckNervousStatus() { - double icp_mmHg = m_data.GetCardiovascular().GetIntracranialPressure().GetValue(PressureUnit::mmHg); - - //Intracranial Hypertension - if (icp_mmHg > 25.0) // \cite steiner2006monitoring - { - /// \event Patient: Intracranial Hypertension. The intracranial pressure has risen above 25 mmHg. - m_data.GetPatient().SetEvent(CDM::enumPatientEvent::IntracranialHypertension, true, m_data.GetSimulationTime()); - } - else if (m_data.GetPatient().IsEventActive(CDM::enumPatientEvent::IntracranialHypertension) && icp_mmHg < 24.0) - { - /// \event Patient: End Intracranial Hypertension. The intracranial pressure has fallen below 24 mmHg. - m_data.GetPatient().SetEvent(CDM::enumPatientEvent::IntracranialHypertension, false, m_data.GetSimulationTime()); - } - - //Intracranial Hypotension - if (icp_mmHg < 7.0) // \cite steiner2006monitoring - { - /// \event Patient: Intracranial Hypotension. The intracranial pressure has fallen below 7 mmHg. - m_data.GetPatient().SetEvent(CDM::enumPatientEvent::IntracranialHypotension, true, m_data.GetSimulationTime()); - } - else if (m_data.GetPatient().IsEventActive(CDM::enumPatientEvent::IntracranialHypotension) && icp_mmHg > 7.5) - { - /// \event Patient: End Intracranial Hypotension. The intracranial pressure has risen above 7.5 mmHg. - m_data.GetPatient().SetEvent(CDM::enumPatientEvent::IntracranialHypertension, false, m_data.GetSimulationTime()); - } + //-----Check Brain Status----------------- + double icp_mmHg = m_data.GetCardiovascular().GetIntracranialPressure().GetValue(PressureUnit::mmHg); + + //Intracranial Hypertension + if (icp_mmHg > 25.0) // \cite steiner2006monitoring + { + /// \event Patient: Intracranial Hypertension. The intracranial pressure has risen above 25 mmHg. + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::IntracranialHypertension, true, m_data.GetSimulationTime()); + } + else if (m_data.GetPatient().IsEventActive(CDM::enumPatientEvent::IntracranialHypertension) && icp_mmHg < 24.0) + { + /// \event Patient: End Intracranial Hypertension. The intracranial pressure has fallen below 24 mmHg. + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::IntracranialHypertension, false, m_data.GetSimulationTime()); + } + + //Intracranial Hypotension + if (icp_mmHg < 7.0) // \cite steiner2006monitoring + { + /// \event Patient: Intracranial Hypotension. The intracranial pressure has fallen below 7 mmHg. + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::IntracranialHypotension, true, m_data.GetSimulationTime()); + } + else if (m_data.GetPatient().IsEventActive(CDM::enumPatientEvent::IntracranialHypotension) && icp_mmHg > 7.5) + { + /// \event Patient: End Intracranial Hypotension. The intracranial pressure has risen above 7.5 mmHg. + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::IntracranialHypertension, false, m_data.GetSimulationTime()); + } + + //------Fasciculations:------------------------------------------- + + //----Fasciculations due to calcium deficiency (inactive)---------------------------------- + /*if (m_Muscleintracellular.GetSubstanceQuantity(*m_Calcium)->GetConcentration(MassPerVolumeUnit::g_Per_L) < 1.0) + { + /// \event Patient: Patient is fasciculating due to calcium deficiency + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Fasciculation, true, m_data.GetSimulationTime()); + } + else if (m_Muscleintracellular.GetSubstanceQuantity(*m_Calcium)->GetConcentration(MassPerVolumeUnit::g_Per_L) > 3.0) + { + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Fasciculation, false, m_data.GetSimulationTime()); + }*/ + + //-----Fasciculations due to Sarin-------------------------------------------------- + //Occurs due to inhibition of acetylcholinesterase, the enzyme which breaks down the neurotransmitter acetylcholine + double RbcAche_mol_Per_L = m_data.GetBloodChemistry().GetRedBloodCellAcetylcholinesterase(AmountPerVolumeUnit::mol_Per_L); + double RbcFractionInhibited = 1.0 - RbcAche_mol_Per_L / (8e-9); //8 nM is the baseline activity of Rbc-Ache + if (m_data.GetSubstances().IsActive(*m_Sarin)) + { + ///\cite nambda1971cholinesterase + //The above study found that individuals exposed to the organophosphate parathion did not exhibit fasciculation until at least + //80% of Rbc-Ache was inhibited. This was relaxed to 70% because BioGears is calibrated to throw an irreversible state at + //100% inhibition when, in actuality, a patient with 100% rbc-ache inhibition will likely survive (rbc-ache thought to act as a buffer + //for neuromuscular ache) + if (RbcFractionInhibited > 0.7) + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Fasciculation, true, m_data.GetSimulationTime()); + else if ((m_data.GetSubstances().IsActive(*m_Sarin)) && (RbcFractionInhibited < 0.68)) + { + //Oscillations around 70% rbc-ache inhibition are highly unlikely but give some leeway for reversal just in case + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Fasciculation, false, m_data.GetSimulationTime()); + } + } + //----Fasciculations due to Succinylcholine administration.--------------------------------------------------- + //No evidence exists for a correlation between the plasma concentration of succinylcholine + //and the degree or presence of fasciculation. Rather, it has been observed that transient fasciculation tends to occur in most patients after initial dosing + //(particularly at a dose of 1.5 mg/kg, see refs below), subsiding once depolarization at neuromuscular synapses is accomplished. Therefore, we model this + //effect by initiating fasciculation when succinylcholine enters the body and removing it when the neuromuscular block level (calculated in Drugs.cpp) reaches + //90% of maximum. To prevent fasciculation from being re-flagged as succinylcholine leaves the body and the block dissipates, we use a sentinel (m_blockActive, + //initialized to FALSE) so that the event cannot be triggered more than once. + /// \cite @appiah2004pharmacology, @cite mcloughlin1994influence + double neuromuscularBlockLevel = m_data.GetDrugs().GetNeuromuscularBlockLevel().GetValue(); + if (m_data.GetSubstances().IsActive(*m_Succinylcholine)&&(neuromuscularBlockLevel>0.0)) + { + if ((neuromuscularBlockLevel <0.9)&&(!m_blockActive)) + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Fasciculation, true, m_data.GetSimulationTime()); + else + { + m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Fasciculation, false, m_data.GetSimulationTime()); + m_blockActive = true; + } + } } //-------------------------------------------------------------------------------------------------- @@ -392,4 +470,4 @@ void Nervous::SetPupilEffects() GetLeftEyePupillaryResponse().GetReactivityModifier().SetValue(leftPupilReactivityResponseLevel); GetRightEyePupillaryResponse().GetSizeModifier().SetValue(rightPupilSizeResponseLevel); GetRightEyePupillaryResponse().GetReactivityModifier().SetValue(rightPupilReactivityResponseLevel); -} \ No newline at end of file +} diff --git a/src/engine/cpp/Systems/Nervous.h b/src/engine/cpp/Systems/Nervous.h index 78332ca..0e8a4c8 100644 --- a/src/engine/cpp/Systems/Nervous.h +++ b/src/engine/cpp/Systems/Nervous.h @@ -52,7 +52,7 @@ class BIOGEARS_API Nervous : public SENervousSystem, public BioGearsSystem protected: void BaroreceptorFeedback(); - void CheckBrainStatus(); + void CheckNervousStatus(); void ChemoreceptorFeedback(); void SetPupilEffects(); @@ -63,6 +63,9 @@ class BIOGEARS_API Nervous : public SENervousSystem, public BioGearsSystem // Stateless member variable (Set in SetUp()) bool m_FeedbackActive; double m_dt_s; + SESubstance* m_Succinylcholine; + SESubstance* m_Sarin; + bool m_blockActive; // Configuration fractions representing the amount by which each quantity can change due to baroreceptors; double m_normalizedGammaHeartRate; double m_normalizedGammaElastance; @@ -73,4 +76,5 @@ class BIOGEARS_API Nervous : public SENervousSystem, public BioGearsSystem double m_normalizedAlphaCompliance; double m_normalizedAlphaResistance; double m_normalizedBetaHeartRate; + }; diff --git a/src/engine/cpp/Systems/Renal.cpp b/src/engine/cpp/Systems/Renal.cpp index eed9569..68049d3 100644 --- a/src/engine/cpp/Systems/Renal.cpp +++ b/src/engine/cpp/Systems/Renal.cpp @@ -37,8 +37,6 @@ specific language governing permissions and limitations under the License. #include "properties/SEScalarVolumePerTimePressure.h" #include "properties/SEScalarMassPerAmount.h" #include "patient/SEPatient.h" -#include "patient/SEMeal.h" -#include "patient/conditions/SEConsumeMeal.h" #include "scenario/SECondition.h" #include "patient/conditions/SEChronicRenalStenosis.h" @@ -443,11 +441,13 @@ void Renal::AtSteadyState() { if (m_data.GetState() == EngineState::AtInitialStableState) { + /* if (m_data.GetConditions().HasConsumeMeal()) { double elapsedTime_s = m_data.GetConditions().GetConsumeMeal()->GetMeal().GetElapsedTime(TimeUnit::s); ConsumeMeal(elapsedTime_s); } + */ } if (m_data.GetState() == EngineState::AtSecondaryStableState) @@ -926,37 +926,37 @@ void Renal::CalculateSecretion() //LEFT ureterPotassium = m_leftUreterPotassium; peritubularPotassium = m_leftPeritubularPotassium; - peritubularVolume_dL = m_leftPeritubular->GetVolume().GetValue(VolumeUnit::dL); + peritubularVolume_dL = m_leftPeritubular->GetVolume().GetValue(VolumeUnit::dL); } else { //RIGHT ureterPotassium = m_rightUreterPotassium; peritubularPotassium = m_rightPeritubularPotassium; - peritubularVolume_dL = m_rightPeritubular->GetVolume().GetValue(VolumeUnit::dL); + peritubularVolume_dL = m_rightPeritubular->GetVolume().GetValue(VolumeUnit::dL); } - } - //grab current concentration and volume, - potassiumConcentration_g_Per_dL = peritubularPotassium->GetConcentration().GetValue(MassPerVolumeUnit::g_Per_dL); + //grab current concentration and volume, + potassiumConcentration_g_Per_dL = peritubularPotassium->GetConcentration().GetValue(MassPerVolumeUnit::g_Per_dL); - //only do if current levels of potassium are too high: - if (potassiumConcentration_g_Per_dL > m_baselinePotassiumConcentration_g_Per_dL) - { - //calculate mass to move in mg: - massPotassiumToMove_mg = (potassiumConcentration_g_Per_dL - m_baselinePotassiumConcentration_g_Per_dL)*peritubularVolume_dL; + //only do if current levels of potassium are too high: + if (potassiumConcentration_g_Per_dL > m_baselinePotassiumConcentration_g_Per_dL) + { + //calculate mass to move in mg: + massPotassiumToMove_mg = (potassiumConcentration_g_Per_dL - m_baselinePotassiumConcentration_g_Per_dL)*peritubularVolume_dL; - //Increment & decrement - peritubularPotassium->GetMass().IncrementValue(-massPotassiumToMove_mg, MassUnit::mg); - ureterPotassium->GetMass().IncrementValue(massPotassiumToMove_mg, MassUnit::mg); + //Increment & decrement + peritubularPotassium->GetMass().IncrementValue(-massPotassiumToMove_mg, MassUnit::mg); + ureterPotassium->GetMass().IncrementValue(massPotassiumToMove_mg, MassUnit::mg); - // if its super small negative mass set to zero: - if (peritubularPotassium->GetMass().IsNegative()) - peritubularPotassium->GetMass().SetValue(0.0, MassUnit::mg); + // if its super small negative mass set to zero: + if (peritubularPotassium->GetMass().IsNegative()) + peritubularPotassium->GetMass().SetValue(0.0, MassUnit::mg); - //Calculate new concentrations - ureterPotassium->Balance(BalanceLiquidBy::Mass); - peritubularPotassium->Balance(BalanceLiquidBy::Mass); + //Calculate new concentrations + ureterPotassium->Balance(BalanceLiquidBy::Mass); + peritubularPotassium->Balance(BalanceLiquidBy::Mass); + } } } diff --git a/src/engine/cpp/Systems/Respiratory.cpp b/src/engine/cpp/Systems/Respiratory.cpp index fc329b3..da3bd9b 100644 --- a/src/engine/cpp/Systems/Respiratory.cpp +++ b/src/engine/cpp/Systems/Respiratory.cpp @@ -460,7 +460,7 @@ void Respiratory::AtSteadyState() COPD(); LobarPneumonia(); //These conditions stack effects - //If combined, it will be a fraction of the already effected alveolar surface area + //If combined, it will be a fraction of the already affected alveolar surface area ImpairedAlveolarExchange(); } } @@ -575,7 +575,7 @@ void Respiratory::UpdatePleuralCompliance() /// \details /// For each aerosol get the SIDE coefficient to determine deposited mass in each respiratory compartment. /// Adjust the resistances between compartments as a function of deposited mass to reach validated data. -/// Liquid and solid aerosols are handeled here. +/// Liquid and solid aerosols are handled here. //-------------------------------------------------------------------------------------------------- void Respiratory::ProcessAerosolSubstances() { @@ -611,7 +611,7 @@ void Respiratory::ProcessAerosolSubstances() double rightDeadSpaceResistanceModifier=1; double rightAlveoliResistanceModifier=1; - // Currently, There is no way to clear out depositied particulate out of the respiratory systems. + // Currently, there is no way to clear deposited particulate out of the respiratory system // Maybe we could have it to cough or some other excretion related method... SELiquidSubstanceQuantity* subQ; @@ -705,7 +705,7 @@ void Respiratory::ProcessAerosolSubstances() rightAlveoliTotalDepositied_ug = subQ->GetMassDeposited().IncrementValue(rightAlveoliDepositied_ug, MassUnit::ug); rightAlveoliResistanceModifier += rightAlveoliTotalDepositied_ug*inflammationCoefficient; - // Apply the BronchileModifier dilation effect + // Apply the BronchioleModifier dilation effect // This is all just tuned to Albuterol - it'll work for other substances, and can be tuned using the other parameters (especially BronchioleModifier) if (subQ->GetSubstance().GetState() == CDM::enumSubstanceState::Liquid) { @@ -1183,6 +1183,7 @@ void Respiratory::BronchoDilation() //The bronchi are ~30% of the total pulmonary resistance, so we'll make a dilation effect of 1.0 be at the respiratory open resistance. //Dilation effect values have max effect at 1 and below -1, so anything outside of that will maintain that effect. double bronchoDilationEffect = m_data.GetDrugs().GetBronchodilationLevel().GetValue(); + double resTrack = 0.0; if (bronchoDilationEffect != 0.0) { //Note: It'll pretty much always get in here because there's epinephrine present @@ -1198,8 +1199,9 @@ void Respiratory::BronchoDilation() else //negative, therefore constriction { bronchoDilationEffect = MIN(-bronchoDilationEffect, 1.0); - dLeftBronchiResistance = GeneralMath::ResistanceFunction(10.0, dLeftBronchiResistance, m_dRespOpenResistance_cmH2O_s_Per_L, bronchoDilationEffect); - dRightBronchiResistance = GeneralMath::ResistanceFunction(10.0, dRightBronchiResistance, m_dRespOpenResistance_cmH2O_s_Per_L, bronchoDilationEffect); + dLeftBronchiResistance = GeneralMath::ResistanceFunction(10.0, m_dRespOpenResistance_cmH2O_s_Per_L, dLeftBronchiResistance, bronchoDilationEffect); + dRightBronchiResistance = GeneralMath::ResistanceFunction(10.0, m_dRespOpenResistance_cmH2O_s_Per_L, dRightBronchiResistance, bronchoDilationEffect); + resTrack = dRightBronchiResistance; } m_CarinaToLeftAnatomicDeadSpace->GetNextResistance().SetValue(dLeftBronchiResistance, FlowResistanceUnit::cmH2O_s_Per_L); m_CarinaToRightAnatomicDeadSpace->GetNextResistance().SetValue(dRightBronchiResistance, FlowResistanceUnit::cmH2O_s_Per_L); diff --git a/src/engine/cpp/Systems/Tissue.cpp b/src/engine/cpp/Systems/Tissue.cpp index 8a6ec40..7bee5a5 100644 --- a/src/engine/cpp/Systems/Tissue.cpp +++ b/src/engine/cpp/Systems/Tissue.cpp @@ -10,6 +10,7 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. **************************************************************************************/ +#include #include "stdafx.h" #include "Tissue.h" #include "Cardiovascular.h" @@ -17,9 +18,9 @@ specific language governing permissions and limitations under the License. #include "Respiratory.h" #include "Drugs.h" +#include "bind/RunningAverageData.hxx" + #include "patient/SEPatient.h" -#include "patient/SEMeal.h" -#include "patient/conditions/SEConsumeMeal.h" #include "substance/SESubstance.h" #include "substance/SESubstancePharmacokinetics.h" #include "substance/SESubstanceTissuePharmacokinetics.h" @@ -30,6 +31,7 @@ specific language governing permissions and limitations under the License. #include "compartment/substances/SEGasSubstanceQuantity.h" #include "compartment/substances/SELiquidSubstanceQuantity.h" #include "properties/SEScalar0To1.h" +#include "properties/SEScalarTime.h" #include "properties/SEScalarArea.h" #include "properties/SEScalarAreaPerTimePressure.h" #include "properties/SEScalarFraction.h" @@ -51,6 +53,13 @@ specific language governing permissions and limitations under the License. #include "properties/SEScalarAmountPerTime.h" #include "properties/SEScalarMassPerAreaTime.h" #include "properties/SEScalarVolume.h" +#include "properties/SEScalarElectricResistance.h" + +// Conditions +#include "patient/conditions/SEDehydration.h" +#include "patient/conditions/SEStarvation.h" +#include "patient/conditions/SEDiabetesType1.h" +#include "patient/conditions/SEDiabetesType2.h" #pragma warning(disable:4786) #pragma warning(disable:4275) @@ -86,6 +95,8 @@ void Tissue::Clear() m_Creatinine = nullptr; m_Sodium = nullptr; m_Calcium = nullptr; + m_Chloride = nullptr; + m_Potassium = nullptr; m_Insulin = nullptr; m_LeftLungTissue = nullptr; @@ -106,6 +117,10 @@ void Tissue::Clear() m_RightPulmonaryCapillaries = nullptr; m_PatientActions = nullptr; + + m_O2ConsumedRunningAverage_mL_Per_s.Reset(); + m_CO2ProducedRunningAverage_mL_Per_s.Reset(); + m_RespiratoryQuotientRunningAverage.Reset(); } //-------------------------------------------------------------------------------------------------- @@ -116,26 +131,21 @@ void Tissue::Initialize() { BioGearsSystem::Initialize(); - /*Tissue System*/ // Get total tissue resting values for substances SETissueCompartment* tissue; SELiquidCompartment* vascular; - m_RestingTissueGlucose_g = 0.0; + SEScalarMassPerVolume density; + GeneralMath::CalculateWaterDensity(m_data.GetEnergy().GetCoreTemperature(), density); for (auto tissueVascular : m_TissueToVascular) { tissue = tissueVascular.first; vascular = tissueVascular.second; SELiquidCompartment& extracellular = m_data.GetCompartments().GetExtracellularFluid(*tissue); SELiquidCompartment& intracellular = m_data.GetCompartments().GetIntracellularFluid(*tissue); - - m_RestingTissueGlucose_g += extracellular.GetSubstanceQuantity(*m_Glucose)->GetMass(MassUnit::g); m_RestingFluidMass_kg += vascular->GetVolume(VolumeUnit::mL)*m_data.GetBloodChemistry().GetBloodDensity(MassPerVolumeUnit::kg_Per_mL); - m_RestingFluidMass_kg += intracellular.GetVolume(VolumeUnit::mL)*m_data.GetConfiguration().GetWaterDensity(MassPerVolumeUnit::kg_Per_mL); - m_RestingFluidMass_kg += extracellular.GetVolume(VolumeUnit::mL)*m_data.GetConfiguration().GetWaterDensity(MassPerVolumeUnit::kg_Per_mL); + m_RestingFluidMass_kg += intracellular.GetVolume(VolumeUnit::mL)*density.GetValue(MassPerVolumeUnit::kg_Per_mL); + m_RestingFluidMass_kg += extracellular.GetVolume(VolumeUnit::mL)*density.GetValue(MassPerVolumeUnit::kg_Per_mL); } - m_RestingBloodGlucose_g_Per_L = m_data.GetCompartments().GetLiquidCompartment(BGE::VascularCompartment::VenaCava)->GetSubstanceQuantity(*m_Glucose)->GetConcentration(MassPerVolumeUnit::g_Per_L); - m_RestingBloodLipid_g_Per_L = m_data.GetCompartments().GetLiquidCompartment(BGE::VascularCompartment::VenaCava)->GetSubstanceQuantity(*m_Triacylglycerol)->GetConcentration(MassPerVolumeUnit::g_Per_L); - m_RestingBloodInsulin_g_Per_L = m_data.GetCompartments().GetLiquidCompartment(BGE::VascularCompartment::VenaCava)->GetSubstanceQuantity(*m_Insulin)->GetConcentration(MassPerVolumeUnit::g_Per_L); m_RestingPatientMass_kg = m_data.GetPatient().GetWeight(MassUnit::kg); GetIntracellularFluidPH().SetValue(7.0); @@ -153,10 +163,15 @@ void Tissue::Initialize() GetFatGlucagonSetPoint().Set(m_data.GetCompartments().GetLiquidCompartment(BGE::VascularCompartment::Fat)->GetSubstanceQuantity(*m_Glucagon)->GetConcentration()); //Set nutrient stores (also reset in AtSteadyState) - GetLiverGlycogen().SetValue(.065 * m_data.GetCompartments().GetTissueCompartment(BGE::TissueCompartment::Liver)->GetTotalMass(MassUnit::g), MassUnit::g);//TODO figure out what to initialize glycogen to; do we want it full or empty to start, or in between? For now, it's 6.5% of liver mass - GetMuscleGlycogen().SetValue(.02 * m_data.GetPatient().GetWeight(MassUnit::g) * .35, MassUnit::g); //2% of muscle mass + GetLiverGlycogen().SetValue(.065 * m_data.GetCompartments().GetTissueCompartment(BGE::TissueCompartment::Liver)->GetTotalMass(MassUnit::g), MassUnit::g); + GetMuscleGlycogen().SetValue(.02 * m_data.GetPatient().GetMuscleMass(MassUnit::g), MassUnit::g); //2% of muscle mass GetStoredProtein().SetValue(110, MassUnit::g); //"Reusable" protein stores are usually about 1% of total body protein, ~110 g (https://www.nap.edu/read/10490/chapter/12#595) GetStoredFat().SetValue(m_data.GetPatient().GetWeight(MassUnit::g) * m_data.GetPatient().GetBodyFatFraction().GetValue(), MassUnit::g); + + + + GetDehydrationFraction().SetValue(0); + } bool Tissue::Load(const CDM::BioGearsTissueSystemData& in) @@ -164,12 +179,9 @@ bool Tissue::Load(const CDM::BioGearsTissueSystemData& in) if (!SETissueSystem::Load(in)) return false; - m_RestingTissueGlucose_g = in.RestingTissueGlucose_g(); - m_RestingBloodGlucose_g_Per_L = in.RestingBloodGlucose_g_Per_L(); - m_RestingBloodLipid_g_Per_L = in.RestingBloodLipid_g_Per_L(); - m_RestingBloodInsulin_g_Per_L = in.RestingBloodInsulin_g_Per_L(); - m_RestingPatientMass_kg = in.RestingPatientMass_kg(); - m_RestingFluidMass_kg = in.RestingFluidMass_kg(); + m_O2ConsumedRunningAverage_mL_Per_s.Load(in.O2ConsumedRunningAverage_mL_Per_s()); + m_CO2ProducedRunningAverage_mL_Per_s.Load(in.CO2ProducedRunningAverage_mL_Per_s()); + m_RespiratoryQuotientRunningAverage.Load(in.RespiratoryQuotientRunningAverage()); BioGearsSystem::LoadState(); return true; @@ -184,23 +196,9 @@ void Tissue::Unload(CDM::BioGearsTissueSystemData& data) const { SETissueSystem::Unload(data); - data.RestingTissueGlucose_g(m_RestingTissueGlucose_g); - data.RestingBloodGlucose_g_Per_L(m_RestingBloodGlucose_g_Per_L); - data.RestingBloodLipid_g_Per_L(m_RestingBloodLipid_g_Per_L); - data.RestingBloodInsulin_g_Per_L(m_RestingBloodInsulin_g_Per_L); - data.RestingPatientMass_kg(m_RestingPatientMass_kg); - data.RestingFluidMass_kg(m_RestingFluidMass_kg); - - //data.LiverInsulinSetPoint_pmol_Per_L(m_LiverInsulinSetPoint_pmol_Per_L); - //data.LiverGlucagonSetPoint_pg_Per_mL(m_LiverGlucagonSetPoint_pg_Per_mL); - //data.MuscleInsulinSetPoint_pmol_Per_L(m_MuscleInsulinSetPoint_pmol_Per_L); - //data.MuscleGlucagonSetPoint_pg_Per_mL(m_MuscleGlucagonSetPoint_pg_Per_mL); - //data.FatInsulinSetPoint_pmol_Per_L(m_FatInsulinSetPoint_pmol_Per_L); - //data.FatGlucagonSetPoint_pg_Per_mL(m_FatGlucagonSetPoint_pg_Per_mL); - //data.LiverGlycogen_g(m_LiverGlycogen_g); - //data.MuscleGlycogen_g(m_MuscleGlycogen_g); - //data.StoredProtein_g(m_StoredProtein_g); - //data.StoredFat_g(m_StoredFat_g); + data.O2ConsumedRunningAverage_mL_Per_s(std::unique_ptr(m_O2ConsumedRunningAverage_mL_Per_s.Unload())); + data.CO2ProducedRunningAverage_mL_Per_s(std::unique_ptr(m_CO2ProducedRunningAverage_mL_Per_s.Unload())); + data.RespiratoryQuotientRunningAverage(std::unique_ptr(m_RespiratoryQuotientRunningAverage.Unload())); } //-------------------------------------------------------------------------------------------------- @@ -217,6 +215,8 @@ void Tissue::SetUp() //"Reusable" protein stores are usually about 1% of total body protein, ~110 g (https://www.nap.edu/read/10490/chapter/12#595) m_maxProteinStorage_g = 110; + m_lastFatigueTime = INFINITY; + Tissue::m_hepaticO2Consumed_mol = 0; //Hepatic system will send O2/CO2 changes from lipogenesis/gluconeogenesis to Tissue to factor into O2 consumption/CO2 production outputs Tissue::m_hepaticCO2Produced_mol = 0; @@ -236,7 +236,9 @@ void Tissue::SetUp() m_Ketones = &m_data.GetSubstances().GetKetones(); m_Creatinine = &m_data.GetSubstances().GetCreatinine(); m_Sodium = &m_data.GetSubstances().GetSodium(); + m_Potassium = &m_data.GetSubstances().GetPotassium(); m_Calcium = &m_data.GetSubstances().GetCalcium(); + m_Chloride = &m_data.GetSubstances().GetChloride(); m_Insulin = &m_data.GetSubstances().GetInsulin(); m_Urea = &m_data.GetSubstances().GetUrea(); @@ -324,61 +326,106 @@ void Tissue::SetUp() //#define logMeal void Tissue::AtSteadyState() { - if (m_data.GetState() == EngineState::AtInitialStableState) - {// Apply our conditions - if (m_data.GetConditions().HasConsumeMeal()) - { - SEScalarMass mass; - SEMeal& meal = m_data.GetConditions().GetConsumeMeal()->GetMeal(); - double elapsedTime_s = meal.GetElapsedTime().GetValue(TimeUnit::s); - double patientWeight_kg = m_data.GetPatient().GetWeight(MassUnit::kg); - double renalVolumeCleared = m_Albumin->GetClearance().GetRenalClearance(VolumePerTimeMassUnit::mL_Per_s_kg)*patientWeight_kg*elapsedTime_s; - double systemicVolumeCleared = m_Albumin->GetClearance().GetSystemicClearance(VolumePerTimeMassUnit::mL_Per_s_kg)*patientWeight_kg*elapsedTime_s - renalVolumeCleared; - SEScalarVolume integratedVolume; - integratedVolume.SetValue(systemicVolumeCleared, VolumeUnit::mL); - //mass.SetValue(m_AlbuminProdutionRate_g_Per_s*elapsedTime_s, MassUnit::g); - // Distribute the mass over the body via a total volume weighting -#ifdef logMeal - std::stringstream m_ss; - double cvPreAbsorbed_g = m_data.GetCircuits()->TotalSubstanceInBlood(*m_Albumin, MassUnit::g); - double evPreAbsorbed_g = m_data.GetCircuits()->TotalSubstanceInTissue(*m_Albumin, MassUnit::g); - m_ss << "Preabsorbed Total Albumin in body : " << cvPreAbsorbed_g + evPreAbsorbed_g << "(g) cv(" << cvPreAbsorbed_g << ") ev(" << evPreAbsorbed_g << ")"; - Info(m_ss); - m_ss << "Distributing " << mass << "of Albumin into body : "; - Info(m_ss); -#endif - /// \todo Replace with new Consume Methodology - // m_data.GetCircuits().DistributeTissueMass(*m_Albumin, mass); - // m_data.GetCircuits().BalanceBloodMassByClearedVolume(*m_Albumin, integratedVolume); -#ifdef logMeal - double cvPostAbsorbed_g = m_data.GetCircuits()->TotalSubstanceInBlood(*m_Albumin, MassUnit::g); - double evPostAbsorbed_g = m_data.GetCircuits()->TotalSubstanceInTissue(*m_Albumin, MassUnit::g); - m_ss << "PostAbsorbed Total Albumin in body : " << cvPostAbsorbed_g + evPostAbsorbed_g << "(g), body absorbed : " << (cvPostAbsorbed_g + evPostAbsorbed_g) - (cvPreAbsorbed_g + evPreAbsorbed_g) << "(g) cv(" << cvPostAbsorbed_g << ") ev(" << evPostAbsorbed_g << ")"; - Info(m_ss); -#endif - } - } - if (m_data.GetState() == EngineState::AtSecondaryStableState) + if (m_data.GetState() == EngineState::AtInitialStableState) + { + // Apply our conditions + if (m_data.GetConditions().HasStarvation()) + SetStarvationState(); + if (m_data.GetConditions().HasDehydration()) + Dehydrate(); + } + + if (m_data.GetState() == EngineState::AtSecondaryStableState && !m_data.GetConditions().HasStarvation()) { - //refill nutrient stores that were depleted during stabilization + //refill nutrient stores that were depleted during stabilization (should match SetUp) GetLiverGlycogen().SetValue(.065 * m_data.GetCompartments().GetTissueCompartment(BGE::TissueCompartment::Liver)->GetTotalMass(MassUnit::g), MassUnit::g); - GetMuscleGlycogen().SetValue(.02 * m_data.GetPatient().GetWeight(MassUnit::g) * .35, MassUnit::g); + GetMuscleGlycogen().SetValue(.02 * m_data.GetPatient().GetMuscleMass(MassUnit::g), MassUnit::g); //2% of muscle mass GetStoredProtein().SetValue(110, MassUnit::g); GetStoredFat().SetValue(m_data.GetPatient().GetWeight(MassUnit::g) * m_data.GetPatient().GetBodyFatFraction().GetValue(), MassUnit::g); Info("Nutrient stores refilled after stabilization"); //TEMPORARILY set to starved state /* - GetLiverGlycogen().SetValue(50, MassUnit::g); - GetMuscleGlycogen().SetValue(200, MassUnit::g); - GetStoredProtein().SetValue(50, MassUnit::g); + GetLiverGlycogen().SetValue(0, MassUnit::g); + GetMuscleGlycogen().SetValue(0, MassUnit::g); + GetStoredProtein().SetValue(110, MassUnit::g); GetStoredFat().SetValue(m_data.GetPatient().GetWeight(MassUnit::g) * m_data.GetPatient().GetBodyFatFraction().GetValue(), MassUnit::g); Info("Nutrient stores set to altered values for TESTING"); */ } } +//-------------------------------------------------------------------------------------------------- +/// \brief +/// Sets the status of nutrient stores and blood concentrations to appropriate starved values +/// +/// \details +/// Liver glycogen, muscle glycogen, muscle mass, stored fat, and stored protein are set based on a +/// linear relationship with the dtarvation duration. Blood concentrations of glucose and ketones are +/// set based on second-order fits of literature values. No other substances are directly set by the +/// starvation condition. +//-------------------------------------------------------------------------------------------------- +void Tissue::SetStarvationState() +{ + double starvedTime_hr = m_data.GetConditions().GetStarvation()->GetTimeSinceMeal().GetValue(TimeUnit::hr); + double totalMassLost_g = 0; + + //Set new storage values + + //Liver glycogen + //Based on engine runtime, lasts ~51 hours, decrease is nearly linear + double liverGlycogen_g = GeneralMath::LinearInterpolator(0, 51, .065 * m_data.GetCompartments().GetTissueCompartment(BGE::TissueCompartment::Liver)->GetTotalMass(MassUnit::g), 0, starvedTime_hr); + LLIM(liverGlycogen_g, 0); //can't have negative + GetLiverGlycogen().SetValue(liverGlycogen_g, MassUnit::g); + totalMassLost_g += (.065 * m_data.GetCompartments().GetTissueCompartment(BGE::TissueCompartment::Liver)->GetTotalMass(MassUnit::g)) - liverGlycogen_g; + + //Muscle glycogen + //Based on engine runtime, drops to ~425g after 76.4 hours, nearly linear + double muscleGlycogen_g = GeneralMath::LinearInterpolator(0, 76.4, .02 * m_Patient->GetMuscleMass(MassUnit::g), 425, starvedTime_hr); + LLIM(muscleGlycogen_g, 0); //can't have negative + GetMuscleGlycogen().SetValue(muscleGlycogen_g, MassUnit::g); + totalMassLost_g += (.02 * m_Patient->GetMuscleMass(MassUnit::g)) - muscleGlycogen_g; + + //Stored Protein + //Based on engine runtime, lasts ~45.5 hours, decrease is nearly linear + double storedProtein_g = GeneralMath::LinearInterpolator(0, 45.5, 110, 0, starvedTime_hr); + LLIM(storedProtein_g, 0); //can't have negative + GetStoredProtein().SetValue(liverGlycogen_g, MassUnit::g); + totalMassLost_g += (110) - storedProtein_g; + + //Muscle Mass + //Drops at same rate as stored protein, but only after that is used up + //If they're starved so long they've used all of their protein, they're dead + double muscleMassDecrease_g = GeneralMath::LinearInterpolator(0, 45.5, 0, 110, starvedTime_hr); + BLIM(muscleMassDecrease_g, 0, m_Patient->GetMuscleMass().GetValue(MassUnit::g)); + m_Patient->GetMuscleMass().IncrementValue(-muscleMassDecrease_g, MassUnit::g); + totalMassLost_g += muscleMassDecrease_g; + + //Stored fat + //Based on engine runtime, drops ~521g in 96 hours, decrease is nearly linear + double fat = GetStoredFat().GetValue(MassUnit::g); + double fatMassDecrease_g = GeneralMath::LinearInterpolator(0, 96, 0, 521, starvedTime_hr); + BLIM(fatMassDecrease_g, 0, GetStoredFat().GetValue(MassUnit::g)); + GetStoredFat().IncrementValue(-fatMassDecrease_g, MassUnit::g); + fat = GetStoredFat().GetValue(MassUnit::g); + totalMassLost_g += fatMassDecrease_g; + + //Body weight and other patient parameters + double weight = m_Patient->GetWeight().GetValue(MassUnit::g); + m_Patient->GetWeight().IncrementValue(-totalMassLost_g, MassUnit::g); + weight = m_Patient->GetWeight().GetValue(MassUnit::g); + m_Patient->GetBodyFatFraction().SetValue(GetStoredFat().GetValue(MassUnit::g) / m_Patient->GetWeight().GetValue(MassUnit::g)); + m_Patient->GetLeanBodyMass().SetValue(m_Patient->GetWeight().GetValue(MassUnit::g) - GetStoredFat().GetValue(MassUnit::g), MassUnit::g); + double SiriBodyDensity_g_Per_cm3 = 4.95 / (m_Patient->GetBodyFatFraction().GetValue() + 4.50); + double BrozekBodyDensity_g_Per_cm3 = 4.57 / (m_Patient->GetBodyFatFraction().GetValue() + 4.142); + double bodyDensity_g_Per_cm3 = (SiriBodyDensity_g_Per_cm3 + BrozekBodyDensity_g_Per_cm3) / 2.0; + m_Patient->GetBodyDensity().SetValue(bodyDensity_g_Per_cm3, MassPerVolumeUnit::g_Per_cm3); //See BioGears::SetUpPatient() + + //Set new blood concentrations + m_data.GetSubstances().SetLiquidCompartmentNonGasesForStarvation(starvedTime_hr); +} + //-------------------------------------------------------------------------------------------------- /// \brief /// Preprocess performs the systems interactions steps required for processing of the substances @@ -438,136 +485,131 @@ void Tissue::CalculateDiffusion() SETissueCompartment* tissue; SELiquidCompartment* vascular; const SESubstanceTissuePharmacokinetics* tissueKinetics; - + for (auto tissueVascular : m_TissueToVascular) { tissue = tissueVascular.first; vascular = tissueVascular.second; - SELiquidCompartment& extracellular = m_data.GetCompartments().GetExtracellularFluid(*tissue); - SELiquidCompartment& intracellular = m_data.GetCompartments().GetIntracellularFluid(*tissue); + SELiquidCompartment& extracellular = m_data.GetCompartments().GetExtracellularFluid(*tissue); + SELiquidCompartment& intracellular = m_data.GetCompartments().GetIntracellularFluid(*tissue); + //The ions Na, K, Cl, and Ca are handled by the IonActiveTransport method + MoveIonsByActiveTransport(*tissue, *vascular, extracellular, intracellular, m_Dt_s); + //Other species for (const SESubstance* sub : m_data.GetCompartments().GetLiquidCompartmentSubstances()) { - tissueKinetics = nullptr; - if(sub->HasPK()) - tissueKinetics = sub->GetPK()->GetTissueKinetics(tissue->GetName()); + //Don't do any calculations for Na, K, Cl, or Ca since they are handled by MoveIonsByActiveTransport function + if (sub->GetClassification()==CDM::enumSubstanceClass::Ion) + continue; + tissueKinetics = nullptr; + if(sub->HasPK()) + tissueKinetics = sub->GetPK()->GetTissueKinetics(tissue->GetName()); //Check to see if substance is a drug with the appropriate parameters to calculate PK diffusion - // If the substance is a PBPK drug, then diffusion is computed by perfusion limited diffusion, as described in \cite huisinga2012modeling + // If the substance is a PBPK drug, then diffusion is computed by perfusion limited diffusion, as described in \cite huisinga2012modeling if (tissueKinetics != nullptr) { - if (!tissueKinetics->HasPartitionCoefficient()) - { - Error("Attempted to diffuse a substance with PK that had no partition coefficient available."); - continue; - } + if (!tissueKinetics->HasPartitionCoefficient()) + { + Error("Attempted to diffuse a substance with PK that had no partition coefficient available."); + continue; + } PerfusionLimitedDiffusion(*tissue, *vascular, *sub, tissueKinetics->GetPartitionCoefficient(), m_Dt_s); //Balance happens in the method } - // Otherwise, the diffusion is computed by either: - // Instantaneous diffusion, Simple diffusion, Facilitated diffusion, or Active diffusion + // Otherwise, the diffusion is computed by either: + // Instantaneous diffusion, Simple diffusion, Facilitated diffusion, or Active diffusion else { - double moved_ug; //used only for possible debugging output + double moved_ug; //used only for possible debugging output // Skip the lungs because they have their own methodology with AlveolarPartialPressureGradientDiffusion - // This is not true. AlveolarPartialPressureGradientDiffusion goes from a gas compartment to a vascular cmpt. Still need to move stuff into the extravascular space. TODO - //Turned off in diffusion revamp; uncomment if things screw up + // This is not true. AlveolarPartialPressureGradientDiffusion goes from a gas compartment to a vascular cmpt. Still need to move stuff into the extravascular space. TODO + //Turned off in diffusion revamp; uncomment if things screw up //if (tissue == m_LeftLungTissue || tissue == m_RightLungTissue) //continue; - //We have to make an exception for the brain and TAGs, since TAG can't cross blood-brain barrier - if (sub->GetName() == "Triacylglycerol" && extracellular.GetName().find("Brain") != std::string::npos) - continue; - - //Gases get moved by instant diffusion - if (sub->GetState() == CDM::enumSubstanceState::Gas) - { - //Vascular to Extracellular - moved_ug = MoveMassByInstantDiffusion(*vascular, extracellular, *sub, m_Dt_s); - - //Extracellular to Intracellular - moved_ug = MoveMassByInstantDiffusion(extracellular, intracellular, *sub, m_Dt_s); - } - //Sodium is currently an oddball handled by instant diffusion, but we may want to change to simple and/or active when we do other ions - else if (sub == m_Sodium) - { - //Vascular to Extracellular - moved_ug = MoveMassByInstantDiffusion(*vascular, extracellular, *sub, m_Dt_s); - - //Extracellular to Intracellular - //Until we get active pumps working this will need to be off or else you'll get a huge initial movement of sodium - //moved_ug = MoveMassByInstantDiffusion(extracellular, intracellular, *sub, m_Dt_s); - } - //All non-gas substances (besides sodium) use either simple, facilitated, or active diffusion - //Bicarbonate doesn't diffuse because it is charged \todo gate diffusion by charge and handle amino acids and lactate specially using facilitated diffusion - else if (sub->GetName() != "Bicarbonate") - { - double molarMass_g_Per_mol = sub->GetMolarMass(MassPerAmountUnit::g_Per_mol); - - //Simple diffusion calculates a permeability based on molecular weight. Even large molecules will diffuse, though slowly. - //We want to prevent movement of large molecules like proteins completely. A gate of 1000 g/mol will filter out things like - //albumin, insulin, etc while allowing glucose, ions, and others to be governed by their molecular weight. - //Note: it doesn't consider lipophilicity, so TAG will need to be artificially tweaked using other diffusion methods. - if (molarMass_g_Per_mol < 1000) - { - // Compute the vascular to extracellular permeability coefficient - // This is the coefficient per gram of tissue independent of the tissue type. - // This uses the Renkin and Curry data for capillary exchange as reported in \cite fournier2011basic - // Divide by 100 is because the Renkin-Curry equations are in per hectogram units, and 100 g/hg - /// \todo I believe we can optimize with a cache of these values. Also, we can cache permeabilityCoefficient_mL_Per_s_g which is not a function of the tissue properties - double molecularRadius_nm = 0.0348*pow(molarMass_g_Per_mol, 0.4175); - double vToECpermeabilityCoefficient_mL_Per_s_g = 0.0287*pow(molecularRadius_nm, -2.920) / 100.0; // This is only valid if the molecular radius is > 1.0 nm. - if (molecularRadius_nm < 1.0) - vToECpermeabilityCoefficient_mL_Per_s_g = 0.0184*pow(molecularRadius_nm, -1.223) / 100.0; - - // Multiply by tissue mass to get the tissue-dependent coefficient. - double vToECpermeabilityCoefficient_mL_Per_s = vToECpermeabilityCoefficient_mL_Per_s_g * tissue->GetTotalMass(MassUnit::g); - - // Tuning factors can be used to help tune the dynamics - note that concentrations will ALWAYS equilibrate in steady state given enough time regardless of the permeability - double vToECPermeabilityTuningFactor = 1.0; - double ECtoICPermeabilityTuningFactor = 1.0; - - //Vascular to Extracellular - moved_ug = MoveMassBySimpleDiffusion(*vascular, extracellular, *sub, vToECPermeabilityTuningFactor*vToECpermeabilityCoefficient_mL_Per_s, m_Dt_s); - - //Extracellular to Intracellular - // Assuming that the capillary permeability coefficient is proportional to the cellular membrane permeability coefficient for a given tissue and substance - moved_ug = MoveMassBySimpleDiffusion(extracellular, intracellular, *sub, ECtoICPermeabilityTuningFactor*vToECpermeabilityCoefficient_mL_Per_s, m_Dt_s); - } - - //Facilitated diffusion depends on the substance having flux values - //Currently, glucose is the only substance with "real" flux values (but even they are chosen to give good engine behavior) - //TAG and ketones have "fake" flux values meant to give extra diffusion movement due to lipophilicity - if (sub->HasMaximumDiffusionFlux()) - { - double massToAreaCoefficient_cm2_Per_g = 1.0; /// \todo Define relationship between tissue mass and membrane area. - double capCoverage_cm2 = massToAreaCoefficient_cm2_Per_g * tissue->GetTotalMass(MassUnit::g); - double maximumMassFlux = sub->GetMaximumDiffusionFlux(MassPerAreaTimeUnit::g_Per_cm2_s); - double combinedCoefficient_g_Per_s = maximumMassFlux*capCoverage_cm2; - - //Vascular to Extracellular - moved_ug = MoveMassByFacilitatedDiffusion(*vascular, extracellular, *sub, combinedCoefficient_g_Per_s, m_Dt_s); - - //Extracellular to Intracellular - moved_ug = MoveMassByFacilitatedDiffusion(extracellular, intracellular, *sub, combinedCoefficient_g_Per_s, m_Dt_s); - } - - //Last is active diffusion, which isn't currently used - //Couple possibly gate it by pump rate - //double pumpRate_g_Per_s = 0.0; - /// \todo Compute the pump rate from an empirically-determined baseline pump rate. - - //Vascular to Extracellular - //moved_ug = MoveMassByActiveTransport(*vascular, extracellular, *sub, pumpRate_g_Per_s, m_Dt_s); - - //Extracellular to Intracellular - //moved_ug = MoveMassByActiveTransport(extracellular, intracellular, *sub, pumpRate_g_Per_s, m_Dt_s); - - } - - //Now that mass has been moved, balance to set concentrations and molarities - vascular->GetSubstanceQuantity(*sub)->Balance(BalanceLiquidBy::Mass); - extracellular.GetSubstanceQuantity(*sub)->Balance(BalanceLiquidBy::Mass); - intracellular.GetSubstanceQuantity(*sub)->Balance(BalanceLiquidBy::Mass); + //We have to make an exception for the brain and TAGs, since TAG can't cross blood-brain barrier + if (sub->GetName() == "Triacylglycerol" && extracellular.GetName().find("Brain") != std::string::npos) + continue; + + //Gases get moved by instant diffusion + if (sub->GetState() == CDM::enumSubstanceState::Gas) + { + //Vascular to Extracellular + moved_ug = MoveMassByInstantDiffusion(*vascular, extracellular, *sub, m_Dt_s); + + //Extracellular to Intracellular + moved_ug = MoveMassByInstantDiffusion(extracellular, intracellular, *sub, m_Dt_s); + } + //Bicarbonate doesn't diffuse because it is charged + else if (sub->GetName() != "Bicarbonate") + { + double molarMass_g_Per_mol = sub->GetMolarMass(MassPerAmountUnit::g_Per_mol); + + //Simple diffusion calculates a permeability based on molecular weight. Even large molecules will diffuse, though slowly. + //We want to prevent movement of large molecules like proteins completely. A gate of 1000 g/mol will filter out things like + //albumin, insulin, etc while allowing glucose, ions, and others to be governed by their molecular weight. + //Note: it doesn't consider lipophilicity, so TAG will need to be artificially tweaked using other diffusion methods. + if (molarMass_g_Per_mol < 1000) + { + // Compute the vascular to extracellular permeability coefficient + // This is the coefficient per gram of tissue independent of the tissue type. + // This uses the Renkin and Curry data for capillary exchange as reported in \cite fournier2011basic + // Divide by 100 is because the Renkin-Curry equations are in per hectogram units, and 100 g/hg + /// \todo I believe we can optimize with a cache of these values. Also, we can cache permeabilityCoefficient_mL_Per_s_g which is not a function of the tissue properties + double molecularRadius_nm = 0.0348*pow(molarMass_g_Per_mol, 0.4175); + double vToECpermeabilityCoefficient_mL_Per_s_g = 0.0287*pow(molecularRadius_nm, -2.920) / 100.0; // This is only valid if the molecular radius is > 1.0 nm. + if (molecularRadius_nm < 1.0) + vToECpermeabilityCoefficient_mL_Per_s_g = 0.0184*pow(molecularRadius_nm, -1.223) / 100.0; + + // Multiply by tissue mass to get the tissue-dependent coefficient. + double vToECpermeabilityCoefficient_mL_Per_s = vToECpermeabilityCoefficient_mL_Per_s_g * tissue->GetTotalMass(MassUnit::g); + + // Tuning factors can be used to help tune the dynamics - note that concentrations will ALWAYS equilibrate in steady state given enough time regardless of the permeability + double vToECPermeabilityTuningFactor = 1.0; + double ECtoICPermeabilityTuningFactor = 1.0; + + //Vascular to Extracellular + moved_ug = MoveMassBySimpleDiffusion(*vascular, extracellular, *sub, vToECPermeabilityTuningFactor*vToECpermeabilityCoefficient_mL_Per_s, m_Dt_s); + + //Extracellular to Intracellular + // Assuming that the capillary permeability coefficient is proportional to the cellular membrane permeability coefficient for a given tissue and substance + moved_ug = MoveMassBySimpleDiffusion(extracellular, intracellular, *sub, ECtoICPermeabilityTuningFactor*vToECpermeabilityCoefficient_mL_Per_s, m_Dt_s); + } + + //Facilitated diffusion depends on the substance having flux values + //Currently, glucose is the only substance with "real" flux values (but even they are chosen to give good engine behavior) + //TAG and ketones have "fake" flux values meant to give extra diffusion movement due to lipophilicity + if (sub->HasMaximumDiffusionFlux()) + { + double massToAreaCoefficient_cm2_Per_g = 1.0; /// \todo Define relationship between tissue mass and membrane area. + double capCoverage_cm2 = massToAreaCoefficient_cm2_Per_g * tissue->GetTotalMass(MassUnit::g); + double maximumMassFlux = sub->GetMaximumDiffusionFlux(MassPerAreaTimeUnit::g_Per_cm2_s); + double combinedCoefficient_g_Per_s = maximumMassFlux*capCoverage_cm2; + + //Vascular to Extracellular + moved_ug = MoveMassByFacilitatedDiffusion(*vascular, extracellular, *sub, combinedCoefficient_g_Per_s, m_Dt_s); + + //Extracellular to Intracellular + moved_ug = MoveMassByFacilitatedDiffusion(extracellular, intracellular, *sub, combinedCoefficient_g_Per_s, m_Dt_s); + } + + //Last is active diffusion, which isn't currently used + //Couple possibly gate it by pump rate + //double pumpRate_g_Per_s = 0.0; + /// \todo Compute the pump rate from an empirically-determined baseline pump rate. + + //Vascular to Extracellular + //moved_ug = MoveMassByActiveTransport(*vascular, extracellular, *sub, pumpRate_g_Per_s, m_Dt_s); + + //Extracellular to Intracellular + //moved_ug = MoveMassByActiveTransport(extracellular, intracellular, *sub, pumpRate_g_Per_s, m_Dt_s); + + } + + //Now that mass has been moved, balance to set concentrations and molarities + vascular->GetSubstanceQuantity(*sub)->Balance(BalanceLiquidBy::Mass); + extracellular.GetSubstanceQuantity(*sub)->Balance(BalanceLiquidBy::Mass); + intracellular.GetSubstanceQuantity(*sub)->Balance(BalanceLiquidBy::Mass); } } } @@ -658,6 +700,7 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) double achievedWorkRate_W = 0; double achievedExerciseLevel = 0; double fatigueLevel = 0; + static double totalFatConsumed_g = 0; //Data double energyPerMolATP_kcal = m_data.GetConfiguration().GetEnergyPerATP(EnergyPerAmountUnit::kcal_Per_mol); @@ -683,10 +726,16 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) double TAG_CellularEfficiency = energyPerMolATP_kcal * ATP_Per_TAG / (3*2340 + 686); //Palmitic acid free energy is 2340 kcal, glycerol is similar to glucose, so assume 686 \cite voet2013fundamentals double AA_CellularEfficiency = energyPerMolATP_kcal * ATP_Per_AA / 387.189; //Alanine heat of combustion is 1.62 MJ/mol \cite livesey1984energy double ketones_CellularEfficiency = glucose_CellularEfficiency; //Assuming the same as glucose - double mandatoryMuscleAnaerobicFraction = .035; //There is always some anaerobic consumption in the body, particularly in muscle fibers with few mitochondria \cite boron2012medical + double mandatoryMuscleAnaerobicFraction = .1; //There is always some anaerobic consumption in the body, particularly in muscle fibers with few mitochondria \cite boron2012medical double kcal_Per_day_Per_Watt = 20.6362855; double maxWorkRate_W = 1200; //see Energy::Exercise + //Patients with COPD show higher levels of anaerobic metabolism \cite mathur1999cerebral \cite engelen2000factors + if (m_data.GetConditions().HasChronicObstructivePulmonaryDisease()) + { + mandatoryMuscleAnaerobicFraction *= 1.5; //50% increase + } + //Reusable values for looping SELiquidCompartment* vascular; SELiquidSubstanceQuantity* TissueO2; @@ -887,10 +936,11 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) } //Start with AA, since obligatory protein loss is 30g/day minimum going up to 125g/day in starvation \cite guyton2006medical - //Use hormone factor (based on liver) to determine "how starved" you are and vary AA consumption linearly from 30 to 125 - double localHormoneFactor = Hepatic::CalculateRelativeHormoneChange(GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, vascular->GetSubstanceQuantity(*m_Insulin), vascular->GetSubstanceQuantity(*m_Glucagon)); + //However, some of this consumption is due to gluconeogenesis (10-20 grams or so, see \cite garber1974hepatic) + //Use hormone factor (based on liver) to determine "how starved" you are and vary AA consumption linearly from 15 to 110 + double localHormoneFactor = Hepatic::CalculateRelativeHormoneChange(GetLiverInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, GetLiverGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, vascular->GetSubstanceQuantity(*m_Insulin), vascular->GetSubstanceQuantity(*m_Glucagon), m_data); BLIM(localHormoneFactor, -2, 0); //positive hormone factors mean we should consume the expected 30g/day - double AAConsumptionRate_g_Per_day = GeneralMath::LinearInterpolator(0, 2, 30, 125, -localHormoneFactor); + double AAConsumptionRate_g_Per_day = GeneralMath::LinearInterpolator(0, 2, 15, 110, -localHormoneFactor); double AAToConsume_mol = (AAConsumptionRate_g_Per_day * time_s * BloodFlowFraction) / (24 * 3600 * m_AminoAcids->GetMolarMass(MassPerAmountUnit::g_Per_mol)); @@ -966,10 +1016,12 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) //Under aerobic conditions, muscles and other tissues burn intracellular FFA by B-oxidation //When fat consumption is unlimited, glycogen is virtually unused, so limit the rate of fat consumption based on hormone factor - //When fat is completely unused, full glycogen stores last almost exactly 12 hours, so vary the rate limitation linearly between 0 for hormone factors >=0 to 1 for the most negative hormone factor + //When fat is completely unused, full glycogen stores last about 16 hours + //The rate should obviously vary between these endpoints, but we have to manage RQ and fat consumption, too //We expect to consume 50-70 or so grams of fat per day, so mols of TAG should be on the order of 1.7e-8, ballpark + //For now, vary linearly between 0 and an empirically determined .001 that gives 60g/day fat consumption and lowers glycogen duration //todo readdress this scaling with respect to exercise to try to make the respiratory quotient trend correctly - double rateLimitingTuningFactor = GeneralMath::LinearInterpolator(0, 2, 0, 1, -localHormoneFactor); + double rateLimitingTuningFactor = GeneralMath::LinearInterpolator(0, 2, 0, .001, -localHormoneFactor); double usableEnergyAsTissueTAG_kcal = rateLimitingTuningFactor*(TissueTAG->GetMolarity(AmountPerVolumeUnit::mol_Per_L)*TissueVolume_L) * ATP_Per_TAG * energyPerMolATP_kcal / TAG_CellularEfficiency; @@ -988,6 +1040,7 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) nonbrainNeededEnergy_kcal -= totalEnergyUsed; heatGenerated_kcal += totalEnergyUsed * (1 - TAG_CellularEfficiency); tissueNeededEnergy_kcal = 0; + totalFatConsumed_g += TAGToConsume_mol * m_Triacylglycerol->GetMolarMass(MassPerAmountUnit::g_Per_mol); } else { @@ -999,6 +1052,7 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) nonbrainNeededEnergy_kcal -= totalEnergyUsed; heatGenerated_kcal += totalEnergyUsed * (1 - TAG_CellularEfficiency); tissueNeededEnergy_kcal -= totalEnergyUsed; + totalFatConsumed_g += TAGToConsume_mol * m_Triacylglycerol->GetMolarMass(MassPerAmountUnit::g_Per_mol); } totalO2Consumed_mol += TAGToConsume_mol * O2_Per_TAG; totalCO2Produced_mol += TAGToConsume_mol * CO2_Per_TAG; @@ -1019,6 +1073,7 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) nonbrainNeededEnergy_kcal -= totalEnergyUsed; heatGenerated_kcal += totalEnergyUsed * (1 - TAG_CellularEfficiency); tissueNeededEnergy_kcal -= totalEnergyUsed; + totalFatConsumed_g += TAGToConsume_mol * m_Triacylglycerol->GetMolarMass(MassPerAmountUnit::g_Per_mol); } else { @@ -1030,6 +1085,7 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) nonbrainNeededEnergy_kcal -= totalEnergyUsed; heatGenerated_kcal += totalEnergyUsed * (1 - TAG_CellularEfficiency); tissueNeededEnergy_kcal -= totalEnergyUsed; + totalFatConsumed_g += TAGToConsume_mol * m_Triacylglycerol->GetMolarMass(MassPerAmountUnit::g_Per_mol); } totalO2Consumed_mol += TAGToConsume_mol * O2_Per_TAG; totalCO2Produced_mol += TAGToConsume_mol * CO2_Per_TAG; @@ -1253,10 +1309,30 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) totalCO2Produced_mol += m_hepaticCO2Produced_mol; oxygenConsumptionRate_g_Per_s = totalO2Consumed_mol * m_O2->GetMolarMass(MassPerAmountUnit::g_Per_mol) / time_s; carbonDioxideProductionRate_g_Per_s = totalCO2Produced_mol * m_CO2->GetMolarMass(MassPerAmountUnit::g_Per_mol) / time_s; + double O2Consumption_mL_Per_s = (oxygenConsumptionRate_g_Per_s / m_O2->GetDensity(MassPerVolumeUnit::g_Per_mL)) ; + double CO2Production_mL_Per_s = (carbonDioxideProductionRate_g_Per_s / m_CO2->GetDensity(MassPerVolumeUnit::g_Per_mL)) ; respiratoryQuotient = totalCO2Produced_mol / totalO2Consumed_mol; - GetOxygenConsumptionRate().SetValue(oxygenConsumptionRate_g_Per_s / m_O2->GetDensity(MassPerVolumeUnit::g_Per_mL), VolumePerTimeUnit::mL_Per_s); - GetCarbonDioxideProductionRate().SetValue(carbonDioxideProductionRate_g_Per_s / m_CO2->GetDensity(MassPerVolumeUnit::g_Per_mL), VolumePerTimeUnit::mL_Per_s); - GetRespiratoryExchangeRatio().SetValue(respiratoryQuotient); + + m_O2ConsumedRunningAverage_mL_Per_s.Sample(O2Consumption_mL_Per_s); + m_CO2ProducedRunningAverage_mL_Per_s.Sample(CO2Production_mL_Per_s); + m_RespiratoryQuotientRunningAverage.Sample(respiratoryQuotient); + + //Only record data every 50 steps for these to iron out noise + int steps = m_data.GetSimulationTime().GetValue(TimeUnit::s) / time_s; + if (steps % 50 == 0) + { + GetOxygenConsumptionRate().SetValue(m_O2ConsumedRunningAverage_mL_Per_s.Value(), VolumePerTimeUnit::mL_Per_s); + GetCarbonDioxideProductionRate().SetValue(m_CO2ProducedRunningAverage_mL_Per_s.Value(), VolumePerTimeUnit::mL_Per_s); + GetRespiratoryExchangeRatio().SetValue(m_RespiratoryQuotientRunningAverage.Value()); + + m_O2ConsumedRunningAverage_mL_Per_s.Reset(); + m_CO2ProducedRunningAverage_mL_Per_s.Reset(); + m_RespiratoryQuotientRunningAverage.Reset(); + } + + //GetOxygenConsumptionRate().SetValue(O2Consumption_mL_Per_s, VolumePerTimeUnit::mL_Per_s); + //GetCarbonDioxideProductionRate().SetValue(CO2Production_mL_Per_s, VolumePerTimeUnit::mL_Per_s); + //GetRespiratoryExchangeRatio().SetValue(respiratoryQuotient); m_energy->GetLactateProductionRate().SetValue(lactateProductionRate_mol_Per_s, AmountPerTimeUnit::mol_Per_s); achievedWorkRate_W = (1/m_Dt_s)*3600*24*(exerciseEnergyRequested_kcal - brainEnergyDeficit_kcal - nonbrainEnergyDeficit_kcal) / kcal_Per_day_Per_Watt; @@ -1277,16 +1353,28 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) double fatigue = ((brainEnergyDeficit_kcal + nonbrainEnergyDeficit_kcal) / (baseEnergyRequested_kcal + exerciseEnergyRequested_kcal)); /// \event Patient: Fatigue - Energy stores are sub-maximal. - if (fatigue > 0.0) + //Fatigue event only goes away if it's been more than 30 seconds since the last fatigue value (to avoid repeatedly flipping on and off on boundaries) + if (fatigue > 1e-10) { - m_Patient->SetEvent(CDM::enumPatientEvent::Fatigue, true, m_data.GetSimulationTime()); + m_lastFatigueTime = m_data.GetSimulationTime().GetValue(TimeUnit::s); + if(!m_Patient->IsEventActive(CDM::enumPatientEvent::Fatigue)) + m_Patient->SetEvent(CDM::enumPatientEvent::Fatigue, true, m_data.GetSimulationTime()); } - else + else if (fatigue < 1e-10 && m_data.GetSimulationTime().GetValue(TimeUnit::s) - m_lastFatigueTime > 30) { m_Patient->SetEvent(CDM::enumPatientEvent::Fatigue, false, m_data.GetSimulationTime()); } m_energy->GetFatigueLevel().SetValue(fatigue); + //Update other patient parameters + //\todo: make sure body weight decreases when CO2 is exhaled + m_Patient->GetBodyFatFraction().SetValue(GetStoredFat().GetValue(MassUnit::g) / m_Patient->GetWeight().GetValue(MassUnit::g)); + m_Patient->GetLeanBodyMass().SetValue(m_Patient->GetWeight().GetValue(MassUnit::g) - GetStoredFat().GetValue(MassUnit::g), MassUnit::g); + double SiriBodyDensity_g_Per_cm3 = 4.95 / (m_Patient->GetBodyFatFraction().GetValue() + 4.50); + double BrozekBodyDensity_g_Per_cm3 = 4.57 / (m_Patient->GetBodyFatFraction().GetValue() + 4.142); + double bodyDensity_g_Per_cm3 = (SiriBodyDensity_g_Per_cm3 + BrozekBodyDensity_g_Per_cm3) / 2.0; + m_Patient->GetBodyDensity().SetValue(bodyDensity_g_Per_cm3, MassPerVolumeUnit::g_Per_cm3); //See BioGears::SetUpPatient() + //Reset O2/CO2 member variables since they're static m_hepaticCO2Produced_mol = 0; m_hepaticO2Consumed_mol = 0; @@ -1296,6 +1384,7 @@ void Tissue::CalculateMetabolicConsumptionAndProduction(double time_s) //m_data.GetDataTrack().Probe("InstantaneousNonBrainEnergyDeficit_kcal", nonbrainEnergyDeficit_kcal); //m_data.GetDataTrack().Probe("NonBrainDeficitFraction", nonbrainEnergyDeficit_kcal / (.8*baseEnergyRequested_kcal + exerciseEnergyRequested_kcal)); //m_data.GetDataTrack().Probe("InstantaneousMetabolicHeatGenerated_kcal", heatGenerated_kcal); + //m_data.GetDataTrack().Probe("CumulativeTAGConsumed_g", totalFatConsumed_g); //Gives you an idea of what non-muscle compartments don't get enough O2 flow and is better than using Info every timestep //Can remove once we get everything tuned adequately @@ -1324,11 +1413,19 @@ void Tissue::ProteinStorageAndRelease() double aminoAcidsBaseline_mg_Per_dL = 50; // \todo make this a CDM set point like glucose? - double hormoneFactor = Hepatic::CalculateRelativeHormoneChange(GetMuscleInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, GetMuscleGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_MuscleInsulin, m_MuscleGlucagon); + //Patients with diabetes type 2 show increased blood AA; allow for up to 30 mg/dL elevation in severe cases \cite guyton2006medical p989 + if (m_data.GetConditions().HasDiabetesType2()) + { + if (m_data.GetConditions().GetDiabetesType2()->HasInsulinResistanceSeverity()) + { + double increasedAA_mg_Per_dL = GeneralMath::LinearInterpolator(0, 1, 0, 30, m_data.GetConditions().GetDiabetesType2()->GetInsulinResistanceSeverity().GetValue()); + aminoAcidsBaseline_mg_Per_dL += increasedAA_mg_Per_dL; + } + } + + double hormoneFactor = Hepatic::CalculateRelativeHormoneChange(GetMuscleInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, GetMuscleGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_MuscleInsulin, m_MuscleGlucagon, m_data); //Guyton says protein blood concentrations should only rise 2-3 mg/dL even after eating because of absorption into tissues - //These values allow a 75g protein meal to only raise amino acid blood concentration ~7 mg/dL - //These might need to change if rate at which protein is absorbed into blood changes double proteinStorageLowerRate_g_Per_s = .3; double proteinStorageUpperRate_g_Per_s = .6; @@ -1342,6 +1439,12 @@ void Tissue::ProteinStorageAndRelease() //https://www.wolframalpha.com/input/?i=y%3D.1%2B.1%2F(1%2Be%5E(-15(x-.75)))+from+0%3Cy%3C.2+and+0%3Cx%3C2 double proteinBreakdownRate_g_Per_s = proteinBreakdownLowerRate_g_Per_s + GeneralMath::LogisticFunction(proteinBreakdownUpperRate_g_Per_s - proteinBreakdownLowerRate_g_Per_s, .75, 15, -hormoneFactor); + //Patients with COPD experience high protein turnover, so up the breakdown rate by a percentage \cite engelen2000enhanced + if (m_data.GetConditions().HasChronicObstructivePulmonaryDisease()) + { + proteinBreakdownRate_g_Per_s *= 1.13; //13% increase + } + //remove excess amino acids from blood and store in muscle while there's room //Body mobilizes protein when glucagon dominates, but we'll have protein shift toward storage unless insulin drops significantly if (hormoneFactor >= -.2 && GetStoredProtein(MassUnit::g) < m_maxProteinStorage_g && m_MuscleAA->GetConcentration().GetValue(MassPerVolumeUnit::mg_Per_dL) > aminoAcidsBaseline_mg_Per_dL) @@ -1357,18 +1460,39 @@ void Tissue::ProteinStorageAndRelease() GetStoredProtein().IncrementValue(proteinStorageRate_g_Per_s * m_Dt_s, MassUnit::g); m_MuscleAA->Balance(BalanceLiquidBy::Mass); } + + //If we rise above 10% of protein storage, turn off the Muscle Catabolism event + if (m_Patient->IsEventActive(CDM::enumPatientEvent::MuscleCatabolism) && GetStoredProtein(MassUnit::g) > .1*m_maxProteinStorage_g) + { + m_Patient->SetEvent(CDM::enumPatientEvent::MuscleCatabolism, false, m_data.GetSimulationTime()); + } } - //remove amino acids from protein stores and dump into blood - //This is not the same as muscle degradation during starvation + //Remove amino acids from protein stores (or muscle if that's depleted) and dump into blood //We don't dump AA into blood if blood concentration is already at basal level else if (hormoneFactor < 0 && m_MuscleAA->GetConcentration().GetValue(MassPerVolumeUnit::mg_Per_dL) < aminoAcidsBaseline_mg_Per_dL) { + //Muscle catabolism if (GetStoredProtein(MassUnit::g) < proteinBreakdownRate_g_Per_s * m_Dt_s) { - //m_ss << "Not enough stored amino acids remaining!"; - //Info(m_ss); + if (!m_Patient->IsEventActive(CDM::enumPatientEvent::MuscleCatabolism)) + { + m_Patient->SetEvent(CDM::enumPatientEvent::MuscleCatabolism, true, m_data.GetSimulationTime()); + } + if (m_Patient->GetMuscleMass(MassUnit::g) < proteinBreakdownRate_g_Per_s * m_Dt_s) + { + m_ss << "The patient has consumed all of their body's amino acids. They've effectively starved."; + Warning(m_ss); + m_Patient->SetEvent(CDM::enumPatientEvent::IrreversibleState, true, m_data.GetSimulationTime()); + } + else + { + m_MuscleAA->GetMass().IncrementValue(proteinBreakdownRate_g_Per_s * m_Dt_s, MassUnit::g); + m_Patient->GetMuscleMass().IncrementValue(-proteinBreakdownRate_g_Per_s * m_Dt_s, MassUnit::g); + m_MuscleAA->Balance(BalanceLiquidBy::Mass); + } } + //Temporary protein storage used else { m_MuscleAA->GetMass().IncrementValue(proteinBreakdownRate_g_Per_s * m_Dt_s, MassUnit::g); @@ -1378,6 +1502,78 @@ void Tissue::ProteinStorageAndRelease() } } +//-------------------------------------------------------------------------------------------------- +/// \brief +/// Establishes the anemia condition by removing hemoglobin and reducing blood viscosity (simulated by vascular resistance reduction). +/// +/// \details +/// We are modeling iron deficiency anemia as a chronic reduction in hemoglobin in the blood as well as +/// a reduction in the cardiovascular resistances to lower the systemic vascular resistance that is +/// seen with the reduced viscosity. The oxygen carrying capacity of the blood is reduced due to the +/// decrease in hemoglobin content. +//-------------------------------------------------------------------------------------------------- +void Tissue::Dehydrate() +{ + SEPatient& Patient = m_data.GetPatient(); + SEDehydration* dehydration = m_data.GetConditions().GetDehydration(); + SEScalarMassPerVolume density; + GeneralMath::CalculateWaterDensity(m_data.GetEnergy().GetCoreTemperature(), density); + + //dehydration determine patient weight loss due to water deficiency + double fractionalWeightLoss = dehydration->GetDehydrationFraction().GetValue(); + + //Set tissue value on CDM + GetDehydrationFraction().SetValue(fractionalWeightLoss); + + double patientMass_kg = m_Patient->GetWeight(MassUnit::kg); + + double waterReduction_L = patientMass_kg * fractionalWeightLoss / density.GetValue(MassPerVolumeUnit::kg_Per_L); + + //from fraction that is water + double waterReductionFraction = waterReduction_L / m_data.GetTissue().GetTotalBodyFluidVolume(VolumeUnit::L); + + //tracking fluid losses + double temp_mL = 0.0; + double totalFluidLoss_mL = 0.0; + + //loop over extracellular fluid compartments and decrement + for (SETissueCompartment* tissue : m_data.GetCompartments().GetTissueLeafCompartments()) + { + SELiquidCompartment& extracellularFluid = m_data.GetCompartments().GetExtracellularFluid(*tissue); //get the compartment + temp_mL = extracellularFluid.GetVolume().GetValue(VolumeUnit::mL); //pull the volume + totalFluidLoss_mL += temp_mL * waterReductionFraction; + extracellularFluid.GetVolume().SetValue(temp_mL * (1 - waterReductionFraction), VolumeUnit::mL); //set the total volume + extracellularFluid.Balance(BalanceLiquidBy::Mass); + + SELiquidCompartment& intracellularFluid = m_data.GetCompartments().GetIntracellularFluid(*tissue); //get the compartment + temp_mL = intracellularFluid.GetVolume().GetValue(VolumeUnit::mL); //pull the volume + totalFluidLoss_mL += temp_mL * waterReductionFraction; + intracellularFluid.GetVolume().SetValue(temp_mL * (1 - waterReductionFraction), VolumeUnit::mL); //set new volume + intracellularFluid.Balance(BalanceLiquidBy::Mass); + } + double blood_mL = 0.0; + for (SELiquidCompartment* cmpt : m_data.GetCompartments().GetVascularLeafCompartments()) + { + if (!cmpt->HasVolume()) + continue; + temp_mL = cmpt->GetVolume(VolumeUnit::mL); //pull the volume + totalFluidLoss_mL += temp_mL * waterReductionFraction; + blood_mL += temp_mL; + cmpt->GetVolume().SetValue(temp_mL * (1 - waterReductionFraction), VolumeUnit::mL); + cmpt->Balance(BalanceLiquidBy::Mass); + } + + //set patient weight + double bloodDensity_kg_Per_mL = m_data.GetBloodChemistry().GetBloodDensity(MassPerVolumeUnit::kg_Per_mL); + double bodyWeightLost_kg = bloodDensity_kg_Per_mL * totalFluidLoss_mL; + + patientMass_kg -= bodyWeightLost_kg; + m_Patient->GetWeight().SetValue(patientMass_kg, MassUnit::kg); + + //need to set blood volume here + m_data.GetCardiovascular().GetBloodVolume().SetValue(blood_mL, VolumeUnit::mL); +} + //-------------------------------------------------------------------------------------------------- /// \brief /// Regulates blood triacylglyceride levels by storing/releasing in fat tissue. @@ -1391,7 +1587,7 @@ void Tissue::FatStorageAndRelease() { double triacylglycerolBaseline_mg_Per_dL = 75; // \todo make this a CDM set point like glucose? - double hormoneFactor = Hepatic::CalculateRelativeHormoneChange(GetFatInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, GetFatGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_FatInsulin, m_FatGlucagon); + double hormoneFactor = Hepatic::CalculateRelativeHormoneChange(GetFatInsulinSetPoint().GetValue(AmountPerVolumeUnit::mmol_Per_L)*1e9, GetFatGlucagonSetPoint().GetValue(MassPerVolumeUnit::mg_Per_mL)*1e9, m_FatInsulin, m_FatGlucagon, m_data); //Guyton says triglycerides in chylomicrons are clear from blood in "a few hours" with chylomicrons having half-life <1 hour //Other sources show triglycerides returning to normal in ~4 hours after peak @@ -1433,8 +1629,9 @@ void Tissue::FatStorageAndRelease() { if (GetStoredFat(MassUnit::g) < fatReleaseRate_g_Per_s * m_Dt_s) { - //m_ss << "Not enough stored fat remaining!"; - //Info(m_ss); + m_ss << "The patient has consumed all of their body's fat. They've effectively starved."; + Warning(m_ss); + m_Patient->SetEvent(CDM::enumPatientEvent::IrreversibleState, true, m_data.GetSimulationTime()); } else { @@ -1463,27 +1660,35 @@ void Tissue::CalculateVitals() double currentFluidMass_kg = 0.0; SETissueCompartment* tissue; SELiquidCompartment* vascular; + SEScalarMassPerVolume density; + GeneralMath::CalculateWaterDensity(m_data.GetEnergy().GetCoreTemperature(), density); for (auto tissueVascular : m_TissueToVascular) { tissue = tissueVascular.first; vascular = tissueVascular.second; currentFluidMass_kg += vascular->GetVolume(VolumeUnit::mL)*m_data.GetBloodChemistry().GetBloodDensity(MassPerVolumeUnit::kg_Per_mL); - currentFluidMass_kg += m_data.GetCompartments().GetIntracellularFluid(*tissue).GetVolume(VolumeUnit::mL)*m_data.GetConfiguration().GetWaterDensity(MassPerVolumeUnit::kg_Per_mL); - currentFluidMass_kg += m_data.GetCompartments().GetExtracellularFluid(*tissue).GetVolume(VolumeUnit::mL)*m_data.GetConfiguration().GetWaterDensity(MassPerVolumeUnit::kg_Per_mL); + currentFluidMass_kg += m_data.GetCompartments().GetIntracellularFluid(*tissue).GetVolume(VolumeUnit::mL)*density.GetValue(MassPerVolumeUnit::kg_Per_mL); + currentFluidMass_kg += m_data.GetCompartments().GetExtracellularFluid(*tissue).GetVolume(VolumeUnit::mL)*density.GetValue(MassPerVolumeUnit::kg_Per_mL); } - if ((m_RestingFluidMass_kg - currentFluidMass_kg) / m_RestingPatientMass_kg > 0.03) + double dehydrationFraction = (m_RestingFluidMass_kg - currentFluidMass_kg) / m_RestingPatientMass_kg; + GetDehydrationFraction().SetValue(dehydrationFraction); + if (dehydrationFraction > 0.03) { /// \event Patient: Patient is dehydrated when 3% of body mass is lost due to fluid reduction m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Dehydration, true, m_data.GetSimulationTime()); /// \cite who2005dehydration } - else if ((m_RestingFluidMass_kg - currentFluidMass_kg) / m_RestingPatientMass_kg < 0.02) + else if (dehydrationFraction < 0.01) { m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Dehydration, false, m_data.GetSimulationTime()); } - // Total tissue volume + ///\TODO: check for patient thirst, when plasma osmolarity increases 3 percent \cite cheuvront2014dehydration + + + // Total tissue/body fluid volume double ecVol_mL = 0.; double icvol_mL = 0.; + double bloodvol_mL = 0.0; for (auto itr : m_data.GetCompartments().GetExtracellularFluid()) { ecVol_mL += itr.second->GetVolume(VolumeUnit::mL); @@ -1495,19 +1700,8 @@ void Tissue::CalculateVitals() GetExtracellularFluidVolume().SetValue(ecVol_mL,VolumeUnit::mL); GetIntracellularFluidVolume().SetValue(icvol_mL, VolumeUnit::mL); GetExtravascularFluidVolume().SetValue(ecVol_mL + icvol_mL, VolumeUnit::mL); - - // Fasciculations (due to calcium deficiency) - Currently inactive for model improvement - // The leading causes of fasciculation include magnesium deficiency, succinylcholine, nerve agents, and ALS. - // Electrolyte imbalance may cause fasciculations. - /*if (m_Muscleintracellular.GetSubstanceQuantity(*m_Calcium)->GetConcentration(MassPerVolumeUnit::g_Per_L) < 1.0) - { - /// \event Patient: Patient is fasciculating due to calcium deficiency - m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Fasciculation, true, m_data.GetSimulationTime()); - } - else if (m_Muscleintracellular.GetSubstanceQuantity(*m_Calcium)->GetConcentration(MassPerVolumeUnit::g_Per_L) > 3.0) - { - m_data.GetPatient().SetEvent(CDM::enumPatientEvent::Fasciculation, false, m_data.GetSimulationTime()); - }*/ + bloodvol_mL = m_data.GetCardiovascular().GetBloodVolume(VolumeUnit::mL); + GetTotalBodyFluidVolume().SetValue(ecVol_mL + icvol_mL + bloodvol_mL, VolumeUnit::mL); } //-------------------------------------------------------------------------------------------------- @@ -2022,3 +2216,198 @@ double Tissue::MoveMassByActiveTransport(SELiquidCompartment& source, SELiquidCo return amountIncrement_g; } // End ActiveMassTransport + + /// -------------------------------------------------------------------------------------------------- + /// \brief + /// Calculates the mass transport of ionic species Na, K, Ca, Cl against their electrochemical gradient using active pumps and cotransport + /// + /// \param tissue: tissue compartment + /// \param vascular: vascular space adjoining tissue compartment + /// \param extra: extracellular space in tissue compartment + /// \param intra: intracellular space in tissue compartment + /// \param timestep_s: the time step + /// + /// \details + /// This active transport moves ions to maintain their resting intracellular and extracellular concentrations using the cell membrane potential and active transport. + /// The current experience by each ion is calculated by comparing its nernst potential to the membrane potential. Active pumps are then called which have been tuned + /// to drive ionic current to maintain homeostatic concentrations. Since this method replaces diffusion for Na, Ca, K, and Cl, transport from the extracellular space + /// to the vascular space is also handled here and balancing of the ions is also performed. + //-------------------------------------------------------------------------------------------------- + +void Tissue::MoveIonsByActiveTransport(SETissueCompartment& tissue, SELiquidCompartment& vascular, SELiquidCompartment& extra, SELiquidCompartment& intra, double timestep_s) +{ + double faradaysConstant_C_Per_mol = 96485; + double membranePotential_V = tissue.GetMembranePotential(ElectricPotentialUnit::V); + double intracellularVolume_mL = intra.GetVolume(VolumeUnit::mL); + + //Get all ion concentrations + double sodiumExtracellular_M = extra.GetSubstanceQuantity(*m_Sodium)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double sodiumIntracellular_M = intra.GetSubstanceQuantity(*m_Sodium)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double potassiumExtracellular_M = extra.GetSubstanceQuantity(*m_Potassium)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double potassiumIntracellular_M = intra.GetSubstanceQuantity(*m_Potassium)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double chlorideExtracellular_M = extra.GetSubstanceQuantity(*m_Chloride)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double chlorideIntracellular_M = intra.GetSubstanceQuantity(*m_Chloride)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double calciumExtracellular_M = extra.GetSubstanceQuantity(*m_Calcium)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double calciumIntracellular_M = intra.GetSubstanceQuantity(*m_Calcium)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + + //Conductances of Na, K, and Cl were obtained by source below on a per cm^2 basis and converted to a per intracellular + //volume basis using the reported surface area to volume ratio. We do this because our intracellular compartments are macro + //entities (not on the cellular level) whose volumes, but not areas, are defined. + /// \ @cite lindblad 1996model + double sodiumConductance_S_Per_mL = 1.0 / (m_Sodium->GetMembraneResistance(ElectricResistanceUnit::Ohm)); + double potassiumConductance_S_Per_mL = 1.0 / (m_Potassium->GetMembraneResistance(ElectricResistanceUnit::Ohm)); + double chlorideConductance_S_Per_mL = 1.0 / (m_Chloride->GetMembraneResistance(ElectricResistanceUnit::Ohm)); + double calciumConductance_S_Per_mL = 1.0 / (m_Calcium->GetMembraneResistance(ElectricResistanceUnit::Ohm)); + + //Calculate current Nernst potentials for each ion (Faradays constant is multiplied by the charge of the ion--1 for Na and K, -1 for Cl) + double sodiumNernst_V = CalculateNernstPotential(extra, intra, m_Sodium); + double potassiumNernst_V = CalculateNernstPotential(extra, intra, m_Potassium); + double chlorideNernst_V = CalculateNernstPotential(extra, intra, m_Chloride); + double calciumNernst_V = CalculateNernstPotential(extra, intra, m_Calcium); + + //Calculate diffusion fluxes of each ion through channels. S/mL*V /(C/mol) = (A/V)/mL*V/(C/mol) = (C/s)/mL/(C/mol) = mol/mL-s + //Structured so that positive diffusion is out of cell and negative is into cell + double sodiumDiffusion_mol_Per_mL_s = sodiumConductance_S_Per_mL*(membranePotential_V - sodiumNernst_V) / (faradaysConstant_C_Per_mol * 1); + double potassiumDiffusion_mol_Per_mL_s = potassiumConductance_S_Per_mL*(membranePotential_V - potassiumNernst_V) / (faradaysConstant_C_Per_mol * 1); + double chlorideDiffusion_mol_Per_mL_s = chlorideConductance_S_Per_mL*(membranePotential_V - chlorideNernst_V) / (faradaysConstant_C_Per_mol*-1); + double calciumDiffusion_mol_Per_mL_s = calciumConductance_S_Per_mL*(membranePotential_V - calciumNernst_V) / (faradaysConstant_C_Per_mol * 2); + + //Calculate currents generated by pumps. Then convert to moles for each ion using pump stoichiometry and faraday's constant + //One cycle of NaK pump moves 3 Na+ out of cell and 2 K+ into cell--> +1 current out of cell + //One cycle of NaKCl cotransport moves 1 Na+, 1 K+, 2Cl- into cell--> 0 current generated (calculate apparent Cl current and use to back calculate movement of Na and K) + double sodiumPotassiumPumpCurrent_A_per_mL = SodiumPotassiumPump(sodiumIntracellular_M, potassiumExtracellular_M, membranePotential_V); + double chloridePumpCurrent_A_Per_mL = SodiumPotassiumChlorideCotransport(sodiumIntracellular_M, sodiumExtracellular_M, potassiumIntracellular_M, potassiumExtracellular_M, chlorideIntracellular_M, chlorideExtracellular_M); + double calciumPumpCurrent_A_Per_mL = CalciumPump(calciumIntracellular_M); + + double sodiumPumped_mol_Per_mL_s = 3 * sodiumPotassiumPumpCurrent_A_per_mL / faradaysConstant_C_Per_mol; //Na moved out, so flux >0 + double sodiumCotransported_mol_Per_mL_s = -0.5*chloridePumpCurrent_A_Per_mL / faradaysConstant_C_Per_mol; //Cotransported in, so flux <0 + double potassiumPumped_mol_Per_mL_s = -2 * sodiumPotassiumPumpCurrent_A_per_mL / faradaysConstant_C_Per_mol; //K moved out, so flux <0 + double potassiumCotransported_mol_Per_mL_s = sodiumCotransported_mol_Per_mL_s; //Moves in 1:1 ratio w/ Na + double chlorideCotransported_mol_Per_mL_s = -chloridePumpCurrent_A_Per_mL / faradaysConstant_C_Per_mol; //technically we divide by -1 for charge of Cl + double calciumPumped_mol_Per_mL_s = calciumPumpCurrent_A_Per_mL / (2 * faradaysConstant_C_Per_mol); + + //Total fluxes are difference equations from the perspective of the intracellular space + double sodiumActiveIncrement_ug = -(sodiumDiffusion_mol_Per_mL_s + sodiumPumped_mol_Per_mL_s + sodiumCotransported_mol_Per_mL_s)*intracellularVolume_mL*timestep_s*(m_Sodium->GetMolarMass(MassPerAmountUnit::ug_Per_mol)); + double potassiumActiveIncrement_ug = -(potassiumDiffusion_mol_Per_mL_s + potassiumPumped_mol_Per_mL_s + potassiumCotransported_mol_Per_mL_s)*intracellularVolume_mL*timestep_s*(m_Potassium->GetMolarMass(MassPerAmountUnit::ug_Per_mol)); + double chlorideActiveIncrement_ug = -(chlorideDiffusion_mol_Per_mL_s + chlorideCotransported_mol_Per_mL_s)*intracellularVolume_mL*timestep_s*(m_Chloride->GetMolarMass(MassPerAmountUnit::ug_Per_mol)); + double calciumActiveIncrement_ug = calciumPumped_mol_Per_mL_s*intracellularVolume_mL*timestep_s; + + //We need to handle simple diffusion of ions between the vascular and extracellular space as well. This has to be done in this method for two reasons: + //1) These ions will be excluded from main diffusion loop and 2) We need to update the membrane potential in this method while we have access + //to the pump current, so we need to update the intra and extracellular concentrations in this method. + //As we loop over these ions, call instant diffusion between the vascular and extracellular spaces after active diffusion is taken care of + SESubstance* ion; + double incrementer_ug = 0.0; + //Use values below to calculate simple diffusion of each ion from vascular-->extracellular + double molecularRadius_nm = 0.0; + double permeability_mL_Per_s = 0.0; + double simpleDiffusion_ug = 0.0; + std::map ionIncrements = { {m_Sodium,sodiumActiveIncrement_ug},{m_Potassium,potassiumActiveIncrement_ug}, + {m_Chloride,chlorideActiveIncrement_ug},{ m_Calcium,calciumActiveIncrement_ug } }; + for (auto ions : ionIncrements) + { + ion = ions.first; + incrementer_ug = ions.second; + + if (incrementer_ug > 0) + { + //Net flux is direction extra-->intra + if (incrementer_ug > extra.GetSubstanceQuantity(*ion)->GetMass(MassUnit::ug)) + { + //Make sure we don't take it more than is there (shouldn't happen but you never know) + incrementer_ug = extra.GetSubstanceQuantity(*ion)->GetMass(MassUnit::ug); + } + DistributeMassbyMassWeighted(extra, *ion, -incrementer_ug, MassUnit::ug); + DistributeMassbyVolumeWeighted(intra, *ion, incrementer_ug, MassUnit::ug); + } + else + { + //Net flux is direction intra-->extra + if (-incrementer_ug > intra.GetSubstanceQuantity(*ion)->GetMass(MassUnit::ug)) + { + incrementer_ug = -intra.GetSubstanceQuantity(*ion)->GetMass(MassUnit::ug); + } + DistributeMassbyVolumeWeighted(extra, *ion, -incrementer_ug, MassUnit::ug); + DistributeMassbyMassWeighted(intra, *ion, incrementer_ug, MassUnit::ug); + } + //Vascular-extracellular diffusion + molecularRadius_nm = 0.0348*pow(ion->GetMolarMass(MassPerAmountUnit::g_Per_mol), 0.4175); + permeability_mL_Per_s = (0.0184*pow(molecularRadius_nm, -1.223) / 100.0)*tissue.GetTotalMass(MassUnit::g); + simpleDiffusion_ug = MoveMassBySimpleDiffusion(vascular, extra, *ion, permeability_mL_Per_s,timestep_s); + + //Balance ion after all mass moved + vascular.GetSubstanceQuantity(*ion)->Balance(BalanceLiquidBy::Mass); + extra.GetSubstanceQuantity(*ion)->Balance(BalanceLiquidBy::Mass); + intra.GetSubstanceQuantity(*ion)->Balance(BalanceLiquidBy::Mass); + } + + //Calculate new membrane potential (requires re-calculating NernstPotentials). Ca not used because it's conductivity is so low relative to other ions + sodiumNernst_V = CalculateNernstPotential(extra, intra, m_Sodium); + potassiumNernst_V = CalculateNernstPotential(extra, intra, m_Potassium); + chlorideNernst_V = CalculateNernstPotential(extra, intra, m_Chloride); + + membranePotential_V = (-sodiumPotassiumPumpCurrent_A_per_mL + sodiumConductance_S_Per_mL*sodiumNernst_V + potassiumConductance_S_Per_mL*potassiumNernst_V + + chlorideConductance_S_Per_mL*chlorideNernst_V) / (sodiumConductance_S_Per_mL + potassiumConductance_S_Per_mL + chlorideConductance_S_Per_mL); + + tissue.GetMembranePotential().SetValue(membranePotential_V, ElectricPotentialUnit::V); +} + +//Returns current generated by Na-K Pump +double Tissue::SodiumPotassiumPump(double intraNa_M, double extraK_M, double potential_V) +{ + double maxCurrent_A_Per_mL = 0.0118; + double potassiumMichaelis_M = 0.001; + double sodiumMichaelis_M = 0.011; + + double KTerm = extraK_M / (extraK_M + potassiumMichaelis_M); + double NaTerm = pow(intraNa_M, 1.5) / (pow(intraNa_M, 1.5) + pow(sodiumMichaelis_M, 1.5)); + double VTerm = (potential_V + 0.15) / (potential_V + 0.2); + + double pumpCurrent_A_per_mL = maxCurrent_A_Per_mL*KTerm*NaTerm*VTerm; + + return pumpCurrent_A_per_mL; +} + +double Tissue::SodiumPotassiumChlorideCotransport(double intraNa_M, double extraNa_M, double intraK_M, double extraK_M, double intraCl_M, double extraCl_M) +{ + double faradaysConstant_C_Per_mol = 96485; + double gasConstant_J_Per_mol = 8.314; + //double coreTemperature_degK = m_data.GetEnergy().GetCoreTemperature(TemperatureUnit::C) + 273.15; + double coreTemperature_degK = 310.0; + double maxCurrent_A_Per_mL = 5.25e-12; + + double numerator = extraNa_M * extraK_M *extraCl_M*extraCl_M; + double denominator = intraNa_M * intraK_M * intraCl_M*intraCl_M; + + double chlorideCurrent_A_Per_mL = maxCurrent_A_Per_mL*faradaysConstant_C_Per_mol*coreTemperature_degK*gasConstant_J_Per_mol*log(numerator / denominator); + + return chlorideCurrent_A_Per_mL; +} + +double Tissue::CalculateNernstPotential(SELiquidCompartment& extra, SELiquidCompartment& intra, SESubstance* ion) +{ + double gasConstant_J_Per_mol = 8.314; + //double coreTemperature_degK = m_data.GetEnergy().GetCoreTemperature(TemperatureUnit::C) + 273.15; + double coreTemperature_degK = 310.0; //Use this while unit testing + double faradaysConstant_C_Per_mol = 96485; + double intraIon_M = intra.GetSubstanceQuantity(*ion)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double extraIon_M = extra.GetSubstanceQuantity(*ion)->GetMolarity(AmountPerVolumeUnit::mol_Per_L); + double z = 1.0; + if (ion->GetName() == "Chloride") + z = -1.0; + if (ion->GetName() == "Calcium") + z = 2.0; + + double nernst_V = (gasConstant_J_Per_mol*coreTemperature_degK) / (faradaysConstant_C_Per_mol * z)*log(extraIon_M / intraIon_M); + return nernst_V; +} + +double Tissue::CalciumPump(double intraCa_M) +{ + double maxCurrent_A_Per_mL = 0.0033; + double calciumMichaelis_M = 2.75e-7; + + double calciumCurrent_A_Per_mL = maxCurrent_A_Per_mL*intraCa_M / (intraCa_M + calciumMichaelis_M); + + return calciumCurrent_A_Per_mL; +} \ No newline at end of file diff --git a/src/engine/cpp/Systems/Tissue.h b/src/engine/cpp/Systems/Tissue.h index 0ef998a..a249455 100644 --- a/src/engine/cpp/Systems/Tissue.h +++ b/src/engine/cpp/Systems/Tissue.h @@ -52,7 +52,7 @@ class BIOGEARS_API Tissue : public SETissueSystem, public BioGearsSystem void Process(); void PostProcess(); - //Used to add Hepatic O2/CO2 changes to Tissue outputs (there's probably a better way to transfer this data) + // Used to add Hepatic O2/CO2 changes to Tissue outputs (there's probably a better way to transfer this data) static double m_hepaticO2Consumed_mol; static double m_hepaticCO2Produced_mol; @@ -62,20 +62,24 @@ class BIOGEARS_API Tissue : public SETissueSystem, public BioGearsSystem void ProteinStorageAndRelease(); void FatStorageAndRelease(); - /*Tissue System*/ - void CalculateMetabolicConsumptionAndProduction(double time); - - /*Process Methods*/ + // Process Methods void CalculateDiffusion(); void CalculatePulmonaryCapillarySubstanceTransfer(); void CalculateVitals(); void CheckGlycogenLevels(); void ManageSubstancesAndSaturation(); + // Postprocess Methods + - /*Postprocess Methods*/ + //conditions + void Dehydrate(); - /*Diffusion Utilities*/ + // Tissue System + void CalculateMetabolicConsumptionAndProduction(double time); + void SetStarvationState(); + + // Diffusion Utilities void DistributeMassbyVolumeWeighted(SELiquidCompartment& cmpt, const SESubstance& sub, double mass, const MassUnit& unit); void DistributeMassbyMassWeighted(SELiquidCompartment& cmpt, const SESubstance& sub, double mass, const MassUnit& unit); @@ -86,18 +90,24 @@ class BIOGEARS_API Tissue : public SETissueSystem, public BioGearsSystem double MoveMassBySimpleDiffusion(SELiquidCompartment& source, SELiquidCompartment& target, const SESubstance& sub, double permeabilityCofficient_mL_Per_s, double timestep_s); double MoveMassByFacilitatedDiffusion(SELiquidCompartment& source, SELiquidCompartment& target, const SESubstance& sub, double combinedCoefficient_g_Per_s, double timestep_s); double MoveMassByActiveTransport(SELiquidCompartment& source, SELiquidCompartment& target, const SESubstance& sub, double pumpRate_g_Per_s, double timestep_s); + void MoveIonsByActiveTransport(SETissueCompartment& tissue, SELiquidCompartment& vascular, SELiquidCompartment& extra, SELiquidCompartment& intra, double timestep_s); + double SodiumPotassiumPump(double intracellularSodium, double extracellularPotassium, double potential_V); + double SodiumPotassiumChlorideCotransport(double intraNa_M, double extraNa_M, double intraK_M, double extraK_M, double intraCl_M, double extraCl_M); + double CalciumPump(double intraCa_M); + double CalculateNernstPotential(SELiquidCompartment& extra, SELiquidCompartment& intra, SESubstance* ion); + // Serializable member variables (Set in Initialize and in schema) - double m_RestingTissueGlucose_g; - double m_RestingBloodGlucose_g_Per_L; - double m_RestingBloodLipid_g_Per_L; - double m_RestingBloodInsulin_g_Per_L; double m_RestingPatientMass_kg; double m_RestingFluidMass_kg; + RunningAverage m_O2ConsumedRunningAverage_mL_Per_s; + RunningAverage m_CO2ProducedRunningAverage_mL_Per_s; + RunningAverage m_RespiratoryQuotientRunningAverage; // Stateless member variable (Set in SetUp()) double m_Dt_s; double m_maxProteinStorage_g; + double m_lastFatigueTime; std::stringstream m_ss; SESubstance* m_Albumin; @@ -112,7 +122,9 @@ class BIOGEARS_API Tissue : public SETissueSystem, public BioGearsSystem SESubstance* m_Ketones; SESubstance* m_Creatinine; SESubstance* m_Sodium; + SESubstance* m_Potassium; SESubstance* m_Calcium; + SESubstance* m_Chloride; SESubstance* m_Insulin; SESubstance* m_Urea; diff --git a/src/engine/test/cpp/BioGearsEngineTest.cpp b/src/engine/test/cpp/BioGearsEngineTest.cpp index 2716f19..8decaf1 100644 --- a/src/engine/test/cpp/BioGearsEngineTest.cpp +++ b/src/engine/test/cpp/BioGearsEngineTest.cpp @@ -121,6 +121,7 @@ void BioGearsEngineTest::FillFunctionMap() bgeMap.insert(std::make_pair("SimpleDiffusionFourCompartmentTest", &BioGearsEngineTest::SimpleDiffusionFourCompartmentTest)); bgeMap.insert(std::make_pair("SimpleDiffusionHierarchyTest", &BioGearsEngineTest::SimpleDiffusionHierarchyTest)); bgeMap.insert(std::make_pair("FacilitatedDiffusionTest", &BioGearsEngineTest::FacilitatedDiffusionTest)); + bgeMap.insert(std::make_pair("ActiveIonDiffusionTest", &BioGearsEngineTest::ActiveIonTransportTest)); bgeMap.insert(std::make_pair("BrainInjuryTest", &BioGearsEngineTest::BrainInjuryTest)); diff --git a/src/engine/test/cpp/BioGearsEngineTest.h b/src/engine/test/cpp/BioGearsEngineTest.h index 9101d0c..0d5d9e7 100644 --- a/src/engine/test/cpp/BioGearsEngineTest.h +++ b/src/engine/test/cpp/BioGearsEngineTest.h @@ -169,11 +169,12 @@ class TEST_DECL BioGearsEngineTest : Loggable void SimpleDiffusionFourCompartmentTest(const std::string& rptDirectory); void SimpleDiffusionHierarchyTest(const std::string& rptDirectory); void FacilitatedDiffusionTest(const std::string& rptDirectory); + void ActiveIonTransportTest(const std::string& rptDirectory); protected: void DistributeMass(SETestSuite& testSuite); void PerfusionLimitedDiffusionTest(SETestSuite& testSuite); void InstantDiffusionTest(SETestSuite& testSuite); - void ActiveTransportTest(SETestSuite& testSuite); + void GenericClearanceTest(SETestSuite& testSuite); void GenericExcretionTest(SETestSuite& testSuite); @@ -190,14 +191,16 @@ class TEST_DECL BioGearsEngineTest : Loggable void FourCompartmentTestSimple(const std::string& sOutputDirectory); void AcidBaseFourCompartmentTest(const std::string& sOutputDirectory); void FiveCompartmentTestWithDiffusion(const std::string& sOutputDirectory); + void FiveCompartmentTestWithActiveDiffusion(const std::string& sOutputDirectory); void AcidBaseFourCompartmentTestWithProductionConsumption(const std::string& sOutputDirectory); void AcidBaseFiveCompartmentTestWithDiffusion(const std::string& sOutputDirectory); void AcidBaseFiveCompartmentTestWithProductionConsumptionAndDiffusion(const std::string& sOutputDirectory); protected: - void FourCompartmentTest(bool usingAcidBase, bool usingProductionConsumption, bool usingDiffusion, const std::string& sOutputDirectory); + void FourCompartmentTest(bool usingAcidBase, bool usingProductionConsumption, bool usingDiffusion, bool activeDiffusion, const std::string& sOutputDirectory); bool usingAcidBase; bool usingProductionConsumption; bool usingDiffusion; + bool activeDiffusion; //Flag to determine whether we are testing active diffusion of ionic species (needs to be used in conjunction with usingDiffusion) ///////////////////// // Acid Base Tests // diff --git a/src/engine/test/cpp/Circuits/TissueTests.cpp b/src/engine/test/cpp/Circuits/TissueTests.cpp index c5fc08b..7e5186e 100644 --- a/src/engine/test/cpp/Circuits/TissueTests.cpp +++ b/src/engine/test/cpp/Circuits/TissueTests.cpp @@ -49,7 +49,6 @@ void BioGearsEngineTest::DistributeMass(SETestSuite& testSuite) // / \ / \ // L2C0 L2C1 L2C2 L2C3 <-- Only these cmpts have data - bg.GetSubstances().LoadSubstanceDirectory(); SELiquidCompartment& L0C0 = bg.GetCompartments().CreateLiquidCompartment("L0C0"); SELiquidCompartment& L1C0 = bg.GetCompartments().CreateLiquidCompartment("L1C0"); @@ -226,7 +225,6 @@ void BioGearsEngineTest::PerfusionLimitedDiffusionTest(SETestSuite& testSuite) Tissue& tsu = (Tissue&)bg.GetTissue(); TimingProfile timer; double timestep_s = 1. / 90.; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* sub = bg.GetSubstances().GetSubstance("Ketamine"); double bFlow_mL_Per_s = 2.0; double PartitionCoeff = 1.52201; @@ -288,7 +286,6 @@ void BioGearsEngineTest::AlveolarOxygenDiffusionTest(const std::string& rptDirec Tissue& tsu = (Tissue&)bg.GetTissue(); double timestep_s = 1.0 / 90; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* O2 = bg.GetSubstances().GetSubstance("Oxygen"); SESubstance* N2 = bg.GetSubstances().GetSubstance("Nitrogen"); bg.GetSubstances().AddActiveSubstance(*O2); @@ -357,7 +354,6 @@ void BioGearsEngineTest::AlveolarCarbonDioxideDiffusionTest(const std::string& r std::string rptFile = rptDirectory + "\\AlveolarCarbonDioxideDiffusionTest.txt"; double timestep_s = 1.0 / 90; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* CO2 = bg.GetSubstances().GetSubstance("CarbonDioxide"); SESubstance* N2 = bg.GetSubstances().GetSubstance("Nitrogen"); @@ -424,7 +420,6 @@ void BioGearsEngineTest::InstantPlusSimpleDiffusionTest(const std::string& rptDi BioGears bg(m_Logger); Tissue& tsu = (Tissue&)bg.GetTissue(); double timestep_s = 1.0 / 90; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* o2 = bg.GetSubstances().GetSubstance("Oxygen"); bg.GetSubstances().AddActiveSubstance(*o2); SELiquidCompartment& cmpt1 = bg.GetCompartments().CreateLiquidCompartment("cmpt1"); @@ -490,7 +485,6 @@ void BioGearsEngineTest::InstantDiffusionTest(SETestSuite& testSuite) BioGears bg(testSuite.GetLogger()); Tissue& tsu = (Tissue&)bg.GetTissue(); double timestep_s = 1.0 / 90; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* o2 = bg.GetSubstances().GetSubstance("Oxygen"); bg.GetSubstances().AddActiveSubstance(*o2); SELiquidCompartment& cmpt1 = bg.GetCompartments().CreateLiquidCompartment("cmpt1"); @@ -535,7 +529,6 @@ void BioGearsEngineTest::SimpleDiffusionTwoCompartmentTest(const std::string& rp BioGears bg(m_Logger); Tissue& tsu = (Tissue&)bg.GetTissue(); double timestep_s = 1.0 / 90; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* o2 = bg.GetSubstances().GetSubstance("Oxygen"); bg.GetSubstances().AddActiveSubstance(*o2); SETissueCompartment& tissue = bg.GetCompartments().CreateTissueCompartment("Tissue"); @@ -617,7 +610,6 @@ void BioGearsEngineTest::SimpleDiffusionFourCompartmentTest(const std::string& r BioGears bg(m_Logger); Tissue& tsu = (Tissue&)bg.GetTissue(); double timestep_s = 1.0 / 90; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* o2 = bg.GetSubstances().GetSubstance("Oxygen"); bg.GetSubstances().AddActiveSubstance(*o2); SETissueCompartment& tissue = bg.GetCompartments().CreateTissueCompartment("Tissue"); @@ -715,7 +707,6 @@ void BioGearsEngineTest::SimpleDiffusionHierarchyTest(const std::string& rptDire BioGears bg(m_Logger); Tissue& tsu = (Tissue&)bg.GetTissue(); double timestep_s = 1.0 / 90; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* sub = bg.GetSubstances().GetSubstance("Desflurane"); bg.GetSubstances().AddActiveSubstance(*sub); @@ -844,7 +835,6 @@ void BioGearsEngineTest::FacilitatedDiffusionTest(const std::string& rptDirector BioGears bg(m_Logger); Tissue& tsu = (Tissue&)bg.GetTissue(); double timestep_s = 1.0 / 90; - bg.GetSubstances().LoadSubstanceDirectory(); SESubstance* sub = bg.GetSubstances().GetSubstance("Glucose"); bg.GetSubstances().AddActiveSubstance(*sub); SETissueCompartment& tissue = bg.GetCompartments().CreateTissueCompartment("Tissue"); @@ -886,40 +876,262 @@ void BioGearsEngineTest::FacilitatedDiffusionTest(const std::string& rptDirector trk1.WriteTrackToFile(rptFile.c_str()); } -void BioGearsEngineTest::ActiveTransportTest(SETestSuite& testSuite) -{ - TimingProfile timer; - BioGears bg(testSuite.GetLogger()); +void BioGearsEngineTest::ActiveIonTransportTest(const std::string& rptDirectory) +{ +//----Constants for each test------------------------------------------- + BioGears bg(m_Logger); Tissue& tsu = (Tissue&)bg.GetTissue(); - double timestep_s = 1. / 90.; - bg.GetSubstances().LoadSubstanceDirectory(); - SESubstance* sub = bg.GetSubstances().GetSubstance("Desflurane"); - bg.GetSubstances().AddActiveSubstance(*sub); - SETissueCompartment& tissue = bg.GetCompartments().CreateTissueCompartment("Tissue"); - SELiquidCompartment& extracellular = bg.GetCompartments().CreateLiquidCompartment("Extracellular"); - SELiquidCompartment& intracellular = bg.GetCompartments().CreateLiquidCompartment("Intracellular"); + double timestep_s = 0.02; - // First test case - pump some substance from one cmpt to another to another - SETestCase& testCase1 = testSuite.CreateTestCase(); - testCase1.SetName("ActivePumpTest"); - timer.Start("Test"); + //Note: Do not call "Load Substance Directory". This is already called in the BioGears constructor. + bg.GetCompartments().StateChange(); - double ecVol_mL = 20.0; - double icVol_mL = 50.0; - double vMass_g = 1.5; - double ecMass_g = 1.5; - double icMass_g = 2.0; - extracellular.GetVolume().SetValue(ecVol_mL, VolumeUnit::mL); - intracellular.GetVolume().SetValue(icVol_mL, VolumeUnit::mL); - extracellular.GetSubstanceQuantity(*sub)->GetMass().SetValue(icMass_g, MassUnit::g); - intracellular.GetSubstanceQuantity(*sub)->GetMass().SetValue(ecMass_g, MassUnit::g); - extracellular.Balance(BalanceLiquidBy::Mass); - intracellular.Balance(BalanceLiquidBy::Mass); + SESubstance* Na = &bg.GetSubstances().GetSodium(); + SESubstance* K = &bg.GetSubstances().GetPotassium(); + SESubstance* Cl = &bg.GetSubstances().GetChloride(); + SESubstance* Ca = &bg.GetSubstances().GetCalcium(); - testCase1.GetDuration().SetValue(timer.GetElapsedTime_s("Test"), TimeUnit::s); + bg.GetSubstances().AddActiveSubstance(*Na); + bg.GetSubstances().AddActiveSubstance(*K); + bg.GetSubstances().AddActiveSubstance(*Cl); + bg.GetSubstances().AddActiveSubstance(*Ca); + + //Make a compartment to mimic adipose that has high extra:intra volume ratio + SETissueCompartment& adiposeTis = bg.GetCompartments().CreateTissueCompartment("Adipose"); + SELiquidCompartment& adiposeEC = bg.GetCompartments().CreateLiquidCompartment("AdiposeExtra"); + SELiquidCompartment& adiposeIC = bg.GetCompartments().CreateLiquidCompartment("AdiposeIntra"); + SELiquidCompartment& adiposeVas = bg.GetCompartments().CreateLiquidCompartment("AdiposeVascular"); + //Make a compartment to mimic liver that has low extra:intra volume ratio + SETissueCompartment& liverTis = bg.GetCompartments().CreateTissueCompartment("Liver"); + SELiquidCompartment& liverEC = bg.GetCompartments().CreateLiquidCompartment("LiverExtra"); + SELiquidCompartment& liverIC = bg.GetCompartments().CreateLiquidCompartment("LiverIntra"); + SELiquidCompartment& liverVas = bg.GetCompartments().CreateLiquidCompartment("LiverVascular"); + + + //Substance Molar Masses + double NaMM = Na->GetMolarMass(MassPerAmountUnit::g_Per_mol); + double KMM = K->GetMolarMass(MassPerAmountUnit::g_Per_mol); + double ClMM = Cl->GetMolarMass(MassPerAmountUnit::g_Per_mol); + double CaMM = Ca->GetMolarMass(MassPerAmountUnit::g_Per_mol); + + + + DataTrack tracker; + + double adiposeECVol_L = 2.13; + double adiposeICVol_L = 0.27; + double adiposeMass_kg = 0.25; + double liverECVol_L = 0.3; + double liverICVol_L = 1.03; + double liverMass_kg = 1.0; + + //Set tissue volumes and masses + adiposeTis.GetTotalMass().SetValue(adiposeMass_kg, MassUnit::kg); + adiposeEC.GetVolume().SetValue(adiposeECVol_L, VolumeUnit::L); + adiposeIC.GetVolume().SetValue(adiposeICVol_L, VolumeUnit::L); + adiposeVas.GetVolume().SetValue(5.0*0.106, VolumeUnit::L); //based on 5L blood volume and vascular fraction from BioGears.cpp + liverTis.GetTotalMass().SetValue(liverMass_kg, MassUnit::kg); + liverEC.GetVolume().SetValue(liverECVol_L, VolumeUnit::L); + liverIC.GetVolume().SetValue(liverICVol_L, VolumeUnit::L); + liverVas.GetVolume().SetValue(5.0*0.05, VolumeUnit::L); //based on 5L blood volume and vascular fraction from BioGears.cpp + + //Set substance concentrations + adiposeEC.GetSubstanceQuantity(*Na)->GetMass().SetValue(0.145*adiposeECVol_L*NaMM, MassUnit::g); + adiposeEC.GetSubstanceQuantity(*K)->GetMass().SetValue(0.0045*adiposeECVol_L*KMM, MassUnit::g); + adiposeEC.GetSubstanceQuantity(*Cl)->GetMass().SetValue(0.116*adiposeECVol_L*ClMM, MassUnit::g); + adiposeEC.GetSubstanceQuantity(*Ca)->GetMass().SetValue(0.0012*adiposeECVol_L*CaMM, MassUnit::g); + adiposeIC.GetSubstanceQuantity(*Na)->GetMass().SetValue(0.015*adiposeICVol_L*NaMM, MassUnit::g); + adiposeIC.GetSubstanceQuantity(*K)->GetMass().SetValue(0.120*adiposeICVol_L*KMM, MassUnit::g); + adiposeIC.GetSubstanceQuantity(*Cl)->GetMass().SetValue(0.02*adiposeICVol_L*ClMM, MassUnit::g); + adiposeIC.GetSubstanceQuantity(*Ca)->GetMass().SetValue(1e-7*adiposeICVol_L*CaMM, MassUnit::g); + adiposeVas.GetSubstanceQuantity(*Na)->GetMass().SetValue(0.142*5.0*.106*NaMM, MassUnit::g); + adiposeVas.GetSubstanceQuantity(*K)->GetMass().SetValue(0.0044*5.0*.106*KMM, MassUnit::g); + adiposeVas.GetSubstanceQuantity(*Cl)->GetMass().SetValue(0.110*5.0*.106*ClMM, MassUnit::g); + adiposeVas.GetSubstanceQuantity(*Ca)->GetMass().SetValue(0.0012*5.0*.106*CaMM, MassUnit::g); + + + liverEC.GetSubstanceQuantity(*Na)->GetMass().SetValue(0.145*liverECVol_L*NaMM, MassUnit::g); + liverEC.GetSubstanceQuantity(*K)->GetMass().SetValue(0.0045*liverECVol_L*KMM, MassUnit::g); + liverEC.GetSubstanceQuantity(*Cl)->GetMass().SetValue(0.116*liverECVol_L*ClMM, MassUnit::g); + liverEC.GetSubstanceQuantity(*Ca)->GetMass().SetValue(0.0012*liverECVol_L*CaMM, MassUnit::g); + liverIC.GetSubstanceQuantity(*Na)->GetMass().SetValue(0.015*liverICVol_L*NaMM, MassUnit::g); + liverIC.GetSubstanceQuantity(*K)->GetMass().SetValue(0.120*liverICVol_L*KMM, MassUnit::g); + liverIC.GetSubstanceQuantity(*Cl)->GetMass().SetValue(0.02*liverICVol_L*ClMM, MassUnit::g); + liverIC.GetSubstanceQuantity(*Ca)->GetMass().SetValue(1e-7*liverICVol_L*CaMM, MassUnit::g); + liverVas.GetSubstanceQuantity(*Na)->GetMass().SetValue(0.142*5.0*0.05*NaMM, MassUnit::g); + liverVas.GetSubstanceQuantity(*K)->GetMass().SetValue(0.0044*5.0*0.05*KMM, MassUnit::g); + liverVas.GetSubstanceQuantity(*Cl)->GetMass().SetValue(0.102*5.0*0.05*ClMM, MassUnit::g); + liverVas.GetSubstanceQuantity(*Ca)->GetMass().SetValue(0.0012*5.0*.05*CaMM, MassUnit::g); + + //Balance everything out + adiposeEC.Balance(BalanceLiquidBy::Mass); + adiposeIC.Balance(BalanceLiquidBy::Mass); + adiposeVas.Balance(BalanceLiquidBy::Mass); + liverEC.Balance(BalanceLiquidBy::Mass); + liverIC.Balance(BalanceLiquidBy::Mass); + liverVas.Balance(BalanceLiquidBy::Mass); + + //Initialize membrane potential + + adiposeTis.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); + liverTis.GetMembranePotential().SetValue(-85.0, ElectricPotentialUnit::mV); + + //Set up variables to track + double adiposePotential_mV = adiposeTis.GetMembranePotential(ElectricPotentialUnit::mV); + double sodiumAdiposeIC_M = adiposeIC.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double sodiumAdiposeEC_M = adiposeEC.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double sodiumAdiposeVas_M = adiposeVas.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double potassiumAdiposeIC_M = adiposeIC.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double potassiumAdiposeEC_M = adiposeEC.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double potassiumAdiposeVas_M = adiposeVas.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double chlorideAdiposeIC_M = adiposeIC.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double chlorideAdiposeEC_M = adiposeEC.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double chlorideAdiposeVas_M = adiposeVas.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double calciumAdiposeIC_M = adiposeIC.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double calciumAdiposeEC_M = adiposeEC.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double calciumAdiposeVas_M = adiposeVas.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + + double liverPotential_mV = liverTis.GetMembranePotential(ElectricPotentialUnit::mV); + double sodiumLiverIC_M = liverIC.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double sodiumLiverEC_M = liverEC.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double sodiumLiverVas_M = liverVas.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double potassiumLiverIC_M = liverIC.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double potassiumLiverEC_M = liverEC.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double potassiumLiverVas_M = liverVas.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double chlorideLiverIC_M = liverIC.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double chlorideLiverEC_M = liverEC.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double chlorideLiverVas_M = liverVas.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double calciumLiverIC_M = liverIC.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double calciumLiverEC_M = liverEC.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + double calciumLiverVas_M = liverVas.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + + //Initialize tracking + std::string rptFile = rptDirectory + "\\ActiveIonTransportTest.txt"; + double time = 0.0; + //tracker.Track("Adipose_Na_EC", time, sodiumAdiposeEC_M); + //tracker.Track("Adipose_Na_IC", time, sodiumAdiposeIC_M); + //tracker.Track("Adipose_Na_Vas", time, sodiumAdiposeVas_M); + //tracker.Track("Adipose_K_EC", time, potassiumAdiposeEC_M); + //tracker.Track("Adipose_K_IC", time, potassiumAdiposeIC_M); + //tracker.Track("Adipose_K_Vas", time, potassiumAdiposeVas_M); + //tracker.Track("Adipose_Cl_EC", time, chlorideAdiposeEC_M); + //tracker.Track("Adipose_Cl_IC", time, chlorideAdiposeIC_M); + //tracker.Track("Adipose_Cl_Vas", time, chlorideAdiposeVas_M); + //tracker.Track("Adipose_Ca_EC", time, calciumAdiposeEC_M); + //tracker.Track("Adipose_Ca_IC", time, calciumAdiposeIC_M); + //tracker.Track("Adipose_Ca_Vas", time, calciumAdiposeVas_M); + //tracker.Track("Adipose_Potential", time, adiposePotential_mV); + + + tracker.Track("Liver_Na_EC", time, sodiumLiverEC_M); + tracker.Track("Liver_Na_IC", time, sodiumLiverIC_M); + tracker.Track("Liver_Na_Vas", time, sodiumLiverVas_M); + tracker.Track("Liver_K_EC", time, potassiumLiverEC_M); + tracker.Track("Liver_K_IC", time, potassiumLiverIC_M); + tracker.Track("Liver_K_Vas", time, potassiumLiverVas_M); + tracker.Track("Liver_Cl_EC", time, chlorideLiverEC_M); + tracker.Track("Liver_Cl_IC", time, chlorideLiverIC_M); + tracker.Track("Liver_Cl_Vas", time, chlorideLiverVas_M); + tracker.Track("Liver_Ca_EC", time, calciumLiverEC_M); + tracker.Track("Liver_Ca_IC", time, calciumLiverIC_M); + tracker.Track("Liver_Ca_Vas", time, calciumLiverVas_M); + tracker.Track("Liver_Potential", time, liverPotential_mV); + + + //------------Test 1: Maintain Steady State------------------------ + + + //------------Test 2: Sodium----------------------- + //Change vascular to 80 and 200 + + //-----------Test 3: Potassium------------------------------------ + //Change vascular to 1.5 and 10 + + //-----------Test 4: Chloride------------------------------------ + //Change vascular to 130 and 80 + + + for (int i = 0; i < 90000; i++) + { + time += timestep_s; + //if (i <=30000) + //{ + //// adiposeVas.GetSubstanceQuantity(*Na)->GetMolarity().SetValue(95, AmountPerVolumeUnit::mmol_Per_L); + //// adiposeVas.Balance(BalanceLiquidBy::Molarity); + //// adiposeEC.GetSubstanceQuantity(*Na)->GetMolarity().SetValue(95, AmountPerVolumeUnit::mmol_Per_L); + //// adiposeEC.Balance(BalanceLiquidBy::Molarity); + // liverVas.GetSubstanceQuantity(*Na)->GetMolarity().SetValue(200, AmountPerVolumeUnit::mmol_Per_L); + // liverVas.Balance(BalanceLiquidBy::Molarity); + // //liverEC.GetSubstanceQuantity(*Na)->GetMolarity().SetValue(200, AmountPerVolumeUnit::mmol_Per_L); + // //liverEC.Balance(BalanceLiquidBy::Molarity); + //} + + //tsu.MoveIonsByActiveTransport(adiposeTis, adiposeVas, adiposeEC, adiposeIC, timestep_s); + tsu.MoveIonsByActiveTransport(liverTis, liverVas, liverEC, liverIC, timestep_s); + + //adiposePotential_mV = adiposeTis.GetMembranePotential(ElectricPotentialUnit::mV); + //sodiumAdiposeIC_M = adiposeIC.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //sodiumAdiposeEC_M = adiposeEC.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //sodiumAdiposeVas_M = adiposeVas.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //potassiumAdiposeIC_M = adiposeIC.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //potassiumAdiposeEC_M = adiposeEC.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //potassiumAdiposeVas_M = adiposeVas.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //chlorideAdiposeIC_M = adiposeIC.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //chlorideAdiposeEC_M = adiposeEC.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //chlorideAdiposeVas_M = adiposeVas.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //calciumAdiposeIC_M = adiposeIC.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //calciumAdiposeEC_M = adiposeEC.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + //calciumAdiposeVas_M = adiposeVas.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + + + liverPotential_mV = liverTis.GetMembranePotential(ElectricPotentialUnit::mV); + sodiumLiverIC_M = liverIC.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + sodiumLiverEC_M = liverEC.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + sodiumLiverVas_M = liverVas.GetSubstanceQuantity(*Na)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + potassiumLiverIC_M = liverIC.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + potassiumLiverEC_M = liverEC.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + potassiumLiverVas_M = liverVas.GetSubstanceQuantity(*K)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + chlorideLiverIC_M = liverIC.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + chlorideLiverEC_M = liverEC.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + chlorideLiverVas_M = liverVas.GetSubstanceQuantity(*Cl)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + calciumLiverIC_M = liverIC.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + calciumLiverEC_M = liverEC.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + calciumLiverVas_M = liverVas.GetSubstanceQuantity(*Ca)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + + + //tracker.Track("Adipose_Na_EC", time, sodiumAdiposeEC_M); + //tracker.Track("Adipose_Na_IC", time, sodiumAdiposeIC_M); + //tracker.Track("Adipose_Na_Vas", time, sodiumAdiposeVas_M); + //tracker.Track("Adipose_K_EC", time, potassiumAdiposeEC_M); + //tracker.Track("Adipose_K_IC", time, potassiumAdiposeIC_M); + //tracker.Track("Adipose_K_Vas", time, potassiumAdiposeVas_M); + //tracker.Track("Adipose_Cl_EC", time, chlorideAdiposeEC_M); + //tracker.Track("Adipose_Cl_IC", time, chlorideAdiposeIC_M); + //tracker.Track("Adipose_Cl_Vas", time, chlorideAdiposeVas_M); + //tracker.Track("Adipose_Ca_EC", time, calciumAdiposeEC_M); + //tracker.Track("Adipose_Ca_IC", time, calciumAdiposeIC_M); + //tracker.Track("Adipose_Ca_Vas", time, calciumAdiposeVas_M); + //tracker.Track("Adipose_Potential", time, adiposePotential_mV); + + tracker.Track("Liver_Na_EC", time, sodiumLiverEC_M); + tracker.Track("Liver_Na_IC", time, sodiumLiverIC_M); + tracker.Track("Liver_Na_Vas", time, sodiumLiverVas_M); + tracker.Track("Liver_K_EC", time, potassiumLiverEC_M); + tracker.Track("Liver_K_IC", time, potassiumLiverIC_M); + tracker.Track("Liver_K_Vas", time, potassiumLiverVas_M); + tracker.Track("Liver_Cl_EC", time, chlorideLiverEC_M); + tracker.Track("Liver_Cl_IC", time, chlorideLiverIC_M); + tracker.Track("Liver_Cl_Vas", time, chlorideLiverVas_M); + tracker.Track("Liver_Ca_EC", time, calciumLiverEC_M); + tracker.Track("Liver_Ca_IC", time, calciumLiverIC_M); + tracker.Track("Liver_Ca_Vas", time, calciumLiverVas_M); + tracker.Track("Liver_Potential", time, liverPotential_mV); + + } + + tracker.WriteTrackToFile(rptFile.c_str()); - // Second test should check bounds. } void BioGearsEngineTest::GenericClearanceTest(SETestSuite& testSuite) @@ -998,4 +1210,4 @@ void BioGearsEngineTest::DiffusionClearanceExcretionTests(const std::string& rpt //GenericExcretionTest(ts8); //testReport.WriteFile(rptDirectory + "\\GasCompartmentTest.xml"); -} +} \ No newline at end of file diff --git a/src/engine/test/cpp/Engine/FourCompartmentTest.cpp b/src/engine/test/cpp/Engine/FourCompartmentTest.cpp index ae96507..11edf1d 100644 --- a/src/engine/test/cpp/Engine/FourCompartmentTest.cpp +++ b/src/engine/test/cpp/Engine/FourCompartmentTest.cpp @@ -51,34 +51,38 @@ double TotalHbMols(SELiquidCompartmentGraph& Graph, SESubstance& Hb, SESubstance return totalHb_g / Hb_g_Per_mol + totalHbO2_g / HbO2_g_Per_mol + totalHbCO2_g / HbCO2_g_Per_mol + totalHBO2CO2_g / HbO2CO2_g_Per_mol; } -void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProductionConsumption, bool usingDiffusion, const std::string& rptDirectory) +void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProductionConsumption, bool usingDiffusion, bool activeDiffusion, const std::string& rptDirectory) { DataTrack trk; std::string outputName; - if (!usingAcidBase && !usingProductionConsumption && !usingDiffusion) + if (!usingAcidBase && !usingProductionConsumption && !usingDiffusion && !activeDiffusion) { outputName = "\\FourCompartmentTestSimple"; } - else if (usingAcidBase && !usingProductionConsumption && !usingDiffusion) + else if (usingAcidBase && !usingProductionConsumption && !usingDiffusion && !activeDiffusion) { outputName = "\\AcidBaseFourCompartmentTest"; } - else if (!usingAcidBase && !usingProductionConsumption && usingDiffusion) + else if (!usingAcidBase && !usingProductionConsumption && usingDiffusion && !activeDiffusion) { outputName = "\\FiveCompartmentTestWithDiffusion"; } - else if (usingAcidBase && usingProductionConsumption && !usingDiffusion) + else if (usingAcidBase && usingProductionConsumption && !usingDiffusion && !activeDiffusion) { outputName = "\\AcidBaseFourCompartmentTestWithProductionConsumption"; } - else if (usingAcidBase && !usingProductionConsumption && usingDiffusion) + else if (usingAcidBase && !usingProductionConsumption && usingDiffusion && !activeDiffusion) { outputName = "\\AcidBaseFiveCompartmentTestWithDiffusion"; } - else if (usingAcidBase && usingProductionConsumption && usingDiffusion) + else if (usingAcidBase && usingProductionConsumption && usingDiffusion && !activeDiffusion) { outputName = "\\AcidBaseFiveCompartmentTestWithProductionConsumptionAndDiffusion"; } + else if (!usingAcidBase && !usingProductionConsumption && usingDiffusion && activeDiffusion) + { + outputName = "\\FiveCompartmentTestWithActiveDiffusion"; + } m_Logger->ResetLogFile(rptDirectory + outputName + ".log"); std::ofstream file; SELiquidTransporter txpt(VolumePerTimeUnit::mL_Per_s, VolumeUnit::mL, MassUnit::ug, MassPerVolumeUnit::ug_Per_mL, m_Logger); @@ -152,7 +156,11 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu SELiquidCompartment& cCapillaries = bg.GetCompartments().CreateLiquidCompartment("Capillaries"); cCapillaries.MapNode(nCapillaries); - SELiquidCompartment& cTissue = bg.GetCompartments().CreateLiquidCompartment("Tissue"); + + SELiquidCompartment& cTissue = bg.GetCompartments().CreateLiquidCompartment("Tissue"); //for simple diffusion + + SELiquidCompartment& tisExtra = bg.GetCompartments().CreateLiquidCompartment("TissueExtracellular"); //for active diffusion with nernst potentials + SELiquidCompartment& tisIntra = bg.GetCompartments().CreateLiquidCompartment("TissueIntracellular"); //for active diffusion with nernst potentials //Create the links SELiquidCompartmentLink& lPulmonaryToArteries = bg.GetCompartments().CreateLiquidLink(cPulmonary, cArteries, "PulmonaryToArteries"); @@ -177,7 +185,7 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu Graph.StateChange(); - //Initialize substances + //Initialize blood gas substances SESubstance& O2 = bg.GetSubstances().GetO2(); SESubstance& Hb = bg.GetSubstances().GetHb(); SESubstance& HbO2 = bg.GetSubstances().GetHbO2(); @@ -187,6 +195,13 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu SESubstance& HbCO2 = bg.GetSubstances().GetHbCO2(); SESubstance& N2 = bg.GetSubstances().GetN2(); + //Intialize ions for active transport test + SESubstance& Sodium = bg.GetSubstances().GetSodium(); + SESubstance& Potassium = bg.GetSubstances().GetPotassium(); + SESubstance& Chloride = bg.GetSubstances().GetChloride(); + SESubstance& Calcium = bg.GetSubstances().GetCalcium(); + std::vector ions = { &Sodium,&Potassium,&Chloride,&Calcium }; + bg.GetSubstances().AddActiveSubstance(O2); bg.GetSubstances().AddActiveSubstance(Hb); bg.GetSubstances().AddActiveSubstance(HbO2); @@ -195,7 +210,14 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu bg.GetSubstances().AddActiveSubstance(HCO3); bg.GetSubstances().AddActiveSubstance(HbCO2); bg.GetSubstances().AddActiveSubstance(N2); + bg.GetSubstances().AddActiveSubstance(Sodium); + bg.GetSubstances().AddActiveSubstance(Potassium); + bg.GetSubstances().AddActiveSubstance(Chloride); + bg.GetSubstances().AddActiveSubstance(Calcium); + + + //Other substances SEScalarMassPerVolume albuminConcentration; SEScalarFraction hematocrit; SEScalarTemperature bodyTemp; @@ -243,22 +265,55 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu bg.GetSaturationCalculator().CalculateBloodGasDistribution(cVeins); } - //Tissue diffusion values + //Tissue diffusion values for simple diffusion test double tissueVolume_L = 61.7; //sum of all tissue volumes double tissueO2Mass_g = tissueVolume_L * cCapillaries.GetSubstanceQuantity(O2)->GetConcentration(MassPerVolumeUnit::g_Per_L); double tissueCO2Mass_g = tissueVolume_L * cCapillaries.GetSubstanceQuantity(CO2)->GetConcentration(MassPerVolumeUnit::g_Per_L); + + //Tissue volume and Ion concentration setpoints for active diffusion test + tisIntra.GetVolume().SetValue(tissueVolume_L*0.7, VolumeUnit::L); //Use ~2.5:1 ratio of intra to extracellular tissue volume + tisExtra.GetVolume().SetValue(tissueVolume_L*0.3, VolumeUnit::L); + Sodium.GetBloodConcentration().SetValue(145.0*Sodium.GetMolarMass(MassPerAmountUnit::g_Per_mmol), MassPerVolumeUnit::g_Per_L); + Sodium.GetTissueConcentration().SetValue(15.0*Sodium.GetMolarMass(MassPerAmountUnit::g_Per_mmol), MassPerVolumeUnit::g_Per_L); + Potassium.GetBloodConcentration().SetValue(4.4*Potassium.GetMolarMass(MassPerAmountUnit::g_Per_mmol), MassPerVolumeUnit::g_Per_L); + Potassium.GetTissueConcentration().SetValue(120*Potassium.GetMolarMass(MassPerAmountUnit::g_Per_mmol), MassPerVolumeUnit::g_Per_L); + Calcium.GetBloodConcentration().SetValue(1.2*Calcium.GetMolarMass(MassPerAmountUnit::g_Per_mmol), MassPerVolumeUnit::g_Per_L); + Calcium.GetTissueConcentration().SetValue(0.0001*Calcium.GetMolarMass(MassPerAmountUnit::g_Per_mmol), MassPerVolumeUnit::g_Per_L); + Chloride.GetBloodConcentration().SetValue(110*Chloride.GetMolarMass(MassPerAmountUnit::g_Per_mmol), MassPerVolumeUnit::g_Per_L); + Chloride.GetTissueConcentration().SetValue(20.0*Chloride.GetMolarMass(MassPerAmountUnit::g_Per_mmol), MassPerVolumeUnit::g_Per_L); if (usingDiffusion) { - //cTissue.GetTotalVolume().SetValue(tissueVolume_L, VolumeUnit::L); - cTissue.GetVolume().SetValue(tissueVolume_L, VolumeUnit::L); - cTissue.GetSubstanceQuantity(O2)->GetMass().SetValue(tissueO2Mass_g, MassUnit::g); - cTissue.GetSubstanceQuantity(Hb)->GetMass().SetValue(0, MassUnit::g); - cTissue.GetSubstanceQuantity(HbO2)->GetMass().SetValue(0, MassUnit::g); - cTissue.GetSubstanceQuantity(HbO2CO2)->GetMass().SetValue(0, MassUnit::g); - cTissue.GetSubstanceQuantity(CO2)->GetMass().SetValue(tissueCO2Mass_g, MassUnit::g); - cTissue.GetSubstanceQuantity(HCO3)->GetMass().SetValue(0, MassUnit::g); - cTissue.GetSubstanceQuantity(HbCO2)->GetMass().SetValue(0, MassUnit::g); - cTissue.Balance(BalanceLiquidBy::Mass); + if (!activeDiffusion) + { + cTissue.GetVolume().SetValue(tissueVolume_L, VolumeUnit::L); + cTissue.GetSubstanceQuantity(O2)->GetMass().SetValue(tissueO2Mass_g, MassUnit::g); + cTissue.GetSubstanceQuantity(Hb)->GetMass().SetValue(0, MassUnit::g); + cTissue.GetSubstanceQuantity(HbO2)->GetMass().SetValue(0, MassUnit::g); + cTissue.GetSubstanceQuantity(HbO2CO2)->GetMass().SetValue(0, MassUnit::g); + cTissue.GetSubstanceQuantity(CO2)->GetMass().SetValue(tissueCO2Mass_g, MassUnit::g); + cTissue.GetSubstanceQuantity(HCO3)->GetMass().SetValue(0, MassUnit::g); + cTissue.GetSubstanceQuantity(HbCO2)->GetMass().SetValue(0, MassUnit::g); + cTissue.Balance(BalanceLiquidBy::Mass); + } + else + { + for (auto ion : ions) + { + //Vascular and extracellular compartments have approximately equal concentrations of respective ions + cPulmonary.GetSubstanceQuantity(*ion)->GetConcentration().SetValue(ion->GetBloodConcentration(MassPerVolumeUnit::g_Per_L), MassPerVolumeUnit::g_Per_L); + cArteries.GetSubstanceQuantity(*ion)->GetConcentration().SetValue(ion->GetBloodConcentration(MassPerVolumeUnit::g_Per_L), MassPerVolumeUnit::g_Per_L); + cCapillaries.GetSubstanceQuantity(*ion)->GetConcentration().SetValue(ion->GetBloodConcentration(MassPerVolumeUnit::g_Per_L), MassPerVolumeUnit::g_Per_L); + cVeins.GetSubstanceQuantity(*ion)->GetConcentration().SetValue(ion->GetBloodConcentration(MassPerVolumeUnit::g_Per_L), MassPerVolumeUnit::g_Per_L); + tisExtra.GetSubstanceQuantity(*ion)->GetConcentration().SetValue(ion->GetBloodConcentration(MassPerVolumeUnit::g_Per_L), MassPerVolumeUnit::g_Per_L); + tisIntra.GetSubstanceQuantity(*ion)->GetConcentration().SetValue(ion->GetTissueConcentration(MassPerVolumeUnit::g_Per_L), MassPerVolumeUnit::g_Per_L); + } + cPulmonary.Balance(BalanceLiquidBy::Concentration); + cArteries.Balance(BalanceLiquidBy::Concentration); + cCapillaries.Balance(BalanceLiquidBy::Concentration); + cVeins.Balance(BalanceLiquidBy::Concentration); + tisExtra.Balance(BalanceLiquidBy::Concentration); + tisIntra.Balance(BalanceLiquidBy::Concentration); + } } @@ -277,36 +332,44 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu //PreProcess - Mimic Produce, Consume, and Alveolar Transfer if (usingProductionConsumption) { - if (usingDiffusion) - { - //Do it in the tissues and let it diffuse - cPulmonary.GetSubstanceQuantity(O2)->GetMass().IncrementValue(O2Consumption_ugPerS * deltaT_s, MassUnit::ug); - cPulmonary.GetSubstanceQuantity(CO2)->GetMass().IncrementValue(-CO2Production_ugPerS * deltaT_s, MassUnit::ug); - cTissue.GetSubstanceQuantity(O2)->GetMass().IncrementValue(-O2Consumption_ugPerS * deltaT_s, MassUnit::ug); - cTissue.GetSubstanceQuantity(CO2)->GetMass().IncrementValue(CO2Production_ugPerS * deltaT_s, MassUnit::ug); - - //See if we took too much out - double pulmonaryCO2Mass_ug = cPulmonary.GetSubstanceQuantity(CO2)->GetMass(MassUnit::ug); - double tissueO2Mass_ug = cCapillaries.GetSubstanceQuantity(O2)->GetMass(MassUnit::ug); - if (pulmonaryCO2Mass_ug < 0.0) + if (usingDiffusion) + { + if (!activeDiffusion) { - ss << "Not Enough Pulmonary CO2 at time " << i*deltaT_s; - cPulmonary.GetSubstanceQuantity(CO2)->GetMass().SetValue(0.0, MassUnit::ug); - Info(ss.str()); - ss.str(""); - ss.clear(); + //Do it in the tissues and let it diffuse + cPulmonary.GetSubstanceQuantity(O2)->GetMass().IncrementValue(O2Consumption_ugPerS * deltaT_s, MassUnit::ug); + cPulmonary.GetSubstanceQuantity(CO2)->GetMass().IncrementValue(-CO2Production_ugPerS * deltaT_s, MassUnit::ug); + cTissue.GetSubstanceQuantity(O2)->GetMass().IncrementValue(-O2Consumption_ugPerS * deltaT_s, MassUnit::ug); + cTissue.GetSubstanceQuantity(CO2)->GetMass().IncrementValue(CO2Production_ugPerS * deltaT_s, MassUnit::ug); + + //See if we took too much out + double pulmonaryCO2Mass_ug = cPulmonary.GetSubstanceQuantity(CO2)->GetMass(MassUnit::ug); + double tissueO2Mass_ug = cCapillaries.GetSubstanceQuantity(O2)->GetMass(MassUnit::ug); + if (pulmonaryCO2Mass_ug < 0.0) + { + ss << "Not Enough Pulmonary CO2 at time " << i*deltaT_s; + cPulmonary.GetSubstanceQuantity(CO2)->GetMass().SetValue(0.0, MassUnit::ug); + Info(ss.str()); + ss.str(""); + ss.clear(); + } + if (tissueO2Mass_ug < 0.0) + { + ss << "Not Enough Tissue O2 at time " << i*deltaT_s; + cTissue.GetSubstanceQuantity(O2)->GetMass().SetValue(0.0, MassUnit::ug); + Info(ss.str()); + ss.str(""); + ss.clear(); + } + + cPulmonary.Balance(BalanceLiquidBy::Mass); + cTissue.Balance(BalanceLiquidBy::Mass); } - if (tissueO2Mass_ug < 0.0) + else { - ss << "Not Enough Tissue O2 at time " << i*deltaT_s; - cTissue.GetSubstanceQuantity(O2)->GetMass().SetValue(0.0, MassUnit::ug); - Info(ss.str()); - ss.str(""); - ss.clear(); + //Stub in the event anyone wants to do a test with production consumption and active ion diffusion } - - cPulmonary.Balance(BalanceLiquidBy::Mass); - cTissue.Balance(BalanceLiquidBy::Mass); + } else { @@ -357,10 +420,35 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu //Diffuse if necessary if (usingDiffusion) { - tsu.MoveMassByInstantDiffusion(cCapillaries, cTissue, O2, deltaT_s); - tsu.MoveMassByInstantDiffusion(cCapillaries, cTissue, CO2, deltaT_s); + if (!activeDiffusion) + { + tsu.MoveMassByInstantDiffusion(cCapillaries, cTissue, O2, deltaT_s); + tsu.MoveMassByInstantDiffusion(cCapillaries, cTissue, CO2, deltaT_s); + cCapillaries.Balance(BalanceLiquidBy::Mass); + cTissue.Balance(BalanceLiquidBy::Mass); + + } + else + { + //Ions: Vascular to Extracellular + tsu.MoveMassByInstantDiffusion(cCapillaries, tisExtra, Sodium, deltaT_s); + tsu.MoveMassByInstantDiffusion(cCapillaries, tisExtra, Potassium, deltaT_s); + tsu.MoveMassByInstantDiffusion(cCapillaries, tisExtra, Calcium, deltaT_s); + tsu.MoveMassByInstantDiffusion(cCapillaries, tisExtra, Chloride, deltaT_s); + + //Ions: Extracellular to Intracellular: Note that this function no longer exists. It was replaced by MoveIonsByActiveTransport + /*tsu.MoveMassByNernstPotential(tisExtra, tisIntra, Sodium, deltaT_s); + tsu.MoveMassByNernstPotential(tisExtra, tisIntra, Potassium, deltaT_s); + tsu.MoveMassByNernstPotential(tisExtra, tisIntra, Calcium, deltaT_s); + tsu.MoveMassByNernstPotential(tisExtra, tisIntra, Chloride, deltaT_s);*/ + + cCapillaries.Balance(BalanceLiquidBy::Mass); + tisExtra.Balance(BalanceLiquidBy::Mass); + tisIntra.Balance(BalanceLiquidBy::Mass); + } } + //convert 'Next' values to current calc.PostProcess(*Circuit); @@ -377,6 +465,11 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu totalMass += cmpt->GetSubstanceQuantity(*sub)->GetMass(MassUnit::g); name = cmpt->GetName(); } + if (usingDiffusion && activeDiffusion) + { + totalMass += tisExtra.GetSubstanceQuantity(*sub)->GetMass(MassUnit::g); + totalMass += tisIntra.GetSubstanceQuantity(*sub)->GetMass(MassUnit::g); + } if(sub->GetName()=="Hemoglobin") trk.Track("Unbound" + sub->GetName() + "_TotalMass_" + "g", time, totalMass); else trk.Track(sub->GetName() + "_TotalMass_" + "g", time, totalMass); } @@ -385,7 +478,7 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu double Hb_Mols = TotalHbMols(Graph, Hb, HbO2, HbO2CO2, HbCO2); trk.Track("HbTotal_mols", time, Hb_Mols); - //Total molarity + //Total molarity of blood gases for (SELiquidCompartment* cmpt : Graph.GetCompartments()) { SELiquidSubstanceQuantity* sqO2 = cmpt->GetSubstanceQuantity(O2); @@ -398,26 +491,40 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu trk.Track(cmpt->GetName() + "_TotalOxygenMolarConcentration_mmol_per_L", time, sqO2->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) + sqHbO2->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) + sqHbO2CO2->GetMolarity(AmountPerVolumeUnit::mmol_Per_L)); trk.Track(cmpt->GetName() + "_TotalCarbonDioxideMolarConcentration_mmol_per_L", time, sqCO2->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) + sqHCO3->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) + sqHbCO2->GetMolarity(AmountPerVolumeUnit::mmol_Per_L) + sqHbO2CO2->GetMolarity(AmountPerVolumeUnit::mmol_Per_L)); } + - //Tissue and total oxygen w/ diffusion if (usingDiffusion) { - cTissue.Balance(BalanceLiquidBy::Mass); - cCapillaries.Balance(BalanceLiquidBy::Mass); - SELiquidSubstanceQuantity* sqO2Tissue = cTissue.GetSubstanceQuantity(O2); - trk.Track("Tissue_OxygenMass_ug", time, sqO2Tissue->GetMass(MassUnit::ug)); - trk.Track("Tissue_OxygenConcentration_ug_per_mL", time, sqO2Tissue->GetMass(MassUnit::ug)/cTissue.GetVolume(VolumeUnit::mL)); - - double totalMassO2 = 0; + if (!activeDiffusion) //Simple diffusion test tracks oxygen species + { + cTissue.Balance(BalanceLiquidBy::Mass); + cCapillaries.Balance(BalanceLiquidBy::Mass); + SELiquidSubstanceQuantity* sqO2Tissue = cTissue.GetSubstanceQuantity(O2); + trk.Track("Tissue_OxygenMass_ug", time, sqO2Tissue->GetMass(MassUnit::ug)); + trk.Track("Tissue_OxygenConcentration_ug_per_mL", time, sqO2Tissue->GetMass(MassUnit::ug) / cTissue.GetVolume(VolumeUnit::mL)); - for (SELiquidCompartment* cmpt : Graph.GetCompartments()) - { - totalMassO2 += cmpt->GetSubstanceQuantity(O2)->GetMass(MassUnit::ug); - } + double totalMassO2 = 0; - totalMassO2 += sqO2Tissue->GetMass(MassUnit::ug); - trk.Track("xTotalOxygenMass_ug", time, totalMassO2); + for (SELiquidCompartment* cmpt : Graph.GetCompartments()) + { + totalMassO2 += cmpt->GetSubstanceQuantity(O2)->GetMass(MassUnit::ug); + } + totalMassO2 += sqO2Tissue->GetMass(MassUnit::ug); + trk.Track("xTotalOxygenMass_ug", time, totalMassO2); + } + else //Active diffusion test tracks ions + { + double intraConcentration_mM; + double extraConcentration_mM; + for (auto ion : ions) + { + intraConcentration_mM = tisIntra.GetSubstanceQuantity(*ion)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + extraConcentration_mM = tisExtra.GetSubstanceQuantity(*ion)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L); + trk.Track(ion->GetName() + "_IntracellularConcentration_mmol_Per_L", time, intraConcentration_mM); + trk.Track(ion->GetName() + "_ExtracellularConcentration_mmol_Per_L", time, extraConcentration_mM); + } + } } if (i == 0) @@ -433,25 +540,29 @@ void BioGearsEngineTest::FourCompartmentTest(bool usingAcidBase, bool usingProdu void BioGearsEngineTest::FourCompartmentTestSimple(const std::string& sOutputDirectory) { - FourCompartmentTest(false, false, false, sOutputDirectory); + FourCompartmentTest(false, false, false, false, sOutputDirectory); } void BioGearsEngineTest::AcidBaseFourCompartmentTest(const std::string& sOutputDirectory) { - FourCompartmentTest(true, false, false, sOutputDirectory); + FourCompartmentTest(true, false, false, false, sOutputDirectory); } void BioGearsEngineTest::FiveCompartmentTestWithDiffusion(const std::string& sOutputDirectory) { - FourCompartmentTest(false, false, true, sOutputDirectory); + FourCompartmentTest(false, false, true, false, sOutputDirectory); } void BioGearsEngineTest::AcidBaseFourCompartmentTestWithProductionConsumption(const std::string& sOutputDirectory) { - FourCompartmentTest(true, true, false, sOutputDirectory); + FourCompartmentTest(true, true, false, false, sOutputDirectory); } void BioGearsEngineTest::AcidBaseFiveCompartmentTestWithDiffusion(const std::string& sOutputDirectory) { - FourCompartmentTest(true, false, true, sOutputDirectory); + FourCompartmentTest(true, false, true, false, sOutputDirectory); } void BioGearsEngineTest::AcidBaseFiveCompartmentTestWithProductionConsumptionAndDiffusion(const std::string& sOutputDirectory) { - FourCompartmentTest(true, true, true, sOutputDirectory); + FourCompartmentTest(true, true, true, false, sOutputDirectory); +} +void BioGearsEngineTest::FiveCompartmentTestWithActiveDiffusion(const std::string& sOutputDirectory) +{ + FourCompartmentTest(false, false, true, true, sOutputDirectory); } \ No newline at end of file diff --git a/src/engine/test/cpp/Engine/NutrientKineticsTest.cpp b/src/engine/test/cpp/Engine/NutrientKineticsTest.cpp index 304a6e8..8eafdb0 100644 --- a/src/engine/test/cpp/Engine/NutrientKineticsTest.cpp +++ b/src/engine/test/cpp/Engine/NutrientKineticsTest.cpp @@ -486,7 +486,7 @@ void BioGearsEngineTest::NutrientKineticsTest(bool usingAbsorption, bool usingDy //Nutrient storage handling if (usingGlycogen) { - hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cLiverVascular->GetSubstanceQuantity(insulin), cLiverVascular->GetSubstanceQuantity(glucagon)); + hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cLiverVascular->GetSubstanceQuantity(insulin), cLiverVascular->GetSubstanceQuantity(glucagon), bg); if(i % trackSkipper == 0) trk.Track("LiverHormoneFactor", time, hormoneFactor); //double insulinDeviation = (cLiverVascular.GetSubstanceQuantity(insulin)->GetMolarity(AmountPerVolumeUnit::mmol_Per_L)*1e9 - insulinBaseline_pmol_Per_L) / insulinBaseline_pmol_Per_L; @@ -546,7 +546,7 @@ void BioGearsEngineTest::NutrientKineticsTest(bool usingAbsorption, bool usingDy } //Now check the hormone factor in the muscle for storage of muscle glycogen - hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cMuscleVascular->GetSubstanceQuantity(insulin), cMuscleVascular->GetSubstanceQuantity(glucagon)); + hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cMuscleVascular->GetSubstanceQuantity(insulin), cMuscleVascular->GetSubstanceQuantity(glucagon), bg); //remove excess glucose from blood and store in muscle glycogen while there's room if ((hormoneFactor > 0) && (muscleGlycogen_g < maxMuscleGlycogen_g)) @@ -568,7 +568,7 @@ void BioGearsEngineTest::NutrientKineticsTest(bool usingAbsorption, bool usingDy if (usingProteinStorage) { - hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cMuscleVascular->GetSubstanceQuantity(insulin), cMuscleVascular->GetSubstanceQuantity(glucagon)); + hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cMuscleVascular->GetSubstanceQuantity(insulin), cMuscleVascular->GetSubstanceQuantity(glucagon), bg); if (i % trackSkipper == 0) trk.Track("MuscleHormoneFactor", time, hormoneFactor); @@ -627,7 +627,7 @@ void BioGearsEngineTest::NutrientKineticsTest(bool usingAbsorption, bool usingDy if (usingFatStorage) { - hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cFatVascular->GetSubstanceQuantity(insulin), cFatVascular->GetSubstanceQuantity(glucagon)); + hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cFatVascular->GetSubstanceQuantity(insulin), cFatVascular->GetSubstanceQuantity(glucagon), bg); if (i % trackSkipper == 0) trk.Track("FatHormoneFactor", time, hormoneFactor); @@ -694,7 +694,7 @@ void BioGearsEngineTest::NutrientKineticsTest(bool usingAbsorption, bool usingDy //Lipogenesis (liver making new TAG from AA and glucose in times of excess) if (usingLipogenesis) { - hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cLiverVascular->GetSubstanceQuantity(insulin), cLiverVascular->GetSubstanceQuantity(glucagon)); + hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cLiverVascular->GetSubstanceQuantity(insulin), cLiverVascular->GetSubstanceQuantity(glucagon), bg); //Get ratio of glucose to AA in liver to determine proportions that get converted to fat (will be a percentage of nutrients as glucose) //Really, what gets broken down to fat depends on concentrations of the facilitating enzymes @@ -792,7 +792,7 @@ void BioGearsEngineTest::NutrientKineticsTest(bool usingAbsorption, bool usingDy //Another note: These processes consume O2, so we will skip them under anaerobic conditions if (usingGluconeogenesis) { - hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cLiverVascular->GetSubstanceQuantity(insulin), cLiverVascular->GetSubstanceQuantity(glucagon)); + hormoneFactor = hptc.CalculateRelativeHormoneChange(insulinBaseline_pmol_Per_L, glucagonBaseline_pg_Per_mL, cLiverVascular->GetSubstanceQuantity(insulin), cLiverVascular->GetSubstanceQuantity(glucagon), bg); //Handle lactate conversion //TODO should this only be done when hormone factor is negative? This would allow for some lactate excretion and maybe make it so that the blood maintains the expected 2-23 mg/dL diff --git a/src/gui/.classpath b/src/gui/.classpath new file mode 100644 index 0000000..5ced9df --- /dev/null +++ b/src/gui/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/gui/.project b/src/gui/.project new file mode 100644 index 0000000..e639d38 --- /dev/null +++ b/src/gui/.project @@ -0,0 +1,17 @@ + + + BioGearsGUI + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/src/gui/build.xml b/src/gui/build.xml new file mode 100644 index 0000000..1111f7b --- /dev/null +++ b/src/gui/build.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/BioGearsGUI.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/BioGearsGUI.java new file mode 100644 index 0000000..c01ba48 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/BioGearsGUI.java @@ -0,0 +1,39 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui; + +import javax.swing.JFrame; +import javax.swing.JScrollPane; + +public class BioGearsGUI +{ + public static void main(String[] args) + { + java.awt.EventQueue.invokeLater(new Runnable() {public void run() {createAndShowUI();}}); + } + + public static void createAndShowUI() + { + // Main window + WindowUtilities.setNativeLookAndFeel(); + JFrame f = GUIContext.getAppFrame(); + f.addWindowListener(new ExitListener()); + f.setJMenuBar(new MainMenu()); + + f.add(new MainPanel()); + + f.pack(); + f.setVisible(true); + f.setResizable(true); + } +} \ No newline at end of file diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/ExitListener.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/ExitListener.java new file mode 100644 index 0000000..24a2f11 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/ExitListener.java @@ -0,0 +1,21 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui; + +import java.awt.event.*; + +public class ExitListener extends WindowAdapter { + public void windowClosing(WindowEvent event) { + System.exit(0); + } +} \ No newline at end of file diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/GUIContext.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/GUIContext.java new file mode 100644 index 0000000..7628b51 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/GUIContext.java @@ -0,0 +1,119 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +/** + * + */ +package mil.tatrc.physiology.biogears.gui; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JFrame; +import javax.swing.JSplitPane; +import javax.swing.UIManager; + +import mil.tatrc.physiology.biogears.gui.controls.ConsoleCtrl; +import mil.tatrc.physiology.biogears.gui.scenario.ScenarioEditor; + +/** + * @author abray + * I don't know if this is a good idea or not. + * Not sure the best way for ui components to talk to each other + * It works for now... + */ +public class GUIContext +{ + protected JFrame appFrame; + protected ConsoleCtrl console; + //protected JSplitPane treeVsCanvas; + protected JSplitPane logSplit; + protected ScenarioEditor scenarioEditor; + + protected static int INITIAL_DIVIDER_LOCATION; + + protected static final String NAME = "BioGears Scenario Driver"; + + protected GUIContext() + { + Font labelFont = UIManager.getFont("Label.font"); + //Font textareaFont = UIManager.getFont("TextArea.font"); + //tweak: + UIManager.put("TextArea.font", labelFont); + + appFrame=new JFrame(NAME); + console=new ConsoleCtrl(); + logSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + scenarioEditor = new ScenarioEditor(); + + INITIAL_DIVIDER_LOCATION = logSplit.getDividerLocation(); + } + protected static GUIContext me=null; + + public static void postFixWindowName(String name) + { + if(me==null) + me=new GUIContext(); + me.appFrame.setTitle(NAME+" - "+name); + } + + public static JFrame getAppFrame() + { + if(me==null) + me=new GUIContext(); + return me.appFrame; + } + + public static ConsoleCtrl getConsole() + { + if(me==null) + me=new GUIContext(); + return me.console; + } + + public static ScenarioEditor getScenarioEditor() + { + if(me==null) + me=new GUIContext(); + return me.scenarioEditor; + } +/* + public static JSplitPane getTreeVsCanvas() + { + if(me==null) + me=new GUIContext(); + return me.treeVsCanvas; + } +*/ + public static JSplitPane getLogSplit() + { + if(me==null) + me=new GUIContext(); + return me.logSplit; + } + + public static GridBagConstraints createGBC(int x,int y, double wx,double wy, int w,int h, int a, int f, Insets i) + { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = x; + gbc.gridy = y; + gbc.weightx = 1; + gbc.weighty = 1; + gbc.gridwidth = w; + gbc.gridheight = h; + gbc.anchor = a; + gbc.fill = f; + gbc.insets = i; + return gbc; + } + +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/MainMenu.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/MainMenu.java new file mode 100644 index 0000000..9f9b415 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/MainMenu.java @@ -0,0 +1,145 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +/** + * + */ +package mil.tatrc.physiology.biogears.gui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; + +/** + * @author abray + * http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html + */ +public class MainMenu extends JMenuBar implements ActionListener +{ + // File + private static String NEW = "New"; + private static String OPEN = "Open"; + private static String SAVE = "Save"; + private static String EXIT = "Exit"; + + // View + private static String LOG = "Log"; + + private JFileChooser fileChooser; + + public MainMenu() + { + fileChooser = new JFileChooser(); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setFileFilter(new FileNameExtensionFilter("XML File", "xml")); + + JMenuItem menuItem; + + // File Menu + JMenu menuFile = new JMenu("File"); + + menuItem = new JMenuItem(NEW); + menuItem.addActionListener(this); + menuFile.add(menuItem); + + menuItem = new JMenuItem(OPEN); + menuItem.addActionListener(this); + menuFile.add(menuItem); + + menuItem = new JMenuItem(SAVE); + menuItem.addActionListener(this); + menuFile.add(menuItem); + menuFile.addSeparator(); + + menuItem = new JMenuItem(EXIT); + menuItem.addActionListener(this); + menuFile.add(menuItem); + menuFile.addSeparator(); + + add(menuFile); + + // View Menu + JMenu menuView = new JMenu("View"); + menuItem = new JMenuItem(LOG); + menuItem.addActionListener(this); + menuView.add(menuItem); + + add(menuView); + } + + /** + * Action Performed when user clicks a menu item + */ + public void actionPerformed(ActionEvent ae) + { + String command = ae.getActionCommand(); + if (command.equals(NEW)) + { + GUIContext.getScenarioEditor().clear(); + } + else if (command.equals(OPEN)) + { + fileChooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) + { + File file = fileChooser.getSelectedFile().getAbsoluteFile(); + GUIContext.getConsole().println("Opening " + file.getName() + "..."); + if (GUIContext.getScenarioEditor().loadScenarioFile(file)) + { + GUIContext.getConsole().println("Done"); + GUIContext.postFixWindowName(file.getAbsolutePath()); + } + else + { + GUIContext.getConsole().println("ERROR: Problem opening " + file.getName()); + } + } + else + { + GUIContext.getConsole().println("Open command cancelled by user."); + } + } + else if (command.equals(SAVE)) + { + fileChooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) + { + File file = fileChooser.getSelectedFile(); + GUIContext.getConsole().println("Saving as " + file.getName() + "..."); + if (GUIContext.getScenarioEditor().saveScenarioFileAs(file.getAbsolutePath())) + { + GUIContext.getConsole().println("Done"); + // TODO Remove the '*' from the name, if present + } + } + else + { + GUIContext.getConsole().println("Save command cancelled by user."); + } + } + else if (command.equals(LOG)) + { + boolean b = GUIContext.getConsole().isVisible(); + GUIContext.getConsole().setVisible(!b); + if (!b) + GUIContext.getLogSplit().setDividerLocation(GUIContext.INITIAL_DIVIDER_LOCATION); + } + else if (command.equals(EXIT)) + { + //exit the program + System.exit(0); + } + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/MainPanel.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/MainPanel.java new file mode 100644 index 0000000..46a9c4d --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/MainPanel.java @@ -0,0 +1,41 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui; + +import java.awt.*; + +import javax.swing.*; + +import mil.tatrc.physiology.biogears.gui.controls.ConsoleListener; + +public class MainPanel extends JPanel +{ + protected ConsoleListener listner; + public MainPanel() + { + super(new BorderLayout()); + // Add ToolBar + //add(new ToolBar(), BorderLayout.PAGE_START); + // Split Pane + JSplitPane split = GUIContext.getLogSplit(); + split.setEnabled(false);//resizing is disabled, so this is too + split.setDividerLocation(510); + split.setTopComponent(GUIContext.getScenarioEditor()); + split.setBottomComponent(GUIContext.getConsole()); + listner = new ConsoleListener(GUIContext.getConsole()); + add(split); + setPreferredSize(new Dimension(680, 610)); + GUIContext.getConsole().setVisible(true); + GUIContext.getConsole().println("Java virtual machine architecture: " + System.getProperty("os.arch")); + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/ToolBar.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/ToolBar.java new file mode 100644 index 0000000..276a84d --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/ToolBar.java @@ -0,0 +1,51 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +/** + * + */ +package mil.tatrc.physiology.biogears.gui; + +import javax.swing.*; + +/** + * @author abray + * + */ +public class ToolBar extends JToolBar +{ + public ToolBar() + { + super("BioGears Tools"); + addSeparator(); + //this.addButtons(); + this.setFloatable(true); + this.setSize(128, 32); + } + + protected void addButtons() + { + /* + ToolBarAction human = new ToolBarAction("Human", new ImageIcon( + "images\\human.png"), "Human", 'H'); + add(human); + //addSeparator(); + ToolBarAction earth = new ToolBarAction("Earth", new ImageIcon( + "images\\earth.png"), "Earth", 'E'); + add(earth); + addSeparator(); + */ + } + + + +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/ToolBarAction.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/ToolBarAction.java new file mode 100644 index 0000000..1a52f81 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/ToolBarAction.java @@ -0,0 +1,42 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +/** + * + */ +package mil.tatrc.physiology.biogears.gui; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import javax.swing.*; + + + +/** + * @author cjackson + * + */ +public class ToolBarAction extends AbstractAction +{ + public ToolBarAction(String text, Icon icon, String description, char accelerator) + { + super(text, icon); + putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(accelerator, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + putValue(SHORT_DESCRIPTION, description); + } + + public void actionPerformed(ActionEvent arg0) + { + GUIContext.getConsole().println("Action Preformed: " + getValue(NAME)); + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/WindowUtilities.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/WindowUtilities.java new file mode 100644 index 0000000..3dcaf9e --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/WindowUtilities.java @@ -0,0 +1,108 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui; + +import javax.swing.*; + +import java.awt.*; + +/** A few utilities that simplify using windows in Swing. + * 1998-99 Marty Hall, http://www.apl.jhu.edu/~hall/java/ + */ + +public class WindowUtilities +{ + + /** Tell system to use native look and feel, as in previous + * releases. Metal (Java) LAF is the default otherwise. + */ + + public static void setNativeLookAndFeel() + { + try + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch(Exception e) + { + System.out.println("Error setting native LAF: " + e); + } + } + + public static void setJavaLookAndFeel() + { + try + { + UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + } + catch(Exception e) + { + System.out.println("Error setting Java LAF: " + e); + } + } + + public static void setMotifLookAndFeel() + { + try + { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + } + catch(Exception e) + { + System.out.println("Error setting Motif LAF: " + e); + } + } + + /** A simplified way to see a JPanel or other Container. + * Pops up a JFrame with specified Container as the content pane. + */ + + public static JFrame openInJFrame(Container content, + int width, + int height, + String title, + Color bgColor) + { + JFrame frame = new JFrame(title); + frame.setBackground(bgColor); + content.setBackground(bgColor); + frame.setSize(width, height); + frame.setContentPane(content); + frame.addWindowListener(new ExitListener()); + frame.setVisible(true); + return(frame); + } + + /** Uses Color.white as the background color. */ + + public static JFrame openInJFrame(Container content, + int width, + int height, + String title) + { + return(openInJFrame(content, width, height, title, Color.white)); + } + + /** Uses Color.white as the background color, and the + * name of the Container's class as the JFrame title. + */ + + public static JFrame openInJFrame(Container content, + int width, + int height) + { + return(openInJFrame(content, width, height, + content.getClass().getName(), + Color.white)); + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ConsoleCtrl.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ConsoleCtrl.java new file mode 100644 index 0000000..6ef249e --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ConsoleCtrl.java @@ -0,0 +1,49 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +/** + * + */ +package mil.tatrc.physiology.biogears.gui.controls; + +import java.awt.BorderLayout; +import java.awt.Color; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + + +/** + * @author abray + * + */ +public class ConsoleCtrl extends JPanel +{ + protected JTextArea tArea; + + public ConsoleCtrl() + { + super(new BorderLayout()); + tArea = new JTextArea(5,80); + tArea.setBackground(Color.white); + tArea.setEditable(false); + add(new JScrollPane(tArea)); + //consoleScroll.setBorder(BorderFactory.createCompoundBorder(raisedbevel,loweredbevel)); + } + + public void println(String msg) + { + tArea.append(msg+"\n"); + tArea.setCaretPosition(tArea.getDocument().getLength()); + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ConsoleListener.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ConsoleListener.java new file mode 100644 index 0000000..e7a7595 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ConsoleListener.java @@ -0,0 +1,52 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.controls; + +import mil.tatrc.physiology.utilities.LogListener; + +/** + * This class will pass any log messages from the engine to the gui console + * @author abray + */ +public class ConsoleListener extends LogListener +{ + protected ConsoleCtrl console; + + public ConsoleListener(ConsoleCtrl console) + { + if (console == null) + throw new RuntimeException("Null console provided"); + this.console=console; + } + + public void handleDebug(String msg) + { + this.console.println("DEBUG : " + msg); + } + public void handleInfo(String msg) + { + this.console.println("INFO : " + msg); + } + public void handleWarn(String msg) + { + this.console.println("WARN : " + msg); + } + public void handleError(String msg) + { + this.console.println("ERROR : " + msg); + } + public void handleFatal(String msg) + { + this.console.println("FATAL : " + msg); + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ScalarCtrl.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ScalarCtrl.java new file mode 100644 index 0000000..5fa561b --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/controls/ScalarCtrl.java @@ -0,0 +1,95 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ +package mil.tatrc.physiology.biogears.gui.controls; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import mil.tatrc.physiology.datamodel.properties.SEScalar; +import mil.tatrc.physiology.utilities.StringUtils; + +public class ScalarCtrl +{ + + public static class ScalarProperty + { + public String name; + public List units; + public Class scalarClass; + } + public static class ScalarPropertyComparator implements Comparator + { + public int compare(ScalarProperty from, ScalarProperty to) + { + return from.name.compareTo(to.name); + } + } + public static List getScalarProperties(Class c, boolean recursive) + { + List properties = new ArrayList(); + for (Method m : c.getMethods()) + { + if (m.getName().startsWith("get")) + { + if (SEScalar.class.isAssignableFrom(m.getReturnType())) + { + ScalarProperty ctrl = new ScalarProperty(); + ctrl.name = StringUtils.spaceCamelCase(m.getName().replaceAll("get", "")); + ctrl.scalarClass = m.getReturnType(); + ctrl.units=getUnitsList((Class)ctrl.scalarClass); + properties.add(ctrl); + } + else if (recursive && m.getReturnType().getSimpleName().startsWith("SE")) + { + List childProperties = getScalarProperties(m.getReturnType(), true); + if(m.getReturnType().getSimpleName().equals("SESubstance")) + continue; + if(m.getReturnType().getSimpleName().equals("SESubstanceFraction")) + continue; + if(m.getReturnType().getSimpleName().equals("SESubstanceCompartmentEffect")) + continue; + if(m.getReturnType().getSimpleName().equals("SENutrition")) + { + for(ScalarProperty p : childProperties) + p.name = "Stomach "+p.name; + } + properties.addAll(childProperties); + } + } + } + Collections.sort(properties, new ScalarPropertyComparator()); + return properties; + } + + public static List getUnitsList(Class c) + { + List unitList = new ArrayList(); + for(Method m : c.getMethods()) + { + if(m.getName().equals("getValue")&&m.getParameterTypes().length==1) + { + if(m.getParameterTypes()[0].isEnum()) + { + Enum[] enums = (Enum[]) m.getParameterTypes()[0].getEnumConstants(); + for(Enum e : enums) + unitList.add(e.toString()); + return unitList; + } + } + } + return null; + } + +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/CompartmentRequestSelection.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/CompartmentRequestSelection.java new file mode 100644 index 0000000..bf9da2e --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/CompartmentRequestSelection.java @@ -0,0 +1,468 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + **************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.datarequests; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.SwingConstants; + +import mil.tatrc.physiology.biogears.engine.BioGearsCompartments; +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl; +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl.ScalarProperty; +import mil.tatrc.physiology.datamodel.compartment.SEGasCompartment; +import mil.tatrc.physiology.datamodel.compartment.SELiquidCompartment; +import mil.tatrc.physiology.datamodel.compartment.SETissueCompartment; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SECompartmentDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEGasCompartmentDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SELiquidCompartmentDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SESubstanceDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEThermalCompartmentDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SETissueCompartmentDataRequest; +import mil.tatrc.physiology.datamodel.substance.*; +import mil.tatrc.physiology.datamodel.substance.quantity.SEGasSubstanceQuantity; +import mil.tatrc.physiology.datamodel.substance.quantity.SELiquidSubstanceQuantity; +import mil.tatrc.physiology.utilities.StringUtils; + +public class CompartmentRequestSelection extends DataRequestSelection +{ + protected SESubstanceManager subMgr; + protected JCheckBox subCheck; + // I am using categoryComboBox for the cmpt types (see BioGearsCompartments) + protected JComboBox activeCmptBox; + protected JComboBox cmptNameComboBox = new JComboBox(); + protected JComboBox cmptPropsBox = new JComboBox(); + protected JComboBox cmptSubPropsBox = new JComboBox(); + protected JComboBox subsBox = new JComboBox(); + + protected Map> category2cmptNameComboBox = new LinkedHashMap>(); + protected Map> category2propsComboBox = new LinkedHashMap>(); + protected Map> category2subQComboBox = new LinkedHashMap>(); + protected Map> category2enum = new LinkedHashMap>(); + + protected JComboBox gasPropsComboBox = new JComboBox(); + protected JComboBox gasSubQPropsComboBox = new JComboBox(); + protected JComboBox liquidPropsComboBox = new JComboBox(); + protected JComboBox liquidSubQPropsComboBox = new JComboBox(); + protected JComboBox tissuePropsComboBox = new JComboBox(); + protected JComboBox thermalPropsComboBox = new JComboBox(); + + JComboBox chymeCmptsComboBox = new JComboBox(); + JComboBox pulmonaryCmptsComboBox = new JComboBox(); + JComboBox tissueCmptsComboBox = new JComboBox(); + JComboBox extravascularCmptsComboBox = new JComboBox(); + JComboBox lymphCmptsComboBox = new JComboBox(); + JComboBox urineCmptsComboBox = new JComboBox(); + JComboBox vascularCmptsComboBox = new JComboBox(); + + JComboBox environmentCmptsComboBox = new JComboBox(); + JComboBox aerosolCmptsComboBox = new JComboBox(); + JComboBox anesthesiaMachineCmptsComboBox = new JComboBox(); + JComboBox inhalerCmptsComboBox = new JComboBox(); + + public CompartmentRequestSelection(ActionListener pnt, SESubstanceManager subMgr) + { + super(pnt, null); + this.subMgr = subMgr; + + for(ScalarProperty p : ScalarCtrl.getScalarProperties(SEGasCompartment.class, false)) + { + gasPropsComboBox.addItem(p.name); + if(!property2unitComboBox.containsKey(p.name)) + { + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + } + gasPropsComboBox.addActionListener(this); + + for(ScalarProperty p : ScalarCtrl.getScalarProperties(SEGasSubstanceQuantity.class, false)) + { + gasSubQPropsComboBox.addItem(p.name); + if(!property2unitComboBox.containsKey(p.name)) + { + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + } + gasSubQPropsComboBox.addActionListener(this); + + for(ScalarProperty p : ScalarCtrl.getScalarProperties(SELiquidCompartment.class, false)) + { + liquidPropsComboBox.addItem(p.name); + if(!property2unitComboBox.containsKey(p.name)) + { + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + } + liquidPropsComboBox.addActionListener(this); + + for(ScalarProperty p : ScalarCtrl.getScalarProperties(SELiquidSubstanceQuantity.class, false)) + { + liquidSubQPropsComboBox.addItem(p.name); + if(!property2unitComboBox.containsKey(p.name)) + { + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + } + liquidSubQPropsComboBox.addActionListener(this); + + for(ScalarProperty p : ScalarCtrl.getScalarProperties(SETissueCompartment.class, false)) + { + tissuePropsComboBox.addItem(p.name); + if(!property2unitComboBox.containsKey(p.name)) + { + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + } + tissuePropsComboBox.addActionListener(this); +/* + for(ScalarProperty p : ScalarCtrl.getScalarProperties(SEThermalCompartment.class, false)) + { + thermalPropsComboBox.addItem(p.name); + if(!property2unitComboBox.containsKey(p.name)) + { + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + } + thermalPropsComboBox.addActionListener(this); +*/ + categoryComboBox.addItem("Chyme"); + for(Enum e : BioGearsCompartments.Chyme.values()) + chymeCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Chyme", chymeCmptsComboBox); + category2propsComboBox.put("Chyme", liquidPropsComboBox); + category2subQComboBox.put("Chyme", liquidSubQPropsComboBox); + + categoryComboBox.addItem("Pulmonary"); + for(Enum e : BioGearsCompartments.Pulmonary.values()) + pulmonaryCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Pulmonary", pulmonaryCmptsComboBox); + category2propsComboBox.put("Pulmonary", gasPropsComboBox); + category2subQComboBox.put("Pulmonary", gasSubQPropsComboBox); + + categoryComboBox.addItem("Tissue"); + for(Enum e : BioGearsCompartments.Tissue.values()) + tissueCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Tissue", tissueCmptsComboBox); + category2propsComboBox.put("Tissue", tissuePropsComboBox); + // No Substances - category2subQComboBox + + categoryComboBox.addItem("Extravascular"); + for(Enum e : BioGearsCompartments.Extravascular.values()) + extravascularCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Extravascular", extravascularCmptsComboBox); + category2propsComboBox.put("Extravascular", liquidPropsComboBox); + category2subQComboBox.put("Extravascular", liquidSubQPropsComboBox); + + categoryComboBox.addItem("Lymph"); + for(Enum e : BioGearsCompartments.Lymph.values()) + lymphCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Lymph", lymphCmptsComboBox); + category2propsComboBox.put("Lymph", liquidPropsComboBox); + category2subQComboBox.put("Lymph", liquidSubQPropsComboBox); + + categoryComboBox.addItem("Urine"); + for(Enum e : BioGearsCompartments.Urine.values()) + urineCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Urine", urineCmptsComboBox); + category2propsComboBox.put("Urine", liquidPropsComboBox); + category2subQComboBox.put("Urine", liquidSubQPropsComboBox); + + categoryComboBox.addItem("Vascular"); + for(Enum e : BioGearsCompartments.Vascular.values()) + vascularCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Vascular", vascularCmptsComboBox); + category2propsComboBox.put("Vascular", liquidPropsComboBox); + category2subQComboBox.put("Vascular", liquidSubQPropsComboBox); + + categoryComboBox.addItem("Environment"); + for(Enum e : BioGearsCompartments.Environment.values()) + environmentCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Environment", environmentCmptsComboBox); + category2propsComboBox.put("Environment", gasPropsComboBox); + category2subQComboBox.put("Environment", gasSubQPropsComboBox); + + categoryComboBox.addItem("Aerosol"); + for(Enum e : BioGearsCompartments.Pulmonary.values()) + aerosolCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Aerosol", aerosolCmptsComboBox); + category2propsComboBox.put("Aerosol", liquidPropsComboBox); + category2subQComboBox.put("Aerosol", liquidSubQPropsComboBox); + + categoryComboBox.addItem("AnesthesiaMachine"); + for(Enum e : BioGearsCompartments.AnesthesiaMachine.values()) + anesthesiaMachineCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("AnesthesiaMachine", anesthesiaMachineCmptsComboBox); + category2propsComboBox.put("AnesthesiaMachine", gasPropsComboBox); + category2subQComboBox.put("AnesthesiaMachine", gasSubQPropsComboBox); + + categoryComboBox.addItem("Inhaler"); + for(Enum e : BioGearsCompartments.Inhaler.values()) + inhalerCmptsComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + category2cmptNameComboBox.put("Inhaler", inhalerCmptsComboBox); + category2propsComboBox.put("Inhaler", gasPropsComboBox); + category2subQComboBox.put("Inhaler", gasSubQPropsComboBox); + + categoryComboBox.addActionListener(this); + + for(SESubstance s : subMgr.getSubstances()) + subsBox.addItem(s.getName()); + subsBox.addActionListener(this); + + + subCheck = new JCheckBox("Substance"); + subCheck.setHorizontalAlignment(SwingConstants.CENTER); + subCheck.setVerticalAlignment(SwingConstants.TOP); + subCheck.setHorizontalTextPosition(SwingConstants.CENTER); + subCheck.setVerticalTextPosition(SwingConstants.BOTTOM); + subCheck.addActionListener(this); + + this.add(subCheck); + this.add(categoryComboBox); + activeCmptBox = category2cmptNameComboBox.get(categoryComboBox.getSelectedItem()); + this.add(activeCmptBox); + activePropBox = category2propsComboBox.get(categoryComboBox.getSelectedItem()); + this.add(activePropBox); + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + + public void actionPerformed(ActionEvent e) + { + if(e.getSource() == this.subCheck) + { + this.removeAll(); + this.add(subCheck); + this.add(categoryComboBox); + this.add(activeCmptBox); + if(this.subCheck.isSelected()) + { + this.add(subsBox); + activePropBox = category2subQComboBox.get(categoryComboBox.getSelectedItem()); + this.add(activePropBox); + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + else + { + activePropBox = category2propsComboBox.get(categoryComboBox.getSelectedItem()); + this.add(activePropBox); + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + } + else if(e.getSource() == this.categoryComboBox) + { + this.removeAll(); + if(category2subQComboBox.containsKey(categoryComboBox.getSelectedItem())) + { + this.add(subCheck); + this.subCheck.setSelected(false); + } + this.add(categoryComboBox); + activeCmptBox = category2cmptNameComboBox.get(categoryComboBox.getSelectedItem()); + this.add(activeCmptBox); + activePropBox = category2propsComboBox.get(categoryComboBox.getSelectedItem()); + this.add(activePropBox); + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + else if(e.getSource() == this.activePropBox) + { + this.remove(activeUnitBox); + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + this.revalidate(); + this.repaint(); + } + + public DataRequestRow createRow(SEDataRequest dr) + { + String label = "Unknown"; + String cmpt; + String substance = ""; + String property = StringUtils.spaceCamelCase(dr.getName()); + + if(dr instanceof SEGasCompartmentDataRequest) + { + SEGasCompartmentDataRequest gdr = (SEGasCompartmentDataRequest)dr; + cmpt = StringUtils.spaceCamelCase(gdr.getCompartment()); + if(gdr.hasSubstance()) + substance = gdr.getSubstance().getName() + " "; + + if(dr.hasUnit()) + label = cmpt +" "+substance+property+" ("+dr.getUnit()+")"; + else + label = cmpt +" "+substance+property+" ("+this.property2unitComboBox.get(property).getItemAt(0)+")"; + } + else if(dr instanceof SELiquidCompartmentDataRequest) + { + SELiquidCompartmentDataRequest ldr = (SELiquidCompartmentDataRequest)dr; + cmpt = StringUtils.spaceCamelCase(ldr.getCompartment()); + if(ldr.hasSubstance()) + substance = ldr.getSubstance().getName() + " "; + + if(dr.hasUnit()) + label = cmpt +" "+substance+property+" ("+dr.getUnit()+")"; + else + label = cmpt +" "+substance+property+" ("+this.property2unitComboBox.get(property).getItemAt(0)+")"; + } + else if(dr instanceof SETissueCompartmentDataRequest) + { + SETissueCompartmentDataRequest tdr = (SETissueCompartmentDataRequest)dr; + cmpt = StringUtils.spaceCamelCase(tdr.getCompartment()); + + if(dr.hasUnit()) + label = cmpt +" "+property+" ("+dr.getUnit()+")"; + else + label = cmpt +" "+property+" ("+this.property2unitComboBox.get(property).getItemAt(0)+")"; + } + else if(dr instanceof SEThermalCompartmentDataRequest) + { + SEThermalCompartmentDataRequest tdr = (SEThermalCompartmentDataRequest)dr; + cmpt = StringUtils.spaceCamelCase(tdr.getCompartment()); + + if(dr.hasUnit()) + label = cmpt +" "+property+" ("+dr.getUnit()+")"; + else + label = cmpt +" "+property+" ("+this.property2unitComboBox.get(property).getItemAt(0)+")"; + } + + DataRequestRow row = new DataRequestRow(this.pntListener, label, dr); + return row; + } + + public DataRequestRow createRow() + { + String label = ""; + String property = StringUtils.removeSpaces((String)this.activePropBox.getSelectedItem()); + String unit = (String)this.activeUnitBox.getSelectedItem(); + String cmpt = StringUtils.removeSpaces((String)this.activeCmptBox.getSelectedItem()); + if(this.activeCmptBox == chymeCmptsComboBox) + cmpt = BioGearsCompartments.Chyme.longValueOf(cmpt); + else if(this.activeCmptBox == pulmonaryCmptsComboBox) + cmpt = BioGearsCompartments.Pulmonary.longValueOf(cmpt); + else if(this.activeCmptBox == tissueCmptsComboBox) + cmpt = BioGearsCompartments.Tissue.longValueOf(cmpt); + else if(this.activeCmptBox == extravascularCmptsComboBox) + cmpt = BioGearsCompartments.Extravascular.longValueOf(cmpt); + else if(this.activeCmptBox == lymphCmptsComboBox) + cmpt = BioGearsCompartments.Lymph.longValueOf(cmpt); + else if(this.activeCmptBox == urineCmptsComboBox) + cmpt = BioGearsCompartments.Urine.longValueOf(cmpt); + else if(this.activeCmptBox == vascularCmptsComboBox) + cmpt = BioGearsCompartments.Vascular.longValueOf(cmpt); + else if(this.activeCmptBox == environmentCmptsComboBox) + cmpt = BioGearsCompartments.Environment.longValueOf(cmpt); + else if(this.activeCmptBox == aerosolCmptsComboBox) + cmpt = BioGearsCompartments.Pulmonary.longValueOf(cmpt); + else if(this.activeCmptBox == anesthesiaMachineCmptsComboBox) + cmpt = BioGearsCompartments.AnesthesiaMachine.longValueOf(cmpt); + else if(this.activeCmptBox == inhalerCmptsComboBox) + cmpt = BioGearsCompartments.Inhaler.longValueOf(cmpt); + + SEDataRequest dr = null; + if(activePropBox == gasPropsComboBox || activePropBox == gasSubQPropsComboBox) + { + SEGasCompartmentDataRequest gdr = new SEGasCompartmentDataRequest(); + gdr.setCompartment(cmpt); + String substance = ""; + if(this.subCheck.isSelected()) + { + substance = (String)this.subsBox.getSelectedItem()+" "; + gdr.setSubstance(subMgr.getSubstance(substance.trim())); + } + gdr.setName(StringUtils.removeSpaces(property)); + gdr.setUnit(unit); + dr = gdr; + label = StringUtils.spaceCamelCase(cmpt) +" "+substance+StringUtils.spaceCamelCase(property)+" ("+unit+")"; + } + else if(activePropBox == liquidPropsComboBox || activePropBox == liquidSubQPropsComboBox) + { + SELiquidCompartmentDataRequest ldr = new SELiquidCompartmentDataRequest(); + ldr.setCompartment(cmpt); + String substance = ""; + if(this.subCheck.isSelected()) + { + substance = (String)this.subsBox.getSelectedItem()+" "; + ldr.setSubstance(subMgr.getSubstance(substance.trim())); + } + ldr.setName(StringUtils.removeSpaces(property)); + ldr.setUnit(unit); + dr = ldr; + label = StringUtils.spaceCamelCase(cmpt) +" "+substance+StringUtils.spaceCamelCase(property)+" ("+unit+")"; + } + else if(activePropBox == tissuePropsComboBox) + { + SETissueCompartmentDataRequest tdr = new SETissueCompartmentDataRequest(); + tdr.setCompartment(cmpt); + tdr.setName(StringUtils.removeSpaces(property)); + tdr.setUnit(unit); + dr = tdr; + label = StringUtils.spaceCamelCase(cmpt) +" "+StringUtils.spaceCamelCase(property)+" ("+unit+")"; + } + else if(activePropBox == thermalPropsComboBox) + { + SEThermalCompartmentDataRequest tdr = new SEThermalCompartmentDataRequest(); + tdr.setCompartment(cmpt); + tdr.setName(StringUtils.removeSpaces(property)); + tdr.setUnit(unit); + dr = tdr; + label = StringUtils.spaceCamelCase(cmpt) +" "+StringUtils.spaceCamelCase(property)+" ("+unit+")"; + } + + DataRequestRow row = new DataRequestRow(this.pntListener, label, dr); + return row; + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestPanel.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestPanel.java new file mode 100644 index 0000000..3bc2c53 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestPanel.java @@ -0,0 +1,270 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + **************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.datarequests; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.LayoutManager; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl; +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl.ScalarProperty; +import mil.tatrc.physiology.datamodel.patient.SEPatient; +import mil.tatrc.physiology.datamodel.patient.nutrition.SENutrition; +import mil.tatrc.physiology.datamodel.scenario.SEScenario; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SECompartmentDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEEnvironmentDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEEquipmentDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEPatientDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEPhysiologyDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SESubstanceDataRequest; +import mil.tatrc.physiology.datamodel.system.SESystem; +import mil.tatrc.physiology.datamodel.system.environment.SEEnvironment; +import mil.tatrc.physiology.datamodel.system.physiology.SEGastrointestinalSystem; +import mil.tatrc.physiology.utilities.FindObjects; +import mil.tatrc.physiology.utilities.Log; +import mil.tatrc.physiology.utilities.StringUtils; + +public class DataRequestPanel extends JPanel implements ActionListener +{ + protected final int HOR_GAP = 5; + protected final int VER_GAP = 5; + protected SEScenario scenario; + + protected JPanel listPanel; + protected JScrollPane scrollPane; + protected JButton addSysRequest; + protected JButton addCmptRequest; + protected JButton addEquipRequest; + protected JButton addSubRequest; + protected JPanel selectionGroup; + + protected DataRequestSelection activeSelector; + protected SystemRequestSelection systemSelection; + protected EquipmentRequestSelection equipmentSelection; + protected CompartmentRequestSelection compartmentSelection; + protected SubstanceRequestSelection substanceSelection; + + protected JButton addRequest; + protected Map listPanelRows = new HashMap(); + + + + public DataRequestPanel(LayoutManager layout, SEScenario scenario) + { + super(layout); + + this.scenario = scenario; + + listPanel = new JPanel(); + listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS)); + + // SYSTEM / PATIENT Requests + Map> systems2properties = new LinkedHashMap>(); + systems2properties.put("Patient", ScalarCtrl.getScalarProperties(SEPatient.class, false)); + systems2properties.put("Environment", ScalarCtrl.getScalarProperties(SEEnvironment.class, false)); + Set> physiologyClasses = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.system.physiology", SESystem.class); + for(Class phys : physiologyClasses) + { + + List props = ScalarCtrl.getScalarProperties(phys, false); + if(phys == SEGastrointestinalSystem.class) + {// Need to specially/manually add the properties of the child 'nutrientsinstomach' class with the matching name used in C++ (Yeah, this is pretty messy) + List stomachProps = ScalarCtrl.getScalarProperties(SENutrition.class, false); + for(ScalarProperty p : stomachProps) + { + p.name = "Stomach "+p.name; + props.add(p); + } + } + systems2properties.put(StringUtils.spaceCamelCase(phys.getSimpleName().substring(2)), props); + } + systemSelection = new SystemRequestSelection(this,systems2properties); + + // EQUIPMENT Requests + Map> equipment2properties = new LinkedHashMap>(); + Set> equipmentClasses = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.system.equipment", SESystem.class); + for(Class equip : equipmentClasses) + { + equipment2properties.put(equip.getSimpleName().substring(2), ScalarCtrl.getScalarProperties(equip, false)); + } + equipmentSelection = new EquipmentRequestSelection(this,equipment2properties); + + // SUBSTANCE Requests + substanceSelection = new SubstanceRequestSelection(this, this.scenario.getSubstanceManager()); + + // COMPARTMENT Requests + compartmentSelection = new CompartmentRequestSelection(this, this.scenario.getSubstanceManager()); + + + activeSelector = null; + addRequest = new JButton("Add"); + addRequest.addActionListener(this); + + JScrollPane scrollPane = new JScrollPane(listPanel); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + this.add(scrollPane, BorderLayout.CENTER); + + DataRequestRow row; + for (SEDataRequest dr : this.scenario.getDataRequests().getRequestedData()) + { + row = null; + if(dr instanceof SEPhysiologyDataRequest || // These 3 are packed into the same drop down + dr instanceof SEPatientDataRequest || + dr instanceof SEEnvironmentDataRequest) + row = systemSelection.createRow(dr); + else if(dr instanceof SEEquipmentDataRequest) + row = equipmentSelection.createRow(dr); + else if(dr instanceof SESubstanceDataRequest) + row = substanceSelection.createRow(dr); + else if(dr instanceof SECompartmentDataRequest) + row = compartmentSelection.createRow(dr); + + if(row == null) + Log.error("Unhandled Data Request Type : "+dr.getClass().getSimpleName()); + else + { + listPanel.add(row); + listPanelRows.put(row.removeMe, row); + } + } + + JPanel requestCreation = new JPanel(new BorderLayout(HOR_GAP, VER_GAP)); + JLabel drLabel = new JLabel(); + drLabel.setText("Create a Data Request : "); + + JPanel buttonPanel = new JPanel(new FlowLayout()); + addSysRequest = new JButton("System"); + addSysRequest.addActionListener(this); + buttonPanel.add(addSysRequest); + addCmptRequest = new JButton("Compartment"); + addCmptRequest.addActionListener(this); + buttonPanel.add(addCmptRequest); + addEquipRequest = new JButton("Equipment"); + addEquipRequest.addActionListener(this); + buttonPanel.add(addEquipRequest); + addSubRequest = new JButton("Substance"); + addSubRequest.addActionListener(this); + buttonPanel.add(addSubRequest); + + selectionGroup = new JPanel(new FlowLayout(FlowLayout.CENTER)); + + requestCreation.add(drLabel, BorderLayout.NORTH); + requestCreation.add(buttonPanel, BorderLayout.CENTER); + requestCreation.add(selectionGroup, BorderLayout.SOUTH); + this.add(requestCreation, BorderLayout.SOUTH); + } + + public void updateScenario() + { + this.scenario.getDataRequests().getRequestedData().clear(); + for(DataRequestRow row : this.listPanelRows.values()) + this.scenario.getDataRequests().getRequestedData().add(row.dr); + } + + public void actionPerformed(ActionEvent e) + { + if (e.getSource() == addSysRequest) + { + if(activeSelector != systemSelection) + { + if(activeSelector != null) + this.selectionGroup.removeAll(); + this.activeSelector = this.systemSelection; + this.selectionGroup.add(systemSelection); + this.selectionGroup.add(this.addRequest); + } + } + else if(e.getSource() == addCmptRequest) + { + if(activeSelector != compartmentSelection) + { + if(activeSelector != null) + this.selectionGroup.removeAll(); + this.activeSelector = this.compartmentSelection; + this.selectionGroup.add(compartmentSelection); + this.selectionGroup.add(this.addRequest); + } + } + else if(e.getSource() == addEquipRequest) + { + if(activeSelector != equipmentSelection) + { + if(activeSelector != null) + this.selectionGroup.removeAll(); + this.activeSelector = this.equipmentSelection; + this.selectionGroup.add(equipmentSelection); + this.selectionGroup.add(this.addRequest); + } + } + else if(e.getSource() == addSubRequest) + { + if(activeSelector != substanceSelection) + { + if(activeSelector != null) + this.selectionGroup.removeAll(); + this.activeSelector = this.substanceSelection; + this.selectionGroup.add(substanceSelection); + this.selectionGroup.add(this.addRequest); + } + } + else if(e.getSource() == addRequest) + { + if(activeSelector != null) + { + DataRequestRow row = this.activeSelector.createRow(); + this.listPanel.add(row); + this.listPanelRows.put(row.removeMe, row); + this.selectionGroup.removeAll(); + this.activeSelector = null; + } + } + else if(e.getSource().getClass() == JButton.class) + { + JButton b = (JButton)e.getSource(); + if(listPanelRows.containsKey(b)) + { + this.listPanel.remove(listPanelRows.get(b)); + this.listPanelRows.remove(b); + } + else + Log.error("I DON'T KNOW WHO THIS BUTTON IS!!"); + } + this.revalidate(); + this.repaint(); + } + + public static boolean containsString(JComboBox cb, String s) + { + boolean found=false; + for(int i=0; i categoryComboBox= new JComboBox(); + + protected JComboBox activePropBox = null; + protected JComboBox activeUnitBox = null; + + protected Map property2category = new HashMap(); + protected Map> category2propertyComboBox = new LinkedHashMap>(); + protected Map> property2unitComboBox = new LinkedHashMap>(); + + protected ActionListener pntListener; + + public DataRequestSelection(ActionListener pnt, Map> categories2properties) + { + super(new FlowLayout(FlowLayout.LEADING, 5, 5)); + this.pntListener = pnt; + + if(categories2properties != null) + { + for(String category : categories2properties.keySet()) + { + categoryComboBox.addItem(category); + JComboBox propertiesComboBox = new JComboBox(); + + category2propertyComboBox.put(category, propertiesComboBox); + + for(ScalarProperty p : categories2properties.get(category)) + { + propertiesComboBox.addItem(p.name); + if(property2category.containsKey(p.name)) + Log.info("I already mapped "+p.name); + property2category.put(p.name,category); + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + propertiesComboBox.addActionListener(this); + } + categoryComboBox.addActionListener(this); + + this.add(categoryComboBox); + activePropBox = category2propertyComboBox.get(categoryComboBox.getSelectedItem()); + this.add(activePropBox); + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + } + + public void actionPerformed(ActionEvent e) + { + if(e.getSource() == this.categoryComboBox) + { + this.remove(activePropBox); + this.remove(activeUnitBox); + this.activePropBox = this.category2propertyComboBox.get(this.categoryComboBox.getSelectedItem()); + this.activeUnitBox = this.property2unitComboBox.get(this.activePropBox.getSelectedItem()); + this.add(activePropBox); + this.add(activeUnitBox); + } + else if(e.getSource() == this.activePropBox) + { + this.remove(activeUnitBox); + this.activeUnitBox = this.property2unitComboBox.get(this.activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + + this.revalidate(); + this.repaint(); + } + + public DataRequestRow createRow(SEDataRequest dr) + { + String label; + String property = StringUtils.spaceCamelCase(dr.getName()); + if(!property2category.containsKey(property) || !property2unitComboBox.containsKey(property)) + label = "Unknown property "+property; + else + { + if(dr.hasUnit()) + label = property2category.get(property)+" "+property+" ("+dr.getUnit()+")"; + else + label = property2category.get(property)+" "+property+" ("+this.property2unitComboBox.get(property).getItemAt(0)+")"; + } + DataRequestRow row = new DataRequestRow(this.pntListener, label, dr); + return row; + } + + public abstract DataRequestRow createRow(); +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestWindow.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestWindow.java new file mode 100644 index 0000000..b19bef4 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/DataRequestWindow.java @@ -0,0 +1,164 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + **************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.datarequests; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.EtchedBorder; +import javax.swing.filechooser.FileNameExtensionFilter; + +import mil.tatrc.physiology.biogears.gui.GUIContext; +import mil.tatrc.physiology.datamodel.scenario.SEScenario; + +public class DataRequestWindow extends JFrame implements ActionListener +{ + private final int FRAME_WIDTH = 800; + private final int FRAME_HEIGHT = 600; + + private final int HOR_GAP = 5; + private final int VER_GAP = 5; + + private final String DEFAULT_TITLE = "Data Requests"; + + private SEScenario scenario; + + private JPanel guiPanel; + private DataRequestPanel dataRequestPanel; + + private JButton resultFileBtn = new JButton("SetResults"); + + private JButton okButton = new JButton("OK"); + private JButton cancelButton = new JButton("Cancel"); + + private JLabel sampleLabel = new JLabel(); + private JTextField sampleRate = new JTextField(); + + private JFileChooser fileChooser = new JFileChooser(); + + public DataRequestWindow(SEScenario scenario) + { + super(); + + this.setSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT)); + if(scenario.getDataRequests().hasFilename()) + this.setTitle(scenario.getDataRequests().getFilename()); + else + this.setTitle(DEFAULT_TITLE); + this.setResizable(false); + + this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + this.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + dispose(); + } + }); + + this.scenario = scenario; + + guiPanel = new JPanel(new BorderLayout(HOR_GAP, VER_GAP)); + + JPanel controlsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + okButton.addActionListener(this); + cancelButton.addActionListener(this); + controlsPanel.add(okButton); + controlsPanel.add(cancelButton); + + JPanel samplePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + sampleLabel.setText("Output file samples per second: "); + sampleRate.setPreferredSize(new Dimension(30, 20)); + if(scenario.getDataRequests().getSamplesPerSecond()>0) + sampleRate.setText(String.valueOf(scenario.getDataRequests().getSamplesPerSecond())); + samplePanel.add(this.sampleLabel); + samplePanel.add(this.sampleRate); + + JPanel controlsPnt = new JPanel(new BorderLayout(HOR_GAP, VER_GAP)); + controlsPnt.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + + JPanel resultsPnt = new JPanel(new FlowLayout(FlowLayout.LEFT)); + resultsPnt.setPreferredSize(new Dimension(215, 20)); + resultsPnt.add(resultFileBtn); + resultFileBtn.addActionListener(this); + + controlsPnt.add(resultsPnt,BorderLayout.WEST); + controlsPnt.add(controlsPanel,BorderLayout.CENTER); + controlsPnt.add(samplePanel,BorderLayout.EAST); + + + dataRequestPanel = new DataRequestPanel(new BorderLayout(HOR_GAP, VER_GAP), this.scenario); + guiPanel.add(dataRequestPanel, BorderLayout.CENTER); + guiPanel.add(controlsPnt, BorderLayout.SOUTH); + + this.add(guiPanel); + + this.setVisible(true); + } + + public void actionPerformed(ActionEvent e) + { + Object src = e.getSource(); + + if (src == this.okButton) + { + this.dataRequestPanel.updateScenario(); + if(!this.getTitle().equals(DEFAULT_TITLE)) + this.scenario.getDataRequests().setFilename(this.getTitle()); + String samplesPerSecond = this.sampleRate.getText(); + if(samplesPerSecond!=null&&!samplesPerSecond.isEmpty()) + { + try + { + this.scenario.getDataRequests().setSamplesPerSecond(Double.parseDouble(samplesPerSecond)); + } + catch(Exception ex) + { + this.scenario.getDataRequests().setSamplesPerSecond(0); + } + } + + this.dispose(); + } + else if (src == this.cancelButton) + { + this.dispose(); + } + else if (src == resultFileBtn) + { + fileChooser.setFileFilter(new FileNameExtensionFilter("txt File", "txt")); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setCurrentDirectory(new File(System.getProperty("user.dir"))); + if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) + { + File file = fileChooser.getSelectedFile(); + this.setTitle(file.getAbsolutePath()); + } + } + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/EquipmentRequestSelection.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/EquipmentRequestSelection.java new file mode 100644 index 0000000..4793c60 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/EquipmentRequestSelection.java @@ -0,0 +1,49 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + **************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.datarequests; + +import java.awt.event.ActionListener; +import java.util.*; + + +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl.ScalarProperty; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEEquipmentDataRequest; +import mil.tatrc.physiology.utilities.StringUtils; + + + +public class EquipmentRequestSelection extends DataRequestSelection +{ + + + public EquipmentRequestSelection(ActionListener pnt, Map> categories2properties) + { + super(pnt, categories2properties); + } + + public DataRequestRow createRow() + { + String[] labels = new String[3]; + labels[0] = (String)this.categoryComboBox.getSelectedItem(); + labels[1] = (String)this.activePropBox.getSelectedItem(); + labels[2] = (String)this.activeUnitBox.getSelectedItem(); + + SEEquipmentDataRequest edr = new SEEquipmentDataRequest(); + edr.setType(StringUtils.removeSpaces(labels[0])); + edr.setName(StringUtils.removeSpaces(labels[1])); + edr.setUnit(labels[2]); + + DataRequestRow row = new DataRequestRow(this.pntListener, labels[0]+" "+labels[1]+"("+labels[2]+")", edr); + return row; + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/SubstanceRequestSelection.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/SubstanceRequestSelection.java new file mode 100644 index 0000000..c16bc4a --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/SubstanceRequestSelection.java @@ -0,0 +1,173 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + **************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.datarequests; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.SwingConstants; + +import mil.tatrc.physiology.biogears.engine.BioGearsCompartments; +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl; +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl.ScalarProperty; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SESubstanceDataRequest; +import mil.tatrc.physiology.datamodel.substance.*; +import mil.tatrc.physiology.utilities.StringUtils; + +public class SubstanceRequestSelection extends DataRequestSelection +{ + protected SESubstanceManager subMgr; + protected JCheckBox subCheck; + protected JComboBox subPropsBox = new JComboBox(); + protected JComboBox subFxPropsBox = new JComboBox(); + protected JComboBox cmptComboBox = new JComboBox(); + + public SubstanceRequestSelection(ActionListener pnt, SESubstanceManager subMgr) + { + super(pnt, null); + this.subMgr = subMgr; + + for(SESubstance s : subMgr.getSubstances()) + categoryComboBox.addItem(s.getName()); + categoryComboBox.addActionListener(this); + + for(ScalarProperty p : ScalarCtrl.getScalarProperties(SESubstance.class, false)) + { + subPropsBox.addItem(p.name); + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + subPropsBox.addActionListener(this); + + for(ScalarProperty p : ScalarCtrl.getScalarProperties(SESubstanceTissuePharmacokinetics.class, false)) + { + subFxPropsBox.addItem(p.name); + JComboBox unitsComboBox = new JComboBox(); + property2unitComboBox.put(p.name, unitsComboBox); + if(p.units != null) + for(String unit : p.units) + unitsComboBox.addItem(unit); + else + unitsComboBox.addItem("Unitless"); + } + subFxPropsBox.addActionListener(this); + + for(Enum e : BioGearsCompartments.Tissue.values()) + cmptComboBox.addItem(StringUtils.spaceCamelCase(e.name())); + + subCheck = new JCheckBox("Tissue Pk"); + subCheck.setHorizontalAlignment(SwingConstants.CENTER); + subCheck.setVerticalAlignment(SwingConstants.TOP); + subCheck.setHorizontalTextPosition(SwingConstants.CENTER); + subCheck.setVerticalTextPosition(SwingConstants.BOTTOM); + subCheck.addActionListener(this); + + + this.add(subCheck); + this.add(categoryComboBox); + this.add(subPropsBox); + activePropBox = subPropsBox; + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + + public void actionPerformed(ActionEvent e) + { + if(e.getSource() == this.subCheck) + { + this.removeAll(); + this.add(subCheck); + this.add(categoryComboBox); + if(this.subCheck.isSelected()) + { + this.add(cmptComboBox); + this.add(subFxPropsBox); + activePropBox = subFxPropsBox; + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + else + { + this.add(subPropsBox); + activePropBox = subPropsBox; + activeUnitBox = property2unitComboBox.get(activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + } + else if(e.getSource() == this.activePropBox) + { + this.remove(activeUnitBox); + this.activeUnitBox = this.property2unitComboBox.get(this.activePropBox.getSelectedItem()); + this.add(activeUnitBox); + } + this.revalidate(); + this.repaint(); + } + + public DataRequestRow createRow(SEDataRequest dr) + { + String label; + String property = StringUtils.spaceCamelCase(dr.getName()); + + SESubstanceDataRequest sdr = (SESubstanceDataRequest)dr; + String substance = sdr.getSubstance().getName(); + + if(!sdr.hasCompartment()) + { + if(dr.hasUnit()) + label = substance +" "+property+" ("+dr.getUnit()+")"; + else + label = substance +" "+property+" ("+this.property2unitComboBox.get(property).getItemAt(0)+")"; + } + else + { + if(dr.hasUnit()) + label = substance +" "+StringUtils.spaceCamelCase(sdr.getCompartment())+" "+property+" ("+dr.getUnit()+")"; + else + label = substance +" "+StringUtils.spaceCamelCase(sdr.getCompartment())+" "+property+" ("+this.property2unitComboBox.get(property).getItemAt(0)+")"; + } + DataRequestRow row = new DataRequestRow(this.pntListener, label, dr); + return row; + } + + public DataRequestRow createRow() + { + String[] labels = new String[4]; + labels[0] = (String)this.categoryComboBox.getSelectedItem(); + labels[1] = (String)this.cmptComboBox.getSelectedItem(); + labels[2] = (String)this.activePropBox.getSelectedItem(); + labels[3] = (String)this.activeUnitBox.getSelectedItem(); + + SESubstanceDataRequest sdr = new SESubstanceDataRequest(subMgr.getSubstance(labels[0])); + if(activePropBox == subFxPropsBox) + { + sdr.setCompartment(StringUtils.removeSpaces(labels[1])); + labels[1] += " "; + } + else + labels[1] = ""; + sdr.setName(StringUtils.removeSpaces(labels[2])); + sdr.setUnit(labels[3]); + + DataRequestRow row = new DataRequestRow(this.pntListener, labels[0]+" "+labels[1]+labels[2]+"("+labels[3]+")", sdr); + return row; + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/SystemRequestSelection.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/SystemRequestSelection.java new file mode 100644 index 0000000..528295e --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/datarequests/SystemRequestSelection.java @@ -0,0 +1,64 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + **************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.datarequests; + +import java.awt.event.ActionListener; +import java.util.*; + +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl.ScalarProperty; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEEnvironmentDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEPatientDataRequest; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEPhysiologyDataRequest; +import mil.tatrc.physiology.utilities.StringUtils; + +public class SystemRequestSelection extends DataRequestSelection +{ + public SystemRequestSelection(ActionListener pnt, Map> categories2properties) + { + super(pnt, categories2properties); + } + + public DataRequestRow createRow() + { + String[] labels = new String[3]; + labels[0] = (String)this.categoryComboBox.getSelectedItem(); + labels[1] = (String)this.activePropBox.getSelectedItem(); + labels[2] = (String)this.activeUnitBox.getSelectedItem(); + + SEDataRequest dr; + if(labels[0].equals("Patient")) + { + SEPatientDataRequest pdr = new SEPatientDataRequest(); + pdr.setName(StringUtils.removeSpaces(labels[1])); + pdr.setUnit(labels[2]); + dr = pdr; + } + else if(labels[0].equals("Environment")) + { + SEEnvironmentDataRequest pdr = new SEEnvironmentDataRequest(); + pdr.setName(StringUtils.removeSpaces(labels[1])); + pdr.setUnit(labels[2]); + dr = pdr; + } + else + { + SEPhysiologyDataRequest pdr = new SEPhysiologyDataRequest(); + pdr.setName(StringUtils.removeSpaces(labels[1])); + pdr.setUnit(labels[2]); + dr = pdr; + } + DataRequestRow row = new DataRequestRow(this.pntListener, labels[0]+" "+labels[1]+"("+labels[2]+")", dr); + return row; + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ConsciousRespirationEditor.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ConsciousRespirationEditor.java new file mode 100644 index 0000000..74a5d4f --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ConsciousRespirationEditor.java @@ -0,0 +1,67 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + **************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.awt.Dimension; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.List; +import java.util.Map; + +import javax.swing.JDialog; +import javax.swing.JFrame; + +import mil.tatrc.physiology.datamodel.patient.actions.SEConsciousRespiration; +import mil.tatrc.physiology.utilities.Log; + +public class ConsciousRespirationEditor extends JDialog +{ + private final int FRAME_WIDTH = 420; + private final int FRAME_HEIGHT= 500; + + private ConsciousRespirationPanel panel; + + public ConsciousRespirationEditor(SEConsciousRespiration cr, Map> propertyOptions) + { + super(); + + this.setModal(true); + this.setTitle(cr.getClass().getSimpleName()); + this.setSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT)); + this.setResizable(false); + + this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + this.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + panel.cancelled = true; + dispose(); + try + { + finalize(); + } + catch(Throwable ex) + { + Log.error("Unable to close window",ex); + } + } + }); + + this.panel = new ConsciousRespirationPanel(cr,propertyOptions); + this.add(this.panel); + } + public boolean wasCancelled(){ return panel.cancelled; } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ConsciousRespirationPanel.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ConsciousRespirationPanel.java new file mode 100644 index 0000000..980c493 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ConsciousRespirationPanel.java @@ -0,0 +1,332 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; +import javax.swing.plaf.basic.BasicArrowButton; + +import mil.tatrc.physiology.biogears.gui.GUIContext; +import mil.tatrc.physiology.biogears.gui.scenario.DynamicPropertyPanel.Row; +import mil.tatrc.physiology.datamodel.patient.actions.SEBreathHold; +import mil.tatrc.physiology.datamodel.patient.actions.SEConsciousRespiration; +import mil.tatrc.physiology.datamodel.patient.actions.SEConsciousRespirationCommand; +import mil.tatrc.physiology.datamodel.patient.actions.SEForcedExhale; +import mil.tatrc.physiology.datamodel.patient.actions.SEForcedInhale; +import mil.tatrc.physiology.datamodel.patient.actions.SEUseInhaler; +import mil.tatrc.physiology.utilities.Log; +import mil.tatrc.physiology.utilities.FindObjects.BagMethod; + +public class ConsciousRespirationPanel extends JPanel implements ActionListener +{ + private final static int HOR_GAP = 5; + private final static int VER_GAP = 2; + + private SEConsciousRespiration consResp; + private JPanel commands = new JPanel(); + private JButton forcedInhale = new JButton("Inhale"); + private JButton forcedExhale = new JButton("Exhale"); + private JButton breathHold = new JButton("HoldBreath"); + private JButton useInhaler = new JButton("Use Inhaler"); + + private JButton okButton = new JButton("OK"); + private JButton cancelButton = new JButton("Cancel"); + protected boolean cancelled=false; + + private Map> propertyOptions; + + private List cmds = new ArrayList(); + + public ConsciousRespirationPanel(SEConsciousRespiration cr, Map> propertyOptions) + { + this.consResp = cr; + this.propertyOptions = propertyOptions; + this.setLayout(new BorderLayout(HOR_GAP, VER_GAP)); + + commands.setLayout(new BoxLayout(commands, BoxLayout.Y_AXIS)); + JScrollPane scrollPane = new JScrollPane(commands); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.getViewport().setPreferredSize(new Dimension(200,400)); + + JPanel controlsPanel = new JPanel(new GridBagLayout()); + forcedInhale.addActionListener(this); + forcedExhale.addActionListener(this); + breathHold.addActionListener(this); + useInhaler.addActionListener(this); + okButton.addActionListener(this); + cancelButton.addActionListener(this); + Dimension d = new Dimension(90, 20); + forcedInhale.setPreferredSize(d); + forcedExhale.setPreferredSize(d); + breathHold.setPreferredSize(d); + useInhaler.setPreferredSize(d); + okButton.setPreferredSize(d); + cancelButton.setPreferredSize(d); + + forcedInhale.setMinimumSize(d); + forcedExhale.setMinimumSize(d); + breathHold.setMinimumSize(d); + useInhaler.setMinimumSize(d); + okButton.setMinimumSize(d); + cancelButton.setMinimumSize(d); + + forcedInhale.setMaximumSize(d); + forcedExhale.setMaximumSize(d); + breathHold.setMaximumSize(d); + useInhaler.setMaximumSize(d); + okButton.setMaximumSize(d); + cancelButton.setMaximumSize(d); + + controlsPanel.add(forcedInhale, GUIContext.createGBC(0,0, 1,1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5,3,5,3))); + controlsPanel.add(forcedExhale, GUIContext.createGBC(1,0, 1,1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5,3,5,3))); + controlsPanel.add(breathHold, GUIContext.createGBC(2,0, 1,1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5,3,5,3))); + controlsPanel.add(useInhaler, GUIContext.createGBC(3,0, 1,1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5,3,5,3))); + controlsPanel.add(okButton, GUIContext.createGBC(1,1, 1,1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5,3,5,3))); + controlsPanel.add(cancelButton, GUIContext.createGBC(2,1, 1,1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5,3,5,3))); + + JPanel controlsPnt = new JPanel(new BorderLayout(HOR_GAP, VER_GAP)); + controlsPnt.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + controlsPnt.add(controlsPanel,BorderLayout.CENTER); + + this.add(scrollPane, BorderLayout.CENTER); + this.add(controlsPnt, BorderLayout.SOUTH); + + for(SEConsciousRespirationCommand cmd : cr.getCommands()) + AddCommand(cmd); + + // TODO Comment + } + + protected void AddCommand(SEConsciousRespirationCommand cmd) + { + CommandPanel cp = new CommandPanel(this,cmd,propertyOptions); + cmds.add(cp); + commands.add(cp); + commands.revalidate(); + commands.repaint(); + } + + protected static class CommandPanel extends JPanel + { + JButton deleteCmd = new JButton("Delete"); + JButton upCmd = new BasicArrowButton(BasicArrowButton.NORTH); + JButton downCmd = new BasicArrowButton(BasicArrowButton.SOUTH); + Map rowMap; + + public CommandPanel(ActionListener pnt, SEConsciousRespirationCommand cmd, Map> propertyOptions) + { + super(); + setLayout(new BorderLayout(HOR_GAP, VER_GAP)); + Border b = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); + TitledBorder title = BorderFactory.createTitledBorder(b, cmd.getClass().getSimpleName().substring(2)); + title.setTitleJustification(TitledBorder.LEFT); + setBorder(title); + + downCmd.addActionListener(pnt); + deleteCmd.addActionListener(pnt); + upCmd.addActionListener(pnt); + + JPanel controlsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + controlsPanel.add(downCmd); + controlsPanel.add(deleteCmd); + controlsPanel.add(upCmd); + + JPanel cmdFields = new JPanel(new GridBagLayout()); + + rowMap = new HashMap(); + List order = new ArrayList(); + List bags = new ArrayList(); + try{ + DynamicPropertyPanel.getPropertyBags(cmd, bags, null, null); + }catch(Exception ex){ Log.error("Having trouble with this class "+cmd.getClass().getSimpleName(),ex);} + for(BagMethod bag : bags) + { + if(bag.returnType == List.class) + continue;// Have to handle these with custom code above + + Row row = new Row(bag, propertyOptions); + + if(row.label.getText().equals("Comment")) + { + rowMap.put(row.label.getText(),row); + } + else + { + order.add(row.label.getText()); + rowMap.put(row.label.getText(),row); + } + } + Collections.sort(order); + order.add("Comment");// Put this last + int y=0; + for(String name : order) + { + Row row = rowMap.get(name); + if(row == null) + continue; + cmdFields.add(row.label, GUIContext.createGBC(0,y, 0.01,1, 1,1, GridBagConstraints.EAST, GridBagConstraints.BOTH, new Insets(2,5,0,5))); + cmdFields.add(row.value, GUIContext.createGBC(1,y, 1,1, 1,1, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(2,0,0,5))); + y++; + } + add(cmdFields, BorderLayout.CENTER); + add(controlsPanel,BorderLayout.SOUTH); + } + + public String pullData() + { + for(Row row : rowMap.values()) + { + String err=row.pullData(); + if(err!=null && !err.isEmpty()) + return err; + } + return null; + } + } + + public void actionPerformed(ActionEvent e) + { + Object src = e.getSource(); + + if(src == forcedInhale) + { + SEConsciousRespirationCommand cmd = new SEForcedInhale(); + this.consResp.getCommands().add(cmd); + AddCommand(cmd); + return; + } + else if(src == forcedExhale) + { + SEConsciousRespirationCommand cmd = new SEForcedExhale(); + this.consResp.getCommands().add(cmd); + AddCommand(cmd); + return; + } + else if(src == breathHold) + { + SEConsciousRespirationCommand cmd = new SEBreathHold(); + this.consResp.getCommands().add(cmd); + AddCommand(cmd); + return; + } + else if(src == useInhaler) + { + SEConsciousRespirationCommand cmd = new SEUseInhaler(); + this.consResp.getCommands().add(cmd); + AddCommand(cmd); + return; + } + else if(src == this.okButton) + { + // Pull data from the dialog and update the action + for(CommandPanel cmd : cmds) + { + String err = cmd.pullData(); + if(err!=null&&!err.isEmpty()) + { + JOptionPane.showMessageDialog(this, err); + return; + } + } + if(!this.consResp.isValid()) + { + JOptionPane.showMessageDialog(this, "Action is not vaild"); + return; + } + ((Window) getRootPane().getParent()).dispose(); + } + else if (src == this.cancelButton) + { + this.cancelled = true; + ((Window) getRootPane().getParent()).dispose(); + } + int i=-1; + boolean clicked=false; + for(CommandPanel cmd : this.cmds) + { + i++; + if(src == cmd.upCmd) + { + if(i>0) + { + CommandPanel c1 = cmds.get(i); + CommandPanel c0 = cmds.get(i-1); + commands.add(c0, i); + commands.add(c1, i-1); + cmds.set(i, c0); + cmds.set(i-1, c1); + // also switch the commands in the action + SEConsciousRespirationCommand cmd1 = consResp.getCommands().get(i); + SEConsciousRespirationCommand cmd0 = consResp.getCommands().get(i-1); + consResp.getCommands().set(i, cmd0); + consResp.getCommands().set(i-1, cmd1); + } + clicked=true; + } + else if(src == cmd.deleteCmd) + { + commands.remove(cmds.remove(i)); + consResp.getCommands().remove(i); + clicked=true; + } + else if(src == cmd.downCmd) + { + if(i> propertyOptions) + { + super(); + + this.setModal(true); + this.setTitle(obj.getClass().getSimpleName()); + this.setSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT)); + this.setResizable(false); + + this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + this.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + panel.cancelled = true; + dispose(); + try + { + finalize(); + } + catch(Throwable ex) + { + Log.error("Unable to close window",ex); + } + } + }); + + this.panel = new DynamicPropertyPanel(obj,propertyOptions); + this.add(this.panel); + } + public boolean wasCancelled(){ return panel.cancelled; } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/DynamicPropertyPanel.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/DynamicPropertyPanel.java new file mode 100644 index 0000000..8eec0ee --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/DynamicPropertyPanel.java @@ -0,0 +1,524 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.EtchedBorder; + +import mil.tatrc.physiology.biogears.gui.GUIContext; +import mil.tatrc.physiology.biogears.gui.controls.ScalarCtrl; +import mil.tatrc.physiology.datamodel.properties.SEScalar; +import mil.tatrc.physiology.datamodel.properties.SEScalarFraction; +import mil.tatrc.physiology.datamodel.scenario.actions.SEAction; +import mil.tatrc.physiology.datamodel.substance.SESubstance; +import mil.tatrc.physiology.datamodel.substance.SESubstanceCompound; +import mil.tatrc.physiology.datamodel.substance.SESubstanceFraction; +import mil.tatrc.physiology.datamodel.system.environment.SEEnvironmentalConditions; +import mil.tatrc.physiology.datamodel.system.environment.actions.SEEnvironmentChange; +import mil.tatrc.physiology.datamodel.system.environment.conditions.InitialEnvironment; +import mil.tatrc.physiology.utilities.FindObjects; +import mil.tatrc.physiology.utilities.Log; +import mil.tatrc.physiology.utilities.FindObjects.BagMethod; + +public class DynamicPropertyPanel extends JPanel implements ActionListener +{ + private final int HOR_GAP = 5; + private final int VER_GAP = 5; + + private Object obj; + private JButton okButton = new JButton("OK"); + private JButton cancelButton = new JButton("Cancel"); + protected Boolean cancelled=false; + + List rows = new ArrayList(); + + protected static class Row + { + public BagMethod bag; + + public JLabel label; + public JPanel value; + + protected JFormattedTextField scalarValue; + protected JComboBox scalarUnits; + protected JComboBox enumComboBox; + protected JComboBox optionsComboBox; + protected JTextArea comment; + protected JTextField stringValue; + + Row(BagMethod b, Map> propertyOptions) + { + this.bag = b; + + String name; + if(bag.myName != null && !bag.myName.equals("Configuration") && !bag.myName.equals("Conditions")) + name = bag.myName+bag.propertyName; + else + name = bag.propertyName; + label = new JLabel(name); + value = new JPanel(new FlowLayout(FlowLayout.LEFT)); + + if(propertyOptions !=null && propertyOptions.containsKey(bag.propertyName)) + { + optionsComboBox = new JComboBox(); + for(String s : propertyOptions.get(bag.propertyName)) + optionsComboBox.addItem(s); + value.add(optionsComboBox); + try + { + if((Boolean)bag.has.invoke(bag.me)) + { + if(String.class.isAssignableFrom(bag.returnType)) + { + String s = (String)bag.get.invoke(bag.me); + optionsComboBox.setSelectedItem(s); + } + else + { + // Should be a child object with a getName method... if not, you're fuct + Object o = bag.get.invoke(bag.me); + Method h = o.getClass().getMethod("hasName"); + if((Boolean)h.invoke(o)) + { + Method g = o.getClass().getMethod("getName"); + String s = (String)g.invoke(o); + optionsComboBox.setSelectedItem(s); + } + else + optionsComboBox.setSelectedIndex(-1); + } + } + else + optionsComboBox.setSelectedIndex(-1); + } + catch(Exception ex) + { + Log.error("Unable to set enum field : "+bag.propertyName,ex); + } + } + else if(SEScalar.class.isAssignableFrom(bag.returnType)) + { + // Create Controls + NumberFormat ni = NumberFormat.getNumberInstance(); + ni.setMaximumFractionDigits(9); + scalarValue = new JFormattedTextField(); + scalarValue.setColumns(10); + scalarValue.setPreferredSize(new Dimension(80,22)); + value.add(scalarValue); + + scalarUnits = new JComboBox(); + value.add(scalarUnits); + List units = ScalarCtrl.getUnitsList((Class)bag.returnType); + if(units==null || units.isEmpty()) + scalarUnits.addItem("Unitless"); + else + { + for(String unit : units) + { + scalarUnits.addItem(unit); + } + } + + // Fill out or default values + try + { + if((Boolean)bag.has.invoke(bag.me)) + { + SEScalar s = (SEScalar)bag.get.invoke(bag.me); + scalarValue.setText(Double.toString(s.getValue())); + scalarUnits.setSelectedItem(s.getUnit()); + } + } + catch(Exception ex) + { + Log.error("Unable to set scalar field : "+bag.propertyName,ex); + } + } + else if(bag.returnType.isEnum()) + { + // Create controls + enumComboBox = new JComboBox(); + for (Object e: bag.returnType.getEnumConstants()) + { + enumComboBox.addItem(e.toString()); + value.add(enumComboBox); + } + // Fill out or default values + try + { + if((Boolean)bag.has.invoke(bag.me)) + { + Enum e = (Enum)bag.get.invoke(bag.me); + enumComboBox.setSelectedItem(e.name()); + } + else + enumComboBox.setSelectedIndex(-1); + } + catch(Exception ex) + { + Log.error("Unable to set enum field : "+bag.propertyName,ex); + } + } + else if(String.class.isAssignableFrom(bag.returnType)) + { + + if(bag.propertyName.equals("Comment")) + { + comment = new JTextArea(); + comment.setLineWrap(true); + comment.setWrapStyleWord(true); + JScrollPane scroll = new JScrollPane(comment); + scroll.setPreferredSize(new Dimension(175,30)); + scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + value.add(scroll); + try + { + if((Boolean)bag.has.invoke(bag.me)) + comment.setText((String)bag.get.invoke(bag.me)); + } + catch(Exception ex) + { + Log.error("Unable to set enum field : "+bag.propertyName,ex); + } + } + else + { + stringValue = new JTextField(); + stringValue.setPreferredSize(new Dimension(225,22)); + value.add(stringValue); + try + { + if((Boolean)bag.has.invoke(bag.me)) + comment.setText((String)bag.get.invoke(bag.me)); + } + catch(Exception ex) + { + Log.error("Unable to set enum field : "+bag.propertyName,ex); + } + } + } + } + + public String pullData() + { + if(optionsComboBox != null) + { + try + { + if(String.class.isAssignableFrom(bag.returnType)) + { + Method m = bag.me.getClass().getMethod("set"+bag.propertyName, bag.returnType); + m.invoke(bag.me, optionsComboBox.getSelectedItem()); + } + else + { + // Should be a child object with a setName method... if not, you're fuct + Object o = bag.get.invoke(bag.me); + if(o==null) + { + o=bag.returnType.newInstance(); + Method m = bag.me.getClass().getMethod("set"+bag.propertyName, bag.returnType); + m.invoke(bag.me, o); + } + Method s = o.getClass().getMethod("setName",String.class); + s.invoke(o, optionsComboBox.getSelectedItem()); + } + }catch(Exception ex) + { + Log.error("Cannot find options set method for : "+bag.propertyName,ex); + } + } + else if(scalarValue != null) + { + try + { + if(scalarValue.getText()==null || scalarValue.getText().isEmpty()) + { + SEScalar s = (SEScalar)bag.get.invoke(bag.me); + s.invalidate(); + } + else + { + double v = 0; + try{ + v=Double.parseDouble(scalarValue.getText()); + }catch(Exception ex){Log.error("Scalar value must be numerical for property : "+bag.propertyName);} + String unit = (String)scalarUnits.getSelectedItem(); + + + SEScalar s = (SEScalar)bag.get.invoke(bag.me); + if(s instanceof SEScalarFraction) + { + if(v<0 || v>1) + return bag.propertyName+" is a fraction. Must be between [0,1]"; + } + if(unit.equals("Unitless")) + s.setValue(v); + else + s.setValue(v,unit); + } + } + catch(Exception ex) + { + Log.error("get method is not setup propertly for property : "+bag.propertyName); + } + } + else if(enumComboBox != null) + { + if(enumComboBox.getSelectedIndex()==-1) + { + + } + else + { + try{ + String selection = (String)enumComboBox.getSelectedItem(); + Method set = bag.me.getClass().getMethod("set"+bag.propertyName, bag.returnType); + for (Object o: bag.returnType.getEnumConstants()) + { + if(o.toString().equals(selection)) + { + set.invoke(bag.me,o); + break; + } + } + }catch(Exception ex){Log.error("Unable to update enum property : "+bag.propertyName,ex);} + } + } + else if(stringValue != null) + { + try{ + Method set = bag.me.getClass().getMethod("set"+bag.propertyName, bag.returnType); + set.invoke(bag.me, stringValue.getText()); + }catch(Exception ex){Log.error("Unable to update string property : "+bag.propertyName,ex);} + } + else if(comment != null) + { + if(bag.me instanceof SEAction) + ((SEAction)bag.me).setComment(comment.getText()); + } + else + Log.error("This Action Row is unmapped to the action"); + + return null; + } + + } + + public DynamicPropertyPanel(Object obj, Map> propertyOptions) + { + this.obj = obj; + this.setLayout(new BorderLayout(HOR_GAP, VER_GAP)); + + JPanel actionFields = new JPanel(); + actionFields.setLayout(new GridBagLayout()); + + JScrollPane scrollPane = new JScrollPane(actionFields); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + + + List skipProperties = new ArrayList(); + skipProperties.add("ScenarioTime"); + skipProperties.add("ConditionsFile"); + skipProperties.add("ConfigurationFile"); + skipProperties.add("MCIS"); + + List order = new ArrayList(); + Map rowMap = new HashMap(); + List bags = new ArrayList(); + try{ + getPropertyBags(obj, bags, skipProperties, null); + }catch(Exception ex){ Log.error("Having trouble with this class "+obj.getClass().getSimpleName(),ex);} + + if(obj.getClass() == SEEnvironmentChange.class || obj.getClass() == InitialEnvironment.class) + { + SEEnvironmentalConditions ec=null; + if(obj.getClass() == SEEnvironmentChange.class) + ec = ((SEEnvironmentChange)obj).getConditions(); + else if(obj.getClass() == InitialEnvironment.class) + ec = ((InitialEnvironment)obj).getConditions(); + + + try + { + SESubstance O2 = new SESubstance(); + O2.setName("Oxygen"); + SESubstanceFraction o2 = ec.getAmbientGas(O2); + BagMethod o2bag = new BagMethod(); + o2bag.me = o2; + o2bag.propertyName = "Fraction of Oxygen"; + o2bag.has = o2.getClass().getMethod("hasFractionAmount"); + o2bag.get = o2.getClass().getMethod("getFractionAmount"); + o2bag.returnType = SEScalarFraction.class; + bags.add(o2bag); + + SESubstance CO2 = new SESubstance(); + CO2.setName("CarbonDioxide"); + SESubstanceFraction co2 = ec.getAmbientGas(CO2); + BagMethod co2bag = new BagMethod(); + co2bag.me = co2; + co2bag.propertyName = "Fraction of CarbonDioxide"; + co2bag.has = co2.getClass().getMethod("hasFractionAmount"); + co2bag.get = co2.getClass().getMethod("getFractionAmount"); + co2bag.returnType = SEScalarFraction.class; + bags.add(co2bag); + + SESubstance N2 = new SESubstance(); + N2.setName("Nitrogen"); + SESubstanceFraction n2 = ec.getAmbientGas(N2); + BagMethod n2bag = new BagMethod(); + n2bag.me = n2; + n2bag.propertyName = "Fraction of Nitrogen"; + n2bag.has = n2.getClass().getMethod("hasFractionAmount"); + n2bag.get = n2.getClass().getMethod("getFractionAmount"); + n2bag.returnType = SEScalarFraction.class; + bags.add(n2bag); + }catch(Exception ex){Log.error("We should be cool here",ex);} + } + + for(BagMethod bag : bags) + { + if(bag.returnType == List.class) + continue;// Have to handle these with custom code above + + Row row = new Row(bag, propertyOptions); + rows.add(row); + + if(row.label.getText().equals("Comment")) + { + rowMap.put(row.label.getText(),row); + } + else + { + order.add(row.label.getText()); + rowMap.put(row.label.getText(),row); + } + } + Collections.sort(order); + order.add("Comment");// Put this last + int y=0; + for(String name : order) + { + Row row = rowMap.get(name); + if(row == null) + continue; + actionFields.add(row.label, GUIContext.createGBC(0,y, 0.01,1, 1,1, GridBagConstraints.EAST, GridBagConstraints.BOTH, new Insets(5,5,0,5))); + actionFields.add(row.value, GUIContext.createGBC(1,y, 1,1, 1,1, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(5,0,0,5))); + y++; + } + + JPanel controlsPanel = new JPanel(new FlowLayout()); + okButton.addActionListener(this); + cancelButton.addActionListener(this); + controlsPanel.add(okButton); + controlsPanel.add(cancelButton); + + JPanel controlsPnt = new JPanel(new BorderLayout(HOR_GAP, VER_GAP)); + controlsPnt.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + controlsPnt.add(controlsPanel,BorderLayout.CENTER); + + this.add(scrollPane, BorderLayout.CENTER); + this.add(controlsPnt, BorderLayout.SOUTH); + } + + public static void getPropertyBags(Object obj, List bags, List skipProperties, String name) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException + { + for(BagMethod bag : FindObjects.getBagMethods(obj.getClass(), skipProperties)) + { + bag.myName = name; + bag.me = obj; + if(SEScalar.class.isAssignableFrom(bag.returnType)) + bags.add(bag); + else if(bag.returnType.isEnum()) + bags.add(bag); + else if(String.class.isAssignableFrom(bag.returnType)) + bags.add(bag); + else if(SESubstance.class.isAssignableFrom(bag.returnType)) + bags.add(bag); + else if(SESubstanceCompound.class.isAssignableFrom(bag.returnType)) + bags.add(bag); + else if(bag.returnType.getSimpleName().startsWith("SE")) + { + getPropertyBags(bag.get.invoke(obj),bags,skipProperties,bag.propertyName); + } + else if(bag.returnType == List.class && bag.propertyName.equals("AmbientSubstance") && bag.me.getClass() == SEEnvironmentalConditions.class) + bags.add(bag);// I am going to have to handle this with specialness + else + Log.warn("Unknown property class : "+bag.returnType.getSimpleName()); + } + } + + public void actionPerformed(ActionEvent e) + { + Object src = e.getSource(); + + if (src == this.okButton) + { + // Pull data from the dialog and update the action + for(Row row : rows) + { + String err = row.pullData(); + if(err!=null&&!err.isEmpty()) + { + JOptionPane.showMessageDialog(this, err); + return; + } + } + if(SEAction.class.isAssignableFrom(obj.getClass())) + { + if(!((SEAction)obj).isValid()) + { + JOptionPane.showMessageDialog(this, "Action is not vaild"); + return; + } + } + ((Window) getRootPane().getParent()).dispose(); + } + else if (src == this.cancelButton) + { + this.cancelled = true; + ((Window) getRootPane().getParent()).dispose(); + } + } + + + +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ResultsWindow.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ResultsWindow.java new file mode 100644 index 0000000..1d8f97e --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ResultsWindow.java @@ -0,0 +1,330 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +/** + * @author fmenozzi + */ +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.awt.BorderLayout; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.border.EtchedBorder; +import javax.swing.plaf.basic.BasicArrowButton; + +public final class ResultsWindow extends JFrame implements ActionListener, KeyListener +{ + // TODO Move things not being listened to back into constructor + private final int FRAME_WIDTH = 1100; + private final int FRAME_HEIGHT = 700; + + private final int HOR_GAP = 5; + private final int VER_GAP = 5; + + private final JFrame frame = this; + + private File resultsDir; + + private List resultsImagesPaths; + + private int currentImageIndex = -1; + + private JPanel guiPanel = new JPanel(new BorderLayout(HOR_GAP, VER_GAP)); + private ImagePanel imagePanel = new ImagePanel(); + private JPanel controlsPanel = new JPanel(new GridLayout(1,3)); + + private JButton selectButton = new JButton("Send To"); + private JButton closeButton = new JButton("Close"); + + private JButton prevButton = new BasicArrowButton(BasicArrowButton.WEST); + private JButton nextButton = new BasicArrowButton(BasicArrowButton.EAST); + + private JLabel imageNumberLabel; + + private JCheckBox errorCheckBox = new JCheckBox("Show Error", false); + private JCheckBox residualCheckBox = new JCheckBox("Show Residual", false); + private JCheckBox standardCheckBox = new JCheckBox("Show Standard", true); + + private class ImagePanel extends JPanel + { + private Image img; + + @Override + public void paintComponent(Graphics g) + { + g.drawImage(img, 0, 0, getWidth(), getHeight(), null); + } + + public void setImage(String imageFilePath) + { + this.img = new ImageIcon(imageFilePath).getImage(); + validate(); + repaint(); + } + } + + public ResultsWindow(File resultsDir) + { + super(); + + errorCheckBox.setEnabled(false); + residualCheckBox.setEnabled(false); + + this.resultsDir = resultsDir; + + this.addKeyListener(this); + this.setFocusable(true); + + this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + this.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + frame.dispose(); + } + }); + + resultsImagesPaths = new ArrayList(); + for (File imageFile : resultsDir.listFiles()) + if (imageFile.getAbsolutePath().endsWith(".jpg")) + resultsImagesPaths.add(imageFile.getAbsolutePath()); + + if(resultsImagesPaths.isEmpty()) + { + this.setTitle("No Results Found"); + return; + } + else + this.setTitle("Results - " + resultsImagesPaths.get(0)); + + this.setSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT)); + + imagePanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + + JPanel selectClosePanel = new JPanel(new FlowLayout()); + selectButton.setToolTipText("Open a file with the default extension viewer"); + selectButton.addActionListener(this); + closeButton.addActionListener(this); + selectClosePanel.add(selectButton); + selectClosePanel.add(closeButton); + controlsPanel.add(selectClosePanel); + + JPanel centerControlPanel = new JPanel(new BorderLayout(HOR_GAP, VER_GAP)); + + JPanel leftRightPanel = new JPanel(new FlowLayout()); + prevButton.addActionListener(this); + nextButton.addActionListener(this); + leftRightPanel.add(new JLabel("Prev")); + leftRightPanel.add(prevButton); + leftRightPanel.add(nextButton); + leftRightPanel.add(new JLabel("Next")); + + JPanel imageNumberPanel = new JPanel(new FlowLayout()); + imageNumberLabel = new JLabel("1 of " + resultsImagesPaths.size()); + imageNumberPanel.add(imageNumberLabel); + + gotoNextImage(); + + centerControlPanel.add(leftRightPanel, BorderLayout.CENTER); + centerControlPanel.add(imageNumberPanel, BorderLayout.SOUTH); + + controlsPanel.add(centerControlPanel); + + JPanel checkboxPanel = new JPanel(new FlowLayout()); + errorCheckBox.addActionListener(this); + residualCheckBox.addActionListener(this); + standardCheckBox.addActionListener(this); + checkboxPanel.add(standardCheckBox); + checkboxPanel.add(errorCheckBox); + checkboxPanel.add(residualCheckBox); + controlsPanel.add(checkboxPanel); + + controlsPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + + guiPanel.add(imagePanel, BorderLayout.CENTER); + guiPanel.add(controlsPanel, BorderLayout.SOUTH); + + this.add(guiPanel); + + this.setVisible(true); + } + + private void decrementCurrentImageIndex() + { + if (currentImageIndex == 0) + currentImageIndex = resultsImagesPaths.size() - 1; + else + currentImageIndex--; + } + + private void incrementCurrentImageIndex() + { + if (currentImageIndex == resultsImagesPaths.size() - 1) + currentImageIndex = 0; + else + currentImageIndex++; + } + + private String getCurrentImagePath() + { + return resultsImagesPaths.get(currentImageIndex); + } + + private boolean currentImageIsResidualImage() + { + return getCurrentImagePath().contains("Residual"); + } + + private boolean currentImageIsErrorImage() + { + return getCurrentImagePath().contains("Error"); + } + + private boolean currentImageIsStandardImage() + { + return ! (currentImageIsErrorImage() || currentImageIsResidualImage()); + } + + private boolean nothingIsChecked() + { + return ! (standardCheckBox.isSelected() || errorCheckBox.isSelected() || residualCheckBox.isSelected()); + } + + private void gotoPreviousImage() + { + if (nothingIsChecked()) + return; + + decrementCurrentImageIndex(); + + while (true) + { + if (currentImageIsErrorImage() && errorCheckBox.isSelected()) + break; + else if (currentImageIsResidualImage() && residualCheckBox.isSelected()) + break; + else if (currentImageIsStandardImage() && standardCheckBox.isSelected()) + break; + + decrementCurrentImageIndex(); + } + + imagePanel.setImage(getCurrentImagePath()); + imageNumberLabel.setText(currentImageIndex+1 + " of " + resultsImagesPaths.size()); + this.setTitle("Results - " + getCurrentImagePath()); + } + + private void gotoNextImage() + { + if (nothingIsChecked()) + return; + + incrementCurrentImageIndex(); + + while (true) + { + if (currentImageIsErrorImage() && errorCheckBox.isSelected()) + break; + else if (currentImageIsResidualImage() && residualCheckBox.isSelected()) + break; + else if (currentImageIsStandardImage() && standardCheckBox.isSelected()) + break; + + incrementCurrentImageIndex(); + } + + imagePanel.setImage(getCurrentImagePath()); + imageNumberLabel.setText(currentImageIndex+1 + " of " + resultsImagesPaths.size()); + this.setTitle("Results - " + getCurrentImagePath()); + } + + @Override + public void actionPerformed(ActionEvent e) + { + Object src = e.getSource(); + + if (src == this.selectButton) + { + JFileChooser fileChooser = new JFileChooser(resultsDir.getAbsoluteFile()); + fileChooser.setDialogTitle("Open File With Default Viewer"); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setSelectedFile(new File(getCurrentImagePath())); + + if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) + { + File file = fileChooser.getSelectedFile(); + try + { + Desktop.getDesktop().open(file); + } + catch (IOException ex) + { + JOptionPane.showMessageDialog(this, "Unable to open " + file.getName(), "Error", JOptionPane.ERROR_MESSAGE); + } + } + } + else if (src == this.closeButton) + { + this.dispose(); + } + else if (src == this.prevButton) + { + gotoPreviousImage(); + } + else if (src == this.nextButton) + { + gotoNextImage(); + } + } + + @Override + public void keyTyped(KeyEvent e) {} + + @Override + public void keyPressed(KeyEvent e) {} + + @Override + public void keyReleased(KeyEvent e) + { + // TODO There's a bug somewhere with setting the focus to this frame + int keyCode = e.getKeyCode(); + if (keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_KP_LEFT) + gotoPreviousImage(); + else if (keyCode == KeyEvent.VK_RIGHT || keyCode == KeyEvent.VK_KP_RIGHT) + gotoNextImage(); + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcCallback.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcCallback.java new file mode 100644 index 0000000..b8530d1 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcCallback.java @@ -0,0 +1,64 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.util.Collection; + +import mil.tatrc.physiology.biogears.engine.BioGears; +import mil.tatrc.physiology.biogears.engine.CDMUpdatedCallback; +import mil.tatrc.physiology.biogears.gui.scenario.ScenarioCalcDisplay.ChartDataRequestScalar; +import mil.tatrc.physiology.datamodel.properties.SEScalar; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEDataRequest; +import mil.tatrc.physiology.utilities.Log; +import mil.tatrc.physiology.utilities.Pair; + +public class ScenarioCalcCallback extends CDMUpdatedCallback +{ + protected BioGears bg; + protected Collection charts; + + public ScenarioCalcCallback(BioGears bg, Collection charts, double updateFrequency_s) + { + super(updateFrequency_s); + this.bg = bg; + this.charts = charts; + } + + public void update(double time_s) + { + double d; + for(ChartDataRequestScalar chart : charts) + { + if(chart.scalar == null) + { + for(Pair p : bg.getDataRequestPairs()) + { + if(p.getL() == chart.dataRequest) + { + chart.scalar = p.getR(); + break; + } + } + if(chart.scalar == null) + Log.error("Could not find scalar for chart " + chart.graphLine.getLabel()); + } + d=0; + if(chart.scalar.isValid()) + d=chart.scalar.getValue(); + if(Double.isInfinite(d)) + d=Double.NaN; + chart.graphLine.addPoint(time_s,d); + chart.zoom(); + } + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcDialog.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcDialog.java new file mode 100644 index 0000000..ffed336 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcDialog.java @@ -0,0 +1,61 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.awt.Dimension; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; + +import javax.swing.JDialog; + +import mil.tatrc.physiology.datamodel.scenario.SEScenario; +import mil.tatrc.physiology.utilities.Log; + +public class ScenarioCalcDialog extends JDialog +{ + private ScenarioCalcPanel panel; + public ScenarioCalcDialog(SEScenario scenario) + { + this.setTitle(scenario.getName()); + this.setModal(true); + this.setSize(new Dimension(1280, 950)); + this.setResizable(true); + this.panel = new ScenarioCalcPanel(scenario); + this.add(this.panel); + + this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + + final ScenarioCalcDialog me = this; + this.addWindowListener(new WindowAdapter() + { + @Override + public void windowClosing(WindowEvent e) + { + if(me.panel.display.cleanUp()) + { + me.dispose(); + try + { + me.finalize(); + } + catch(Throwable ex) + { + Log.error("Unable to close window",ex); + } + } + } + }); + } + +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcDisplay.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcDisplay.java new file mode 100644 index 0000000..68a8775 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcDisplay.java @@ -0,0 +1,354 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.scenario; + +import info.monitorenter.gui.chart.*; +import info.monitorenter.gui.chart.IAxis.*; +import info.monitorenter.gui.chart.traces.*; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; + +import mil.tatrc.physiology.biogears.engine.BioGearsScenarioExec; +import mil.tatrc.physiology.biogears.gui.controls.ConsoleListener; +import mil.tatrc.physiology.datamodel.properties.SEScalar; +import mil.tatrc.physiology.datamodel.properties.SEScalarFraction; +import mil.tatrc.physiology.datamodel.scenario.SEScenario; +import mil.tatrc.physiology.datamodel.scenario.datarequests.*; +import mil.tatrc.physiology.utilities.Log; + + +public class ScenarioCalcDisplay extends JPanel implements ActionListener +{ + protected JButton cancelCloseButton; + protected JScrollPane outputSelection; + + protected ScenarioCalcThread calcThread; + protected ScenarioCalcCallback calcCallback; + protected ConsoleListener calcListener; + + // Controls needed for inline action listener classes + protected GridBagConstraints gbc = new GridBagConstraints(); + protected Dimension chartSize = new Dimension(920, 550); + + protected Map labels2DataRequest = new HashMap(); + protected Map chartMap = new HashMap(); + + protected class ChartDataRequestScalar + { + public ZoomableChart chart; + public ITrace2D graphLine; + public SEScalar scalar; + public SEDataRequest dataRequest; + + public ChartDataRequestScalar(ZoomableChart chart, ITrace2D graphLine, SEDataRequest dr) + { + this.chart = chart; + this.graphLine = graphLine; + this.dataRequest = dr; + } + + protected double ymax=-Double.MAX_VALUE; + protected double ymin=Double.MAX_VALUE; + public void zoom() + { + if(graphLine.getMaxY()>ymax) + ymax=graphLine.getMaxY(); + if(graphLine.getMinY() scenarioRequests = new JList(requests); + scenarioRequests.addMouseListener(new MouseListener() { + public void mouseClicked(MouseEvent e) {} + public void mousePressed(MouseEvent e) + { + Chart2D chart = chartMap.get(scenarioRequests.getSelectedIndex()).chart; + chart.validate(); + chart.setRequestedRepaint(true); + + rGrid.remove(2); + rGrid.add(chart, gbc); + rGrid.validate(); + rGrid.repaint(); + } + public void mouseReleased(MouseEvent e) {} + public void mouseEntered(MouseEvent e) {} + public void mouseExited(MouseEvent e) {} + }); + + outputSelection = new JScrollPane(scenarioRequests); + scenarioRequests.setSelectedIndex(0); + Dimension size = new Dimension(250, 550); + outputSelection.setMinimumSize(size); + outputSelection.setPreferredSize(size); + outputSelection.setMaximumSize(size); + gbc.gridx=0; + gbc.gridy=0; + rRequests.add(outputSelection); + rGrid.add(rRequests, gbc); + + JPanel spacing = new JPanel(new GridBagLayout()); + Dimension space = new Dimension(25, 200); + spacing.setMinimumSize(space); + spacing.setPreferredSize(space); + spacing.setMaximumSize(space); + gbc.gridx=1; + gbc.gridy=0; + rGrid.add(spacing, gbc); + + // Create a chart on the Right side of the Dialog + loadMap(scenario,requests); + + Chart2D firstChart = chartMap.get(0).chart; + gbc.gridx=2; + gbc.gridy=0; + rGrid.add(firstChart, gbc); + + gui.add(rGrid, BorderLayout.NORTH); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + + this.cancelCloseButton = new JButton("Cancel"); + Dimension cSize = new Dimension(100, 25); + this.cancelCloseButton.setMinimumSize(cSize); + this.cancelCloseButton.setPreferredSize(cSize); + this.cancelCloseButton.setMaximumSize(cSize); + this.cancelCloseButton.addActionListener(this); + + buttonPanel.add(this.cancelCloseButton); + gui.add(buttonPanel, BorderLayout.SOUTH); + + add(gui); + + BioGearsScenarioExec executor = new BioGearsScenarioExec(); + this.calcCallback = new ScenarioCalcCallback(executor, chartMap.values(), 0.33); + this.calcThread = new ScenarioCalcThread(executor, scenario, this.calcListener, this.calcCallback, this); + this.calcThread.start(); + } + + public boolean cleanUp() + { + if(this.cancelCloseButton.getText().equals("Close")) + return true; + + int choice = JOptionPane.showConfirmDialog(this, "Cancel calculation and close window?", "Close", JOptionPane.OK_CANCEL_OPTION); + if (choice == JOptionPane.YES_OPTION) + { + cancel(); + return true; + } + return false; + } + + protected boolean cancelling=false;// We only want to process a cancel once + protected void cancel() + { + if(this.calcThread.isAlive()&&!this.cancelling) + { + this.cancelling=true; + // First Let's disable the buttons on this page while we wait for a cancel + this.cancelCloseButton.setEnabled(false); + this.cancelCloseButton.setText("Close"); + int choice = JOptionPane.showConfirmDialog(this, "Create graphs?", "Close", JOptionPane.OK_CANCEL_OPTION); + this.calcThread.cancel(choice == JOptionPane.YES_OPTION); + this.cancelCloseButton.setEnabled(true); + } + } + + private void loadMap(SEScenario scenario, String[] labels) + { + int i=0; + for (String label : labels) + { + ZoomableChart chart = new ZoomableChart(); + chart.setEnabled(false); + chart.setMinimumSize(chartSize); + chart.setPreferredSize(chartSize); + chart.setMaximumSize(chartSize); + chart.getAxisX().setAxisTitle(new AxisTitle("")); + chart.getAxisY().setAxisTitle(new AxisTitle("")); + ITrace2D graphLine = new Trace2DLtd(450); + + graphLine.setName("Time(s) vs "+label); + graphLine.setPhysicalUnits("", ""); + graphLine.setColor(Color.BLUE); + chart.addTrace(graphLine); + chartMap.put(i++, new ChartDataRequestScalar(chart,graphLine,labels2DataRequest.get(label))); + } + } + + private String[] getDataRequestLabels(SEScenario scenario) + { + // Note labels will get units when the engine provides the headers + // So if no unit is given, the engine unit is used + + // Labels are built to mimic how the engine builds it's labels as well + // So we can map the engine label to our gui label + String label; + List dataRequestLabels = new ArrayList(); + for (SEDataRequest dataRequest : scenario.getDataRequests().getRequestedData()) + { + label=""; + if(dataRequest instanceof SEPhysiologyDataRequest) + { + label = dataRequest.getName(); + } + else if(dataRequest instanceof SETissueCompartmentDataRequest) + { + SETissueCompartmentDataRequest dr=(SETissueCompartmentDataRequest)dataRequest; + label = dr.getCompartment(); + label += "-" + dr.getName(); + } + else if(dataRequest instanceof SECompartmentSubstanceDataRequest) + { + SECompartmentSubstanceDataRequest dr=(SECompartmentSubstanceDataRequest)dataRequest; + label = dr.getCompartment(); + if (dr.hasSubstance()) + label += "-" + dr.getSubstance().getName(); + label += "-" + dr.getName(); + } + else if(dataRequest instanceof SECompartmentDataRequest) + { + SECompartmentDataRequest dr=(SECompartmentDataRequest)dataRequest; + label = dr.getCompartment(); + label += "-" + dr.getName(); + } + else if(dataRequest instanceof SEEnvironmentDataRequest) + { + label = dataRequest.getName(); + } + else if(dataRequest instanceof SEEquipmentDataRequest) + { + label = dataRequest.getName(); + } + else if(dataRequest instanceof SESubstanceDataRequest) + { + SESubstanceDataRequest dr = (SESubstanceDataRequest)dataRequest; + label = dr.getSubstance().getName(); + if(dr.hasCompartment()) + label += "-"+dr.getCompartment(); + label += "-" + dr.getName(); + } + else if (dataRequest instanceof SEPatientDataRequest) + { + label = dataRequest.getName(); + } + else + Log.error("Unsupported DataRequest "+dataRequest.getClass().getName()); + + if(dataRequest.hasUnit()) + label = label+"("+dataRequest.getUnit()+")"; + dataRequestLabels.add(label); + labels2DataRequest.put(label, dataRequest); + } + Collections.sort(dataRequestLabels); + return dataRequestLabels.toArray(new String[] {}); + } + + public void actionPerformed(ActionEvent e) + { + Object src = e.getSource(); + + if (src == this.cancelCloseButton) + { + if (this.cancelCloseButton.getText().equals("Cancel")) + { + cancel(); + } + else if (this.cancelCloseButton.getText().equals("Close")) + { + ((Window) getRootPane().getParent()).dispose(); + } + } + else if(e.getSource() instanceof ScenarioCalcThread) + { + if(e.getActionCommand().equals("CalcComplete")) + { + this.cancelCloseButton.setEnabled(true); + + } + else if(e.getActionCommand().equals("Graphing")) + { + this.cancelCloseButton.setText("Close"); + this.cancelCloseButton.setEnabled(false); + } + } + + } + +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcPanel.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcPanel.java new file mode 100644 index 0000000..9e5d711 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcPanel.java @@ -0,0 +1,65 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.awt.BorderLayout; +import java.io.File; + +import javax.swing.JPanel; +import javax.swing.JSplitPane; + +import mil.tatrc.physiology.biogears.gui.controls.ConsoleCtrl; +import mil.tatrc.physiology.biogears.gui.controls.ConsoleListener; +import mil.tatrc.physiology.datamodel.scenario.SEScenario; +import mil.tatrc.physiology.utilities.Log; + +/** + * This panel is a split + * It holds the Graph and Cancel Button on top + * And the Log Console on bottom + * @author abray + * + */ +public class ScenarioCalcPanel extends JPanel +{ + protected ScenarioCalcDisplay display; + protected ConsoleCtrl console; + protected ConsoleListener sceListener; + + + public ScenarioCalcPanel(SEScenario scenario) + { + super(new BorderLayout()); + // Split Pane + JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + split.setDividerLocation(650); + split.setEnabled(false);// I don't want you moving the split for now + // Add the console to the JPanel + this.console = new ConsoleCtrl(); + this.console.setVisible(true); + split.setBottomComponent(this.console); + // Add a listner that listens to the calc and to the log messages in the display class + // And outputs those logs to the console + this.sceListener = new ConsoleListener(this.console); + Log.addAppender(this.sceListener); + // Add the display + this.display = new ScenarioCalcDisplay(scenario, this.sceListener); + split.setTopComponent(this.display); + add(split); + } + + public void finalize() + { + Log.removeAppender(this.sceListener); + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcThread.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcThread.java new file mode 100644 index 0000000..3235ee6 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioCalcThread.java @@ -0,0 +1,76 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import mil.tatrc.physiology.biogears.engine.BioGearsScenarioExec; +import mil.tatrc.physiology.biogears.gui.controls.ConsoleListener; +import mil.tatrc.physiology.datamodel.scenario.SEScenario; +import mil.tatrc.physiology.utilities.Log; +import mil.tatrc.physiology.utilities.csv.plots.CSVPlotTool; + +public class ScenarioCalcThread extends Thread +{ + protected SEScenario scenario; + protected BioGearsScenarioExec exec; + protected ScenarioCalcCallback callback; + protected ConsoleListener listener; + + protected boolean engineRunning; + protected boolean createGraphs=true; + protected ActionListener pnt; + + public ScenarioCalcThread(BioGearsScenarioExec executor, SEScenario scenario, ConsoleListener listener, ScenarioCalcCallback callback, ActionListener pnt) + { + this.listener=listener; + this.scenario = scenario; + this.callback = callback; + this.pnt = pnt; + this.exec = executor; + } + + public void run() + { + String resultsFile = scenario.getDataRequests().getFilename().replaceAll("[\\\\]", "/"); + + File resultsDir = new File(resultsFile.substring(0,resultsFile.lastIndexOf("/"))+"/"+scenario.getName()+"Graphs"); + resultsDir.mkdirs(); + + CSVPlotTool plotter=new CSVPlotTool(); + Log.addAppender(this.listener); + this.exec.setListener(this.listener); + // Put the results file in the same place as the sceFile + String logFile = resultsFile.replaceAll(".txt", ".log"); + this.engineRunning=true; + this.exec.runScenario(logFile, scenario, resultsFile, this.callback); + this.engineRunning=false; + this.pnt.actionPerformed(new ActionEvent(this, 1, "Graphing")); + if(this.createGraphs) + plotter.graphResults(resultsFile,resultsDir.getAbsolutePath()); + Log.info("Calculation Complete"); + Log.removeAppender(this.listener); + this.pnt.actionPerformed(new ActionEvent(this, 1, "CalcComplete")); + } + + public void cancel(boolean createGraphs) + { + this.createGraphs=createGraphs; + if(this.engineRunning) + { + this.exec.cancelScenario(); + } + } +} diff --git a/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioEditor.java b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioEditor.java new file mode 100644 index 0000000..3ce7c00 --- /dev/null +++ b/src/gui/java/mil/tatrc/physiology/biogears/gui/scenario/ScenarioEditor.java @@ -0,0 +1,923 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +package mil.tatrc.physiology.biogears.gui.scenario; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.*; +import javax.swing.plaf.basic.BasicArrowButton; + +import mil.tatrc.physiology.biogears.gui.GUIContext; +import mil.tatrc.physiology.biogears.gui.datarequests.DataRequestWindow; +import mil.tatrc.physiology.datamodel.CDMSerializer; +import mil.tatrc.physiology.datamodel.bind.ScenarioData; +import mil.tatrc.physiology.datamodel.patient.SEPatient; +import mil.tatrc.physiology.datamodel.patient.actions.SEConsciousRespiration; +import mil.tatrc.physiology.datamodel.patient.actions.SEConsciousRespirationCommand; +import mil.tatrc.physiology.datamodel.patient.actions.SEHemorrhage; +import mil.tatrc.physiology.datamodel.patient.actions.SEPatientAction; +import mil.tatrc.physiology.datamodel.patient.actions.SESubstanceBolus; +import mil.tatrc.physiology.datamodel.patient.actions.SESubstanceCompoundInfusion; +import mil.tatrc.physiology.datamodel.patient.actions.SESubstanceInfusion; +import mil.tatrc.physiology.datamodel.patient.conditions.SEPatientCondition; +import mil.tatrc.physiology.datamodel.properties.CommonUnits; +import mil.tatrc.physiology.datamodel.properties.CommonUnits.TimeUnit; +import mil.tatrc.physiology.datamodel.scenario.SEScenario; +import mil.tatrc.physiology.datamodel.scenario.SEScenarioInitialParameters; +import mil.tatrc.physiology.datamodel.scenario.actions.SEAction; +import mil.tatrc.physiology.datamodel.scenario.actions.SEAdvanceTime; +import mil.tatrc.physiology.datamodel.scenario.conditions.SECondition; +import mil.tatrc.physiology.datamodel.scenario.datarequests.SEDataRequestManager; +import mil.tatrc.physiology.datamodel.substance.SESubstance; +import mil.tatrc.physiology.datamodel.substance.SESubstanceCompound; +import mil.tatrc.physiology.datamodel.substance.SESubstanceManager; +import mil.tatrc.physiology.datamodel.system.environment.actions.SEEnvironmentAction; +import mil.tatrc.physiology.datamodel.system.environment.conditions.SEEnvironmentCondition; +import mil.tatrc.physiology.datamodel.system.equipment.anesthesia.actions.SEAnesthesiaMachineAction; +import mil.tatrc.physiology.datamodel.system.equipment.anesthesia.actions.SEAnesthesiaMachineConfiguration; +import mil.tatrc.physiology.datamodel.system.equipment.inhaler.actions.SEInhalerAction; +import mil.tatrc.physiology.datamodel.system.equipment.inhaler.actions.SEInhalerConfiguration; +import mil.tatrc.physiology.utilities.FindObjects; +import mil.tatrc.physiology.utilities.Log; +import mil.tatrc.physiology.utilities.StringUtils; + +public class ScenarioEditor extends JPanel implements ActionListener +{ + /* + * We'll put all the objects declared in the constructor up here as fields for now; + * once we know which ones we'll need to listen to and which ones we won't, we'll + * move the ones we don't need to listen to back into the constructor + */ + + + protected SESubstanceManager subMgr; + protected SEScenario scenario; + protected Map patients = new HashMap(); + + protected JPanel gui; + + protected JPanel scenarioGrid; + protected JTextField scenarioName; + protected JTextArea scenarioDescription; + protected JScrollPane descriptionScroll; + + protected JRadioButton patientBtn; + protected JComboBox patientFiles; + protected DefaultComboBoxModel patientFilesModel; + protected String defaultPatient; + protected JRadioButton stateBtn; + protected JComboBox stateFiles; + protected DefaultComboBoxModel stateFilesModel; + protected String defaultState; + protected JButton editCondition; + protected JButton clearCondition; + protected DefaultListModel conditionsListModel; + protected JList conditionsList; + protected JScrollPane conditionsScroll; + protected Set> patientConditions; + protected Set> environmentConditions; + protected List allConditionNames; + protected Map allConditions; + + + protected DefaultListModel actionsListModel; + protected JList actionsList; + protected JScrollPane actionsScroll; + protected JButton addAction; + protected DefaultListModel activeActionsListModel; + protected JList activeActionsList; + protected JScrollPane activeActionsScroll; + protected JPanel activeActionsControls; + protected JButton upAction; + protected JButton downAction; + protected JButton editAction; + protected JButton deleteAction; + protected Set> basicActions; + protected Set> patientActions; + protected Set> anesthesiaActions; + protected Set> inhalerActions; + protected Set> environmentActions; + protected Set> allActions=new HashSet>(); + + protected JButton outputButton; + protected JButton executeButton; + protected JButton resultsButton; + + protected Map,Map>> actionPropertyOptions; + + public void setSize(Component c, int width, int height) + { + Dimension d = new Dimension(width,height); + c.setSize(d); + c.setMinimumSize(d); + c.setMaximumSize(d); + c.setPreferredSize(d); + } + + public ScenarioEditor() + { + super(); + + // I am going to set up specific options for specific properties for specific actions.. generically! + actionPropertyOptions = new HashMap,Map>>(); + // Hemorrhage--this was already hacky with a list of "Left Arm, Right Arm, etc". I extended the hackiness with more detailed compartments. + List hCmpts = new ArrayList(); + hCmpts.add("Head"); + hCmpts.add("Lung"); + hCmpts.add("Myocardium"); + hCmpts.add("Liver"); + hCmpts.add("Spleen"); + hCmpts.add("Splanchnic"); + hCmpts.add("Kidney"); + hCmpts.add("Small Intestine"); + hCmpts.add("Large Intestine"); + hCmpts.add("Major Artery"); + hCmpts.add("Vena Cava"); + hCmpts.add("Arm"); + hCmpts.add("Leg"); + Map> hMap = new HashMap>(); + hMap.put("Compartment", hCmpts); + actionPropertyOptions.put(SEHemorrhage.class, hMap); + // Substance Administrations + List liqDrugs = new ArrayList(); + liqDrugs.add("Epinephrine"); + liqDrugs.add("Fentanyl"); + liqDrugs.add("Furosemide"); + liqDrugs.add("Ketamine"); + liqDrugs.add("Midazolam"); + liqDrugs.add("Morphine"); + liqDrugs.add("Naloxone"); + liqDrugs.add("Norepinephrine"); + liqDrugs.add("Pralidoxime"); + liqDrugs.add("Prednisone"); + liqDrugs.add("Propofol"); + liqDrugs.add("Rocuronium"); + liqDrugs.add("Succinylcholine"); + Map> saMap = new HashMap>(); + saMap.put("Substance", liqDrugs); + actionPropertyOptions.put(SESubstanceBolus.class, saMap); + actionPropertyOptions.put(SESubstanceInfusion.class, saMap); + List liqCmpds = new ArrayList(); + liqCmpds.add("Blood"); + liqCmpds.add("Saline"); + Map> caMap = new HashMap>(); + caMap.put("SubstanceCompound", liqCmpds); + actionPropertyOptions.put(SESubstanceCompoundInfusion.class, caMap); + // Anesthesia Machine Substances + List anesDrugs = new ArrayList(); + anesDrugs.add("Cortexiphan"); + anesDrugs.add("Desflurane"); + Map> anesMap = new HashMap>(); + anesMap.put("Substance", anesDrugs); + actionPropertyOptions.put(SEAnesthesiaMachineConfiguration.class, anesMap); + // Inhaler Substances + List iDrugs = new ArrayList(); + iDrugs.add("Albuterol"); + Map> iMap = new HashMap>(); + iMap.put("Substance", iDrugs); + actionPropertyOptions.put(SEInhalerConfiguration.class, iMap); + + gui = new JPanel(); + gui.setLayout(new BoxLayout(gui, BoxLayout.Y_AXIS)); + gui.setSize(new Dimension(600, 425)); + + // Scenario Name/Description + scenarioGrid = new JPanel(new GridBagLayout()); + + // Name + scenarioGrid.add(new JLabel("Name:",JLabel.LEFT), GUIContext.createGBC(0,1, 0.1,1, 1,1, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + scenarioName = new JTextField(10); + setSize(scenarioName,200, 25); + scenarioGrid.add(scenarioName, GUIContext.createGBC(1,1, 0.7,1, 1,1, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + //Description + scenarioGrid.add(new JLabel("Description:",JLabel.LEFT), GUIContext.createGBC(0,2, 0.1,1, 1,1, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + scenarioDescription = new JTextArea(); + scenarioDescription.setLineWrap(true); + scenarioDescription.setWrapStyleWord(true); + descriptionScroll = new JScrollPane(scenarioDescription); + descriptionScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); setSize(descriptionScroll,200, 150); + scenarioGrid.add(descriptionScroll, GUIContext.createGBC(1,2, 0.7,1, 1,1, GridBagConstraints.NORTHEAST, GridBagConstraints.BOTH, new Insets(0,0,0,5))); + + // Engine State||Patient + ButtonGroup initBtnGroup = new ButtonGroup(); + // State + stateBtn = new JRadioButton("Engine State"); + stateBtn.setAlignmentX(Component.CENTER_ALIGNMENT); + setSize(stateBtn,150, 25); + initBtnGroup.add(stateBtn); + stateBtn.addActionListener(this); + scenarioGrid.add(stateBtn, GUIContext.createGBC(3,0, 0.1,1, 1,1, GridBagConstraints.PAGE_END, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,5))); + List stateXMLs = getXMLFilenameList(new File(System.getProperty("user.dir") + "/states")); + for(String sFile : stateXMLs) + { + if(sFile.indexOf("StandardMale")>-1) + defaultState = sFile; + } + stateFilesModel = new DefaultComboBoxModel(stateXMLs.toArray(new String[] {})); + stateFiles = new JComboBox(stateFilesModel); + setSize(stateFiles,150, 25); + stateFiles.setSelectedIndex(-1); + stateFiles.setEditable(true); + stateFiles.setEnabled(true); + stateFiles.addActionListener(this); + scenarioGrid.add(stateFiles, GUIContext.createGBC(3,1, 0.1,1, 1,2, GridBagConstraints.PAGE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + // Patient + patientBtn = new JRadioButton("Patient File"); + patientBtn.setAlignmentX(Component.CENTER_ALIGNMENT); + setSize(patientBtn,150, 25); + initBtnGroup.add(patientBtn); + patientBtn.addActionListener(this); + scenarioGrid.add(patientBtn, GUIContext.createGBC(2,0, 0.1,1, 1,1, GridBagConstraints.PAGE_END, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,5))); + List patientXMLs = getXMLFilenameList(new File(System.getProperty("user.dir") + "/patients")); + patientFilesModel = new DefaultComboBoxModel(patientXMLs.toArray(new String[] {})); + patientFiles = new JComboBox(patientFilesModel); + setSize(patientFiles,150, 25); + patientFiles.setSelectedIndex(-1); + patientFiles.setEditable(true); + patientFiles.setEnabled(true); + patientFiles.addActionListener(this); + scenarioGrid.add(patientFiles, GUIContext.createGBC(2,1, 0.1,1, 1,2, GridBagConstraints.PAGE_START, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + // Load up those patients + for(String pFile : patientXMLs) + { + SEPatient patient = new SEPatient(); + patient.loadPatientFile("./patients/"+pFile); + this.patients.put(pFile, patient); + if(pFile.indexOf("StandardMale")>-1) + defaultPatient = pFile; + } + // Conditions List + JPanel conditionsPanel = new JPanel(new GridBagLayout()); + conditionsListModel = new DefaultListModel(); + conditionsList = new JList(conditionsListModel); + conditionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + conditionsScroll = new JScrollPane(conditionsList); + conditionsScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + setSize(conditionsScroll,250, 150); + allConditionNames = new ArrayList(); + allConditions = new HashMap(); + Set> patientConditions = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.patient.conditions", SEPatientCondition.class); + for(Class c : patientConditions) + { + if(Modifier.isAbstract(c.getModifiers())) + continue; + try{ + allConditionNames.add(StringUtils.spaceCamelCase(c.getSimpleName())); + allConditions.put(StringUtils.spaceCamelCase(c.getSimpleName()),c.newInstance()); + }catch(Exception ex){Log.error("Unable to create condition : "+c.getSimpleName(),ex);} + } + Set> envConditions = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.system.environment.conditions", SEEnvironmentCondition.class); + for(Class c : envConditions) + { + if(Modifier.isAbstract(c.getModifiers())) + continue; + try{ + allConditionNames.add(StringUtils.spaceCamelCase(c.getSimpleName())); + allConditions.put(StringUtils.spaceCamelCase(c.getSimpleName()),c.newInstance()); + }catch(Exception ex){Log.error("Unable to create condition : "+c.getSimpleName(),ex);} + } + Collections.sort(allConditionNames); + for(String s : allConditionNames) + conditionsListModel.addElement(new JLabel(s+" (Inactive)"){public String toString() {return getText();}}); + conditionsPanel.add(conditionsScroll, GUIContext.createGBC(0,0, 1,1, 1,2, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(0,0,0,5))); + // Buttons + JPanel conditionButtonsPanel = new JPanel(new GridBagLayout()); + editCondition = new JButton("Edit"); + editCondition.addActionListener(this); + conditionButtonsPanel.add(editCondition, GUIContext.createGBC(0,0, 0.1,1, 1,1, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + clearCondition = new JButton("Clear"); + clearCondition.addActionListener(this); + conditionButtonsPanel.add(clearCondition, GUIContext.createGBC(0,1, 0.1,1, 1,1, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + conditionsPanel.add(conditionButtonsPanel, GUIContext.createGBC(1,0, 0.1,1, 1,1, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + scenarioGrid.add(conditionsPanel, GUIContext.createGBC(2,2, 0.05,1, 2,2, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(0,0,0,0))); + + // Available Actions + List bActions = new ArrayList(); + basicActions = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.scenario.actions", SEAction.class); + for(Class c : basicActions) + { + bActions.add(StringUtils.spaceCamelCase(c.getSimpleName().substring(2))); + } + Collections.sort(bActions); + allActions.addAll(basicActions); + List pActions = new ArrayList(); + patientActions = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.patient.actions", SEPatientAction.class); + for(Class c : patientActions) + { + if(Modifier.isAbstract(c.getModifiers())) + continue; + pActions.add(StringUtils.spaceCamelCase(c.getSimpleName().substring(2))); + } + Collections.sort(pActions); + allActions.addAll(patientActions); + List eActions = new ArrayList(); + environmentActions = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.system.environment.actions", SEEnvironmentAction.class); + for(Class c : environmentActions) + { + if(Modifier.isAbstract(c.getModifiers())) + continue; + eActions.add(StringUtils.spaceCamelCase(c.getSimpleName().substring(2))); + } + Collections.sort(eActions); + allActions.addAll(environmentActions); + List aActions = new ArrayList(); + anesthesiaActions = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.system.equipment.anesthesia.actions", SEAnesthesiaMachineAction.class); + for(Class c : anesthesiaActions) + { + if(Modifier.isAbstract(c.getModifiers())) + continue; + aActions.add(StringUtils.spaceCamelCase(c.getSimpleName().substring(2))); + } + Collections.sort(aActions); + allActions.addAll(anesthesiaActions); + List iActions = new ArrayList(); + inhalerActions = FindObjects.findClassSubTypes("mil.tatrc.physiology.datamodel.system.equipment.inhaler.actions", SEInhalerAction.class); + for(Class c : inhalerActions) + { + if(Modifier.isAbstract(c.getModifiers())) + continue; + iActions.add(StringUtils.spaceCamelCase(c.getSimpleName().substring(2))); + } + Collections.sort(iActions); + allActions.addAll(inhalerActions); + + + // Actions + scenarioGrid.add(new JLabel("Actions:",JLabel.LEFT), GUIContext.createGBC(0,5, 0,1, 1,1, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + actionsListModel = new DefaultListModel(); + actionsList = new JList(actionsListModel); + actionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + actionsScroll = new JScrollPane(actionsList); + actionsScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + setSize(actionsScroll,200, 250); + for(String s : bActions) + actionsListModel.addElement(new JLabel(s){public String toString() {return getText();}}); + //Patient Actions + actionsListModel.addElement(new JLabel("Patient Actions"){public String toString() {return getText();}}); + for(String s : pActions) + actionsListModel.addElement(new JLabel(" "+s){public String toString() {return getText();}}); + // Environment Actions + actionsListModel.addElement(new JLabel("Environment Actions"){public String toString() {return getText();}}); + for(String s : eActions) + actionsListModel.addElement(new JLabel(" "+s){public String toString() {return getText();}}); + //Anesthesia Machine Actions + actionsListModel.addElement(new JLabel("Anesthesia Machine Actions"){public String toString() {return getText();}}); + for(String s : aActions) + actionsListModel.addElement(new JLabel(" "+s){public String toString() {return getText();}}); + //Inhaler Actions + actionsListModel.addElement(new JLabel("Inhaler Machine Actions"){public String toString() {return getText();}}); + for(String s : iActions) + actionsListModel.addElement(new JLabel(" "+s){public String toString() {return getText();}}); + scenarioGrid.add(actionsScroll, GUIContext.createGBC(1,5, 1,1, 1,1, GridBagConstraints.NORTHEAST, GridBagConstraints.BOTH, new Insets(5,0,0,5))); + // Active Actions + JPanel activeActionsPanel = new JPanel(new GridBagLayout()); + activeActionsListModel = new DefaultListModel(); + activeActionsList = new JList(activeActionsListModel); + activeActionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + activeActionsScroll = new JScrollPane(activeActionsList); + activeActionsScroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + setSize(activeActionsScroll,265, 250); + activeActionsPanel.add(activeActionsScroll, GUIContext.createGBC(0,0, 1,1, 1,1, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(0,0,0,5))); + JPanel engineButtonsPanel = new JPanel(new GridBagLayout()); + outputButton = new JButton("Output"); + outputButton.addActionListener(this); + engineButtonsPanel.add(outputButton, GUIContext.createGBC(0,0, 1,1, 1,1, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + executeButton = new JButton("Execute"); + executeButton.addActionListener(this); + engineButtonsPanel.add(executeButton, GUIContext.createGBC(0,1, 1,1, 1,1, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + resultsButton = new JButton("Results"); + resultsButton.addActionListener(this); + engineButtonsPanel.add(resultsButton, GUIContext.createGBC(0,2, 1,1, 1,1, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + activeActionsPanel.add(engineButtonsPanel, GUIContext.createGBC(1,0, 0.1,1, 1,1, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0,0,5,5))); + + + activeActionsControls = new JPanel(new FlowLayout(FlowLayout.LEFT)); + upAction = new BasicArrowButton(BasicArrowButton.NORTH); + upAction.addActionListener(this); + activeActionsControls.add(upAction); + addAction = new JButton("Add"); + addAction.addActionListener(this); + activeActionsControls.add(addAction); + editAction = new JButton("Edit"); + editAction.addActionListener(this); + activeActionsControls.add(editAction); + deleteAction = new JButton("Delete"); + deleteAction.addActionListener(this); + activeActionsControls.add(deleteAction); + downAction = new BasicArrowButton(BasicArrowButton.SOUTH); + downAction.addActionListener(this); + activeActionsControls.add(downAction); + activeActionsPanel.add(activeActionsControls, GUIContext.createGBC(0,1, 1,1, 1,1, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0,0,0,0))); + + scenarioGrid.add(activeActionsPanel, GUIContext.createGBC(2,5, 1,1, 2,1, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(5,0,0,0))); + + gui.add(scenarioGrid); + add(gui); + + subMgr = new SESubstanceManager(); + clear(); + } + + public void clear() + { + this.scenario = new SEScenario(subMgr); + this.scenarioName.setText("MyScenario"); + this.scenarioDescription.setText(""); + this.stateBtn.setSelected(true); + this.stateFiles.setSelectedItem(defaultState); + this.stateFiles.setEnabled(true); + this.patientFiles.setEnabled(false); + this.patientFiles.setSelectedIndex(-1); + + this.conditionsList.removeAll(); + this.conditionsListModel.removeAllElements(); + for(String s : allConditionNames) + conditionsListModel.addElement(new JLabel(s+" (Inactive)"){public String toString() {return getText();}}); + this.conditionsList.setEnabled(false); + this.editCondition.setEnabled(false); + this.clearCondition.setEnabled(false); + this.activeActionsList.removeAll(); + this.activeActionsListModel.removeAllElements(); + } + + public boolean loadScenarioFile(File file) + { + clear(); + Object obj = CDMSerializer.readFile(file.getAbsolutePath()); + if(obj instanceof ScenarioData) + { + if(!this.scenario.load((ScenarioData)obj)) + { + GUIContext.getConsole().println(file.getName()+" is not a valid scenario file"); + return false; + } + } + else + { + GUIContext.getConsole().println(file.getName()+" is not a scenario file"); + return false; + } + + this.scenarioName.setText(this.scenario.getName()); + this.scenarioDescription.setText(this.scenario.getDescription()); + + SEDataRequestManager drs = this.scenario.getDataRequests(); + if(!drs.hasFilename()) + { + String resultsFile = file.getAbsolutePath().replaceAll(".xml", "Results.txt"); + drs.setFilename(resultsFile); + } + + // Let's see if we can replace a patient file with a state file + if(this.scenario.hasInitialParameters() && this.scenario.getInitialParameters().getConditions().isEmpty()) + { + SEScenarioInitialParameters params = this.scenario.getInitialParameters(); + String pFile = params.getPatientFile(); + if(pFile.startsWith("./patients/")) + pFile = pFile.substring(10); + File stateFile = new File("./states/"+pFile.substring(0, pFile.indexOf(".xml"))+"@0s.xml"); + if(stateFile.exists()) + { + this.scenario.setEngineState(stateFile.getAbsolutePath()); + Log.info("I have replaced the patient file with the complementary engine state file"); + } + } + + if(this.scenario.hasInitialParameters()) + { + SEScenarioInitialParameters params = this.scenario.getInitialParameters(); + String pFile = params.getPatientFile(); + if(pFile.startsWith("./patients/")) + pFile = pFile.substring(10); + this.patientFiles.setSelectedItem(pFile); + + SEPatient patient = this.patients.get(pFile); + if (patient == null) + { + patient = new SEPatient(); + patient.loadPatientFile("./patients/"+pFile); + this.patients.put(pFile, patient); + } + + this.patientBtn.setSelected(true); + this.patientFiles.setEnabled(true); + this.stateFiles.setSelectedIndex(-1); + this.stateFiles.setEnabled(false); + this.conditionsList.setEnabled(true); + this.editCondition.setEnabled(true); + this.clearCondition.setEnabled(true); + this.patientFiles.setSelectedItem(pFile); + + this.conditionsList.removeAll(); + this.conditionsListModel.removeAllElements(); + for(String s : allConditionNames) + conditionsListModel.addElement(new JLabel(s+" (Inactive)"){public String toString() {return getText();}}); + + for (SECondition c : params.getConditions()) + { + String name = StringUtils.spaceCamelCase(c.getClass().getSimpleName()); + conditionsList.setSelectedIndex(allConditionNames.indexOf(name)); + UpdateModelContent(c.toString(), conditionsListModel, conditionsList); + } + } + else if(this.scenario.hasEngineState()) + { + this.stateBtn.setSelected(true); + + String sFile = this.scenario.getEngineState(); + if(sFile.startsWith("./states/")) + sFile = sFile.substring(9); + this.patientFiles.setSelectedItem(sFile); + } + + this.activeActionsList.removeAll(); + this.activeActionsListModel.removeAllElements(); + for (SEAction action : this.scenario.getActions()) + AddAction(action); + UpdateActionTimes(); + + return true; + } + + public boolean saveScenarioFileAs(String fileName) + { + this.scenario.setName(this.scenarioName.getText()); + this.scenario.setDescription(this.scenarioDescription.getText()); + CDMSerializer.writeFile(fileName, this.scenario.unload()); + return true; + } + + public List getXMLFilenameList(File dir) + { + List files = new ArrayList(); + if(!dir.exists()) + { + Log.error("XMLFilenameList cannot find directory : "+dir.getAbsolutePath()); + return files; + } + for (final File file : dir.listFiles()) + if (file.getName().endsWith(".xml")) + files.add(file.getName()); + return files; + } + + public void actionPerformed(ActionEvent e) + { + Object src = e.getSource(); + + if(src == this.stateBtn) + { + this.stateFiles.setEnabled(true); + this.stateFiles.setSelectedItem(defaultState); + + this.patientFiles.setEnabled(false); + this.patientFiles.setSelectedIndex(-1); + + this.conditionsList.removeAll(); + this.conditionsListModel.removeAllElements(); + for(String s : allConditionNames) + conditionsListModel.addElement(new JLabel(s+" (Inactive)"){public String toString() {return getText();}}); + this.conditionsList.setEnabled(false); + this.editCondition.setEnabled(false); + this.clearCondition.setEnabled(false); + return; + } + else if(src == this.stateFiles) + { + if(this.stateFiles.getSelectedIndex()==-1) + return; + this.scenario.invalidateInitialParameters(); + this.scenario.setEngineState("./states/"+this.stateFiles.getSelectedItem()); + return; + } + else if(src == this.patientBtn) + { + this.patientFiles.setEnabled(true); + this.stateFiles.setSelectedIndex(-1); + this.stateFiles.setEnabled(false); + this.conditionsList.setEnabled(true); + this.editCondition.setEnabled(true); + this.clearCondition.setEnabled(true); + this.patientFiles.setSelectedItem(defaultPatient); + return; + } + else if(src == this.patientFiles) + { + if(this.patientFiles.getSelectedIndex()==-1) + return; + this.scenario.invalidateEngineState(); + this.scenario.getInitialParameters().setPatientFile((String)this.patientFiles.getSelectedItem()); + return; + } + else if(src == this.editCondition) + { + if(conditionsList.getSelectedIndex()==-1) + return; + String name = allConditionNames.get(conditionsList.getSelectedIndex()); + SECondition c = allConditions.get(name); + DynamicPropertyEditor dlg = new DynamicPropertyEditor(c, null); + dlg.setVisible(true); + if(!dlg.wasCancelled()) + { + if(!this.scenario.getInitialParameters().getConditions().contains(c)) + this.scenario.getInitialParameters().getConditions().add(c); + UpdateModelContent(c.toString(), conditionsListModel, conditionsList); + } + return; + } + else if(src == this.clearCondition) + { + if(conditionsList.getSelectedIndex()==-1) + return; + String name = allConditionNames.get(conditionsList.getSelectedIndex()); + SECondition c = allConditions.get(name); + this.scenario.getInitialParameters().getConditions().remove(c); + UpdateModelContent(name+" (Inactive)", conditionsListModel, conditionsList); + return; + } + + else if (src == this.addAction) + { + String actionName = actionsList.getSelectedValue().getText().trim(); + if(actionName == null || actionName.indexOf("Actions")>-1) + return; + + SEAction action = null; + for(Class c : allActions) + { + try + { + if(c.getSimpleName().equals("SE"+StringUtils.removeSpaces(actionName))) + { + action = (SEAction) c.newInstance(); + break; + } + } + catch(Exception ex) + { + try + { + Constructor[] ctors = c.getConstructors(); + for(Constructor ctor : ctors) + { + if(ctor.getParameterTypes()[0]==SESubstance.class) + { + SESubstance sub = new SESubstance(); + action = (SEAction)ctor.newInstance(sub); + break; + } + else if(ctor.getParameterTypes()[0]==SESubstanceCompound.class) + { + SESubstanceCompound cmpd = new SESubstanceCompound(); + action = (SEAction)ctor.newInstance(cmpd); + break; + } + } + } + catch(Exception ex2) + { + + } + } + } + if(action != null) + { + boolean cancelled=true; + if(SEConsciousRespiration.class.isAssignableFrom(action.getClass())) + { + ConsciousRespirationEditor dlg = new ConsciousRespirationEditor((SEConsciousRespiration)action, actionPropertyOptions.get(action.getClass())); + dlg.setVisible(true); + cancelled = dlg.wasCancelled(); + } + else + { + DynamicPropertyEditor dlg = new DynamicPropertyEditor(action, actionPropertyOptions.get(action.getClass())); + dlg.setVisible(true); + cancelled = dlg.wasCancelled(); + } + + if(!cancelled) + { + AddAction(action); + this.scenario.getActions().add(action); + } + } + else + { + GUIContext.getConsole().println("I am not sure what kind of action "+actionName+" is"); + return; + } + } + else if (src == this.deleteAction) + { + int selectedIndex = activeActionsList.getSelectedIndex(); + if(selectedIndex > -1) + { + activeActionsListModel.remove(selectedIndex); + this.scenario.getActions().remove(selectedIndex); + + if (selectedIndex >= activeActionsListModel.getSize()) + activeActionsList.setSelectedIndex(activeActionsListModel.getSize() - 1); + else + activeActionsList.setSelectedIndex(selectedIndex); + } + return; + } + else if (src == this.editAction) + { + int selectedIndex = activeActionsList.getSelectedIndex(); + if(selectedIndex > -1) + { + SEAction action = this.scenario.getActions().get(selectedIndex); + + if(SEConsciousRespiration.class.isAssignableFrom(action.getClass())) + { + ConsciousRespirationEditor dlg = new ConsciousRespirationEditor((SEConsciousRespiration)action, actionPropertyOptions.get(action.getClass())); + dlg.setVisible(true); + } + else + { + DynamicPropertyEditor dlg = new DynamicPropertyEditor(action, actionPropertyOptions.get(action.getClass())); + dlg.setVisible(true); + } + UpdateModelContent(this.scenario.getActions().get(selectedIndex).toString(), activeActionsListModel, activeActionsList); + } + return; + } + else if (src == this.upAction) + { + if (activeActionsList.getSelectedIndex() > 0) + { + int fromIndex = activeActionsList.getSelectedIndex(); + int toIndex = fromIndex - 1; + + SEAction aTemp = this.scenario.getActions().get(fromIndex); + this.scenario.getActions().set(fromIndex, this.scenario.getActions().get(toIndex)); + this.scenario.getActions().set(toIndex, aTemp); + + JLabel temp = (JLabel) activeActionsListModel.get(fromIndex); + activeActionsListModel.set(fromIndex, activeActionsListModel.get(toIndex)); + activeActionsListModel.set(toIndex, temp); + + activeActionsList.setSelectedIndex(toIndex); + } + } + else if (src == this.downAction) + { + if (activeActionsList.getSelectedIndex() < activeActionsList.getModel().getSize() - 1) + { + int fromIndex = activeActionsList.getSelectedIndex(); + int toIndex = fromIndex + 1; + + SEAction aTemp = this.scenario.getActions().get(fromIndex); + this.scenario.getActions().set(fromIndex, this.scenario.getActions().get(toIndex)); + this.scenario.getActions().set(toIndex, aTemp); + + JLabel temp = (JLabel) activeActionsListModel.get(fromIndex); + activeActionsListModel.set(fromIndex, activeActionsListModel.get(toIndex)); + activeActionsListModel.set(toIndex, temp); + + activeActionsList.setSelectedIndex(toIndex); + } + } + else if (src == this.executeButton) + { + if (this.scenario.getDataRequests().getRequestedData().isEmpty()) + { + JOptionPane.showMessageDialog(this, "No data is requested for the current scenario", "No requested data", JOptionPane.ERROR_MESSAGE); + return; + } + if (this.scenario.getActions().isEmpty()) + { + JOptionPane.showMessageDialog(this, "No actions in scenario", "No requested data", JOptionPane.ERROR_MESSAGE); + return; + } + if (!this.scenario.hasEngineState() && !this.scenario.hasInitialParameters()) + { + JOptionPane.showMessageDialog(this, "No patient data is set for the current scenario", "No requested data", JOptionPane.ERROR_MESSAGE); + return; + } + this.scenario.setName(scenarioName.getText()); + this.scenario.setDescription(scenarioDescription.getText()); + SEDataRequestManager drs = this.scenario.getDataRequests(); + if(!drs.hasFilename()) + drs.setFilename("./"+this.scenario.getName()+"Results.txt"); + ScenarioCalcDialog dlg = new ScenarioCalcDialog(this.scenario); + dlg.setVisible(true); + return; + } + else if (src == this.resultsButton) + { + if (!this.scenario.getDataRequests().hasFilename()) + { + JOptionPane.showMessageDialog(this, "No results file has been specified", "No requested data", JOptionPane.ERROR_MESSAGE); + return; + } + String resultsFile = scenario.getDataRequests().getFilename().replaceAll("[\\\\]", "/"); + File resultsDir = new File(resultsFile.substring(0,resultsFile.lastIndexOf("/"))+"/"+scenario.getName()+"Graphs"); + + if (resultsDir.exists() && resultsDir.isDirectory()) + new ResultsWindow(resultsDir); + else + JOptionPane.showMessageDialog(this, "\""+resultsDir.getAbsolutePath()+"\" does not exist or is not a directory", "No Results Found", JOptionPane.ERROR_MESSAGE); + return; + } + else if (src == this.outputButton) + { + if (this.scenario == null) + { + JOptionPane.showMessageDialog(this, "No scenario is currently loaded", "No Scenario", JOptionPane.ERROR_MESSAGE); + return; + } + + new DataRequestWindow(scenario); + return; + } + + } + + private void UpdateActionTimes() + { + double totalTime_s = 0; + for (SEAction action : this.scenario.getActions()) + { + if (action instanceof SEAdvanceTime) + totalTime_s += ((SEAdvanceTime)action).getTime().getValue(CommonUnits.TimeUnit.s); + action.getScenarioTime().setValue(totalTime_s, TimeUnit.s); + } + } + private void AddAction(SEAction action) + { + String actionContent = action.toString(); + if (action instanceof SEAdvanceTime) + { + actionContent += " (Scenario time:" + (int)action.getScenarioTime().getValue(TimeUnit.s) + "s)"; + } + AddContentToModel(actionContent,activeActionsListModel); + } + private void AddContentToModel(String content, DefaultListModel list) + { + final String formattedAction; + + int firstNewlineTabIndex = content.indexOf("\n\t"); + content = content.replace("\n","
"); + content = content.replace("\t","..."); + if (firstNewlineTabIndex != -1) + { + String boldedActionName = new StringBuilder(content).insert(firstNewlineTabIndex, "").toString(); + formattedAction = ("" + boldedActionName + "
"); + } + else + { + formattedAction = "" + content + ""; + } + list.addElement(new JLabel(formattedAction){public String toString() {return getText();}}); + } + + private void UpdateModelContent(String content, DefaultListModel model, JList list) + { + content = content.replace("\n","
"); + content = content.replace("\t","..."); + final String formattedContent; + int firstBreakIndex = content.indexOf("
"); + if (firstBreakIndex != -1) + { + String boldedName = new StringBuilder(content).insert(firstBreakIndex, "
").toString(); + formattedContent = ("" + boldedName + "
"); + } + else if(content.indexOf("(Inactive)")>-1) + { + formattedContent = "" + content + ""; + } + else + { + formattedContent = "" + content + ""; + } + int idx = list.getSelectedIndex(); + model.insertElementAt(new JLabel(formattedContent){public String toString() {return getText();}}, idx); + model.removeElementAt(idx+1); + list.repaint(); + } +} diff --git a/src/schema/xsd/biogears/BioGearsConfiguration.xsd b/src/schema/xsd/biogears/BioGearsConfiguration.xsd index 01a0d63..ff1a2e2 100644 --- a/src/schema/xsd/biogears/BioGearsConfiguration.xsd +++ b/src/schema/xsd/biogears/BioGearsConfiguration.xsd @@ -113,7 +113,6 @@ - diff --git a/src/schema/xsd/biogears/BioGearsPhysiology.xsd b/src/schema/xsd/biogears/BioGearsPhysiology.xsd index 3d4331a..2d9c599 100644 --- a/src/schema/xsd/biogears/BioGearsPhysiology.xsd +++ b/src/schema/xsd/biogears/BioGearsPhysiology.xsd @@ -76,6 +76,8 @@ specific language governing permissions and limitations under the License. + + @@ -94,12 +96,7 @@ specific language governing permissions and limitations under the License. - - - - - - + @@ -215,12 +212,11 @@ specific language governing permissions and limitations under the License. - - - - + + + diff --git a/src/schema/xsd/cdm/Compartment.xsd b/src/schema/xsd/cdm/Compartment.xsd index eb11ffd..068a055 100644 --- a/src/schema/xsd/cdm/Compartment.xsd +++ b/src/schema/xsd/cdm/Compartment.xsd @@ -149,6 +149,7 @@ specific language governing permissions and limitations under the License. + diff --git a/src/schema/xsd/cdm/Patient.xsd b/src/schema/xsd/cdm/Patient.xsd index 28ec868..fc65e01 100644 --- a/src/schema/xsd/cdm/Patient.xsd +++ b/src/schema/xsd/cdm/Patient.xsd @@ -40,11 +40,19 @@ specific language governing permissions and limitations under the License. + + + + + + + + @@ -58,6 +66,7 @@ specific language governing permissions and limitations under the License. + @@ -96,6 +105,7 @@ specific language governing permissions and limitations under the License. + diff --git a/src/schema/xsd/cdm/PatientActions.xsd b/src/schema/xsd/cdm/PatientActions.xsd index 6186b6d..714207b 100644 --- a/src/schema/xsd/cdm/PatientActions.xsd +++ b/src/schema/xsd/cdm/PatientActions.xsd @@ -252,8 +252,9 @@ specific language governing permissions and limitations under the License. - - + + + diff --git a/src/schema/xsd/cdm/PatientConditions.xsd b/src/schema/xsd/cdm/PatientConditions.xsd index f4e9fc0..b1a05b3 100644 --- a/src/schema/xsd/cdm/PatientConditions.xsd +++ b/src/schema/xsd/cdm/PatientConditions.xsd @@ -80,22 +80,42 @@ specific language governing permissions and limitations under the License. - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -120,5 +140,16 @@ specific language governing permissions and limitations under the License. + + + + + + + + + + + \ No newline at end of file diff --git a/src/schema/xsd/cdm/PatientNutrition.xsd b/src/schema/xsd/cdm/PatientNutrition.xsd index 65edb30..215d6df 100644 --- a/src/schema/xsd/cdm/PatientNutrition.xsd +++ b/src/schema/xsd/cdm/PatientNutrition.xsd @@ -34,16 +34,4 @@ specific language governing permissions and limitations under the License. - - - - - - - - - - - - diff --git a/src/schema/xsd/cdm/Physiology.xsd b/src/schema/xsd/cdm/Physiology.xsd index cdabf6a..0f266ad 100644 --- a/src/schema/xsd/cdm/Physiology.xsd +++ b/src/schema/xsd/cdm/Physiology.xsd @@ -57,6 +57,7 @@ specific language governing permissions and limitations under the License. + @@ -178,13 +179,15 @@ specific language governing permissions and limitations under the License. - - + - + + + + @@ -214,7 +217,8 @@ specific language governing permissions and limitations under the License. - + + @@ -354,10 +358,12 @@ specific language governing permissions and limitations under the License. + - + + diff --git a/src/schema/xsd/cdm/Substance.xsd b/src/schema/xsd/cdm/Substance.xsd index 7af1140..92c477b 100644 --- a/src/schema/xsd/cdm/Substance.xsd +++ b/src/schema/xsd/cdm/Substance.xsd @@ -52,7 +52,15 @@ specific language governing permissions and limitations under the License. - + + + + + + + + + + - + + diff --git a/src/sdk/howto/cpp/BioGearsEngineHowTo.cpp b/src/sdk/howto/cpp/BioGearsEngineHowTo.cpp index 5871f81..3a7a1f1 100644 --- a/src/sdk/howto/cpp/BioGearsEngineHowTo.cpp +++ b/src/sdk/howto/cpp/BioGearsEngineHowTo.cpp @@ -16,29 +16,33 @@ specific language governing permissions and limitations under the License. #include "scenario/requests/SEDataRequest.h" #include "properties/SEScalarTime.h" + int main() { // Uncomment a method to execute fuctionality! - //HowToEngineUse(); - //HowToCreateAPatient(); - - //HowToAirwayObstruction(); - //HowToAnesthesiaMachine(); - //HowToAsthmaAttack(); - //HowToBrainInjury(); - //HowToBolusDrug(); - //HowToConsumeNutrients(); - //HowToCOPD(); - //HowToCPR(); - //HowToEnvironmentChange(); - //HowToExercise(); - //HowToHemorrhage(); - //HowToLobarPneumonia(); - HowToMechanicalVentilation(); - //HowToPulmonaryFunctionTest(); - //HowToSmoke(); - //HowToTensionPneumothorax(); +//HowToEngineUse(); +//HowToCreateAPatient(); +//HowToAirwayObstruction(); +//HowToAnesthesiaMachine(); +//HowToAsthmaAttack(); +//HowToBrainInjury(); +//HowToBolusDrug(); +//HowToConsumeNutrients(); +//HowToCOPD(); +//HowToCPR(); +//HowToEnvironmentChange(); +//HowToExercise(); +//HowToFasciculation(); +//HowToHemorrhage(); +//HowToInfusionDrug(); +//HowToLobarPneumonia(); +//HowToMechanicalVentilation(); +//HowToPulmonaryFunctionTest(); +//HowToSarinExposure(); +//HowToSmoke(); +//HowToTensionPneumothorax(); +HowToVasopressinShockTherapy(); // This one does not really run, is a pure example //HowToRunScenario(); diff --git a/src/sdk/howto/cpp/BioGearsEngineHowTo.h b/src/sdk/howto/cpp/BioGearsEngineHowTo.h index 1d32475..bb3fc1c 100644 --- a/src/sdk/howto/cpp/BioGearsEngineHowTo.h +++ b/src/sdk/howto/cpp/BioGearsEngineHowTo.h @@ -31,12 +31,16 @@ void HowToCOPD(); void HowToCPR(); void HowToEnvironmentChange(); void HowToExercise(); +void HowToFasciculation(); void HowToHemorrhage(); +void HowToInfusionDrug(); void HowToLobarPneumonia(); void HowToMechanicalVentilation(); void HowToPulmonaryFunctionTest(); +void HowToSarinExposure(); void HowToSmoke(); void HowToTensionPneumothorax(); +void HowToVasopressinShockTherapy(); void HowToConcurrentEngines(); void HowToRunScenario(); diff --git a/src/sdk/howto/cpp/HowTo-BolusDrug.cpp b/src/sdk/howto/cpp/HowTo-BolusDrug.cpp index 8e05c10..1ec812e 100644 --- a/src/sdk/howto/cpp/HowTo-BolusDrug.cpp +++ b/src/sdk/howto/cpp/HowTo-BolusDrug.cpp @@ -15,10 +15,12 @@ specific language governing permissions and limitations under the License. // Include the various types you will be using in your code #include "utils/SEEventHandler.h" #include "patient/actions/SESubstanceBolus.h" +#include "patient/actions/SESubstanceInfusion.h" #include "system/physiology/SEBloodChemistrySystem.h" #include "system/physiology/SECardiovascularSystem.h" #include "system/physiology/SEEnergySystem.h" #include "system/physiology/SERespiratorySystem.h" +#include "system/physiology/SEDrugSystem.h" #include "substance/SESubstanceManager.h" #include "patient/SEPatient.h" #include "properties/SEScalarFraction.h" @@ -28,9 +30,10 @@ specific language governing permissions and limitations under the License. #include "properties/SEScalarTemperature.h" #include "properties/SEScalarTime.h" #include "properties/SEScalarVolume.h" +#include "properties/SEScalarMass.h" #include "properties/SEScalarVolumePerTime.h" +#include "properties/SEScalarOsmolality.h" #include "engine/PhysiologyEngineTrack.h" -#include "compartment/SECompartmentManager.h" //-------------------------------------------------------------------------------------------------- /// \brief diff --git a/src/sdk/howto/cpp/HowTo-Fasciculation.cpp b/src/sdk/howto/cpp/HowTo-Fasciculation.cpp new file mode 100644 index 0000000..30086e5 --- /dev/null +++ b/src/sdk/howto/cpp/HowTo-Fasciculation.cpp @@ -0,0 +1,106 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +#include "BioGearsEngineHowTo.h" + +// Include the various types you will be using in your code +#include "patient/actions/SEExercise.h" +#include "patient/SEPatient.h" +#include "system/physiology/SEBloodChemistrySystem.h" +#include "system/physiology/SECardiovascularSystem.h" +#include "system/physiology/SEEnergySystem.h" +#include "system/physiology/SERespiratorySystem.h" +#include "substance/SESubstanceManager.h" +#include "substance/SESubstanceCompound.h" +#include "properties/SEScalarAmountPerVolume.h" +#include "properties/SEScalarFraction.h" +#include "properties/SEScalarFrequency.h" +#include "properties/SEScalarMass.h" +#include "properties/SEScalarMassPerVolume.h" +#include "properties/SEScalarPressure.h" +#include "properties/SEScalarTemperature.h" +#include "properties/SEScalarTime.h" +#include "properties/SEScalarVolume.h" +#include "properties/SEScalarVolumePerTime.h" +#include "properties/SEScalarPower.h" +#include "properties/SEScalar0To1.h" +#include "engine/PhysiologyEngineTrack.h" +#include "compartment/SECompartmentManager.h" + +//-------------------------------------------------------------------------------------------------- +/// \brief +/// Demonstrates how to set concentrations of ionic compounds in bloodstream and tissue and call events +/// based on changes in concentrations +/// +/// \details +/// Refer to the SEEnvironmentChange class +/// Refer to the SEDrug Class +//-------------------------------------------------------------------------------------------------- + +void HowToFasciculation() +{ + // Create the engine and load the patient + std::unique_ptr bg = CreateBioGearsEngine("HowToFasciculation.log"); + bg->GetLogger()->Info("HowToFasciculation"); + + if (!bg->LoadState("./states/StandardMale@0s.xml")) + { + bg->GetLogger()->Error("Could not load state, check the error"); + return; + } + + //---Initialize all variables needed for scenario + SESubstance* Na = bg->GetSubstanceManager().GetSubstance("Sodium"); + SESubstance* K = bg->GetSubstanceManager().GetSubstance("Potassium"); + SESubstance* Cl = bg->GetSubstanceManager().GetSubstance("Chloride"); + SESubstance* Ca = bg->GetSubstanceManager().GetSubstance("Calcium"); + + double monitorTime = 200.0; //how long we're going to be on the look out for ion imbalances in the blood + CDM::enumOnOff::value lowKActive; + lowKActive = CDM::enumOnOff::Off; + + // The tracker is responsible for advancing the engine time and outputting the data requests below at each time step + HowToTracker tracker(*bg); + + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("VenaCava", *Na, "Molarity", AmountPerVolumeUnit::mmol_Per_L); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("VenaCava", *K, "Molarity", AmountPerVolumeUnit::mmol_Per_L); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("VenaCava", *Cl, "Molarity", AmountPerVolumeUnit::mmol_Per_L); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("VenaCava", *Ca, "Molarity", AmountPerVolumeUnit::mmol_Per_L); + + bg->GetEngineTrack()->GetDataRequestManager().SetResultsFilename("HowToFasciculation.txt"); + + // Advance some time to get some resting data + tracker.AdvanceModelTime(60); + + bg->GetLogger()->Info("The patient is nice and healthy"); + std::string message = ""; + message = "Increase membrane resistance to potassium"; + bg->GetLogger()->Info(message); + K->GetMembraneResistance().SetValue(5.0, ElectricResistanceUnit::Ohm); + + + while (bg->GetSimulationTime(TimeUnit::s) < monitorTime + 60.0) + { + if (bg->GetPatient().IsEventActive(CDM::enumPatientEvent::MildHypokalemia) && (lowKActive == CDM::enumOnOff::Off)) + { + lowKActive = CDM::enumOnOff::On; + message = "Patient has low serum potassium, muscle fasciculation may occur"; + bg->GetLogger()->Info(message); + } + tracker.AdvanceModelTime(10.0); + } + message = "Return membrane resistance to potassium to baseline"; + bg->GetLogger()->Info(message); + K->GetMembraneResistance().SetValue(0.248, ElectricResistanceUnit::Ohm); + + tracker.AdvanceModelTime(300); +} diff --git a/src/sdk/howto/cpp/HowTo-Hemorrhage.cpp b/src/sdk/howto/cpp/HowTo-Hemorrhage.cpp index 4f70b25..c28be5e 100644 --- a/src/sdk/howto/cpp/HowTo-Hemorrhage.cpp +++ b/src/sdk/howto/cpp/HowTo-Hemorrhage.cpp @@ -30,18 +30,21 @@ specific language governing permissions and limitations under the License. #include "properties/SEScalarTime.h" #include "properties/SEScalarVolume.h" #include "properties/SEScalarVolumePerTime.h" +#include "properties/SEScalar0To1.h" #include "engine/PhysiologyEngineTrack.h" #include "compartment/SECompartmentManager.h" //-------------------------------------------------------------------------------------------------- /// \brief -/// Usage for applying a Hemorrhage insult to the patient +/// Usage for applying a Hemorrhage insult to the patient and also demonstrate how injury code can be used to initiate a hemorrhage if desired /// /// \details /// Refer to the SEHemorrhage class /// Refer to the SESubstanceManager class /// Refer to the SESubstanceIVFluids class for applying an IV to the patient //-------------------------------------------------------------------------------------------------- +std::string ParseMCIS(std::vector &mcis); + void HowToHemorrhage() { // Create the engine and load the patient @@ -78,26 +81,35 @@ void HowToHemorrhage() bg->GetLogger()->Info(std::stringstream() <<"Diastolic Pressure : " << bg->GetCardiovascularSystem()->GetDiastolicArterialPressure(PressureUnit::mmHg) << PressureUnit::mmHg); bg->GetLogger()->Info(std::stringstream() <<"Heart Rate : " << bg->GetCardiovascularSystem()->GetHeartRate(FrequencyUnit::Per_min) << "bpm");; - // Hemorrhage Starts - instantiate a hemorrhage action and have the engine process it - /*MCIS Code Brief : - Digit 1 = Severity - Digit 2 = Body Region(1 = Head, 2 = Torso, 3 = Arms, 4 = Legs, 5 = Multiple(not currently supported) - Digit 3 = Subregion(not requied for arms or legs) - In Head : 6 = Vessels - In Torso : 6 = Vessels, 7 = Chest, 8 = Abdomen, 9 = Pelvis(not currently supported) - Digit 4 - In Vessels : 1 = Intracranial, 3 - 5 = Carotid / Thoracic / Abdominal arteries(all currently removed from aorta compartment), 6 = VenaCava - In Chest : 1 = Lungs, 2 = Heart In Abdomen : 1 = Liver, 2 = Spleen, 3 = Pancreas(Splanchnic), 4 = Kidney, 5 = SmallIntestine, 6 = LargeIntestine - Digit 5 : Wound information too specific for BioGears(any number fine, 0 used here) - - Stopping a hemorrhage requires Severity = 0 and remainder of code consistent with original wound*/ + //We are going to create a hemorrhage in two different ways. One way will be to specify the location and a severity on a scale of 0-1. + //The other way will be to parse an injury code and derive the location and severity + //Set the choice variable below either to 1 to run with location/severity or to 2 to run with injury code + int choice = 2; - SEHemorrhage hemorrhageAbdominal; - std::vector hemorrhageStart = {4,2,6,5,0 }; - hemorrhageAbdominal.SetMCIS(hemorrhageStart); - hemorrhageAbdominal.ProcessMCIS(); //Extracts the injury severity and injury location from the mcis code + //Create variables for scenario + SEHemorrhage hemorrhageSpleen; //hemorrhage object + std::string location; //location of hemorrhage, valid options are "Major Artery", "Vena Cava", "Head", "Myocardium", "Lung", "Spleen", "Splanchnic", "Small Intestine", "Large Intestine", "Kidney", "Liver", "Arm", "Leg" + double severity; //severity (scale 0-1) + std::vector mcisCode; //injury code if using option 2, see ParseMCIS method below for more details + //Let's create an internal hemorrhage in the spleen (maybe it ruptured...) + switch (choice) { + case 1: + location = "Spleen"; + severity = 0.8; + break; + case 2: + mcisCode = { 4,2,8,2,0 }; //This injury code is a high severity hemorrhage in the spleen + severity = mcisCode[0] / 5.0; //Digit 1 of mcis is severity on 0-5 scale. Convert it to a 0-1 scale + location = ParseMCIS(mcisCode); + break; + } + //Set up hemorrhage with the location and severity info + hemorrhageSpleen.SetCompartment(location); + hemorrhageSpleen.GetSeverity().SetValue(severity); + hemorrhageSpleen.SetBleedPath(); //This is needed to tell engine which circuit pathway to set the hemorrhage on - bg->ProcessAction(hemorrhageAbdominal); + // Hemorrhage Starts - instantiate a hemorrhage action and have the engine process it. Note that BioGears will output the injury code regardless of which method was used + bg->ProcessAction(hemorrhageSpleen); // Advance some time to let the body bleed out a bit tracker.AdvanceModelTime(300); @@ -111,11 +123,10 @@ void HowToHemorrhage() bg->GetLogger()->Info(std::stringstream() <<"Diastolic Pressure : " << bg->GetCardiovascularSystem()->GetDiastolicArterialPressure(PressureUnit::mmHg) << PressureUnit::mmHg); bg->GetLogger()->Info(std::stringstream() <<"Heart Rate : " << bg->GetCardiovascularSystem()->GetHeartRate(FrequencyUnit::Per_min) << "bpm");; - // Hemorrhage is sealed - std::vector hemorrhageEnd = { 0,2,6,5,0 }; - hemorrhageAbdominal.SetMCIS(hemorrhageEnd); - hemorrhageAbdominal.ProcessMCIS(); - bg->ProcessAction(hemorrhageAbdominal); + //Assume that the hemorrhage has been stopped somehow. We do this by setting the severity of our hemorrhage object to 0 + hemorrhageSpleen.GetSeverity().SetValue(0); + //Process update to hemorrhage action + bg->ProcessAction(hemorrhageSpleen); // Advance some time while the medic gets the drugs ready @@ -151,3 +162,57 @@ void HowToHemorrhage() bg->GetLogger()->Info(std::stringstream() <<"Heart Rate : " << bg->GetCardiovascularSystem()->GetHeartRate(FrequencyUnit::Per_min) << "bpm");; bg->GetLogger()->Info("Finished"); } + +std::string ParseMCIS(std::vector &mcis) +{ + /*MCIS Code Brief : + Digit 1 = Severity + Digit 2 = Body Region(1 = Head, 2 = Torso, 3 = Arms, 4 = Legs, 5 = Multiple(not currently supported) + Digit 3 = Subregion(not required for arms or legs) + In Head : 6 = Vessels + In Torso : 6 = Vessels, 7 = Chest, 8 = Abdomen, 9 = Pelvis(not currently supported) + Digit 4 + In Vessels : 1 = Intracranial (if in head), 4 = Major Artery (if in torso), 6 = "Vena Cava" (if in torso) + In Chest : 1 = Lungs, 2 = Heart In Abdomen : 1 = Liver, 2 = Spleen, 3 = Pancreas(Splanchnic), 4 = Kidney, 5 = SmallIntestine, 6 = LargeIntestine + Digit 5 : Wound information too specific for BioGears(any number fine, 0 used here) */ + + std::string comp = ""; + enum region {Head =1, Torso = 2, Arm = 3, Leg = 4}; //This will decide which region to look for compartment based on digit 2 of code + std::map, std::string> torsoMap; //There are so many compartments in the torso, it is easier to map them + //Populate torso map (codes with second digit = 2) so that digits 3-4 of code are key to correct compartment + torsoMap[{6, 4}] = "Major Artery"; + torsoMap[{6, 6}] = "Vena Cava"; + torsoMap[{7, 1}] = "Lung"; + torsoMap[{7, 2}] = "Myocardium"; + torsoMap[{8, 1}] = "Liver"; + torsoMap[{8, 2}] = "Spleen"; + torsoMap[{8, 3}] = "Splanchnic"; + torsoMap[{8, 4}] = "Kidney"; + torsoMap[{8, 5}] = "Small Intestine"; + torsoMap[{8, 6}] = "Large Intestine"; + + int caseKey = mcis[1]; //Need 2nd digit of mcis code to decide in which region to place hemorrhage + + switch (caseKey) { + case Head: + comp = "Head"; + break; + case Torso: + if (torsoMap.find({ mcis.begin() + 2,mcis.end() - 1 }) != torsoMap.end()) //Check to see if subvector made from digits 3-4 is in map. + comp = torsoMap[{mcis.begin() + 2, mcis.end() - 1}]; // If yes, get compartment that goes with these digits + else + comp = "Major Artery"; //If no, we messed up somewhere and we'll put it on the artery so that the sim doesn't crash + break; + case Arm: + comp = "Arm"; + break; + case Leg: + comp = "Leg"; + break; + default: + comp = "Major Artery"; //Default to artery in case anything goes wrong + } + + + return comp; +} \ No newline at end of file diff --git a/src/sdk/howto/cpp/HowTo-InfusionDrug.cpp b/src/sdk/howto/cpp/HowTo-InfusionDrug.cpp new file mode 100644 index 0000000..160c82a --- /dev/null +++ b/src/sdk/howto/cpp/HowTo-InfusionDrug.cpp @@ -0,0 +1,113 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +#include "BioGearsEngineHowTo.h" + +// Include the various types you will be using in your code +#include "utils/SEEventHandler.h" +#include "patient/actions/SESubstanceBolus.h" +#include "patient/actions/SESubstanceInfusion.h" +#include "system/physiology/SEBloodChemistrySystem.h" +#include "system/physiology/SECardiovascularSystem.h" +#include "system/physiology/SEEnergySystem.h" +#include "system/physiology/SERespiratorySystem.h" +#include "system/physiology/SEDrugSystem.h" +#include "substance/SESubstanceManager.h" +#include "patient/SEPatient.h" +#include "properties/SEScalarFraction.h" +#include "properties/SEScalarFrequency.h" +#include "properties/SEScalarMassPerVolume.h" +#include "properties/SEScalarPressure.h" +#include "properties/SEScalarTemperature.h" +#include "properties/SEScalarTime.h" +#include "properties/SEScalarVolume.h" +#include "properties/SEScalarVolumePerTime.h" +#include "properties/SEScalarOsmolality.h" +#include "engine/PhysiologyEngineTrack.h" +#include "compartment/SECompartmentManager.h" + +//-------------------------------------------------------------------------------------------------- +/// \brief +/// Usage for adminstering a substance to the patient via a continuous infusion +/// +/// \details +/// Refer to the SESubstanceInfusion class +/// Refer to the SESubstanceManager class +//-------------------------------------------------------------------------------------------------- +void HowToInfusionDrug() +{ + // Create the engine and load the patient + std::unique_ptr bg = CreateBioGearsEngine("VasopressinPD.log"); + bg->GetLogger()->Info("VasopressinPD"); + if (!bg->LoadState("StandardMale.xml")) + { + bg->GetLogger()->Error("Could not load state, check the error"); + return; + } + + // The tracker is responsible for advancing the engine time and outputting the data requests below at each time step + HowToTracker tracker(*bg); + + enum PDCase { LowDose,HighDose,RampDose }; + + SESubstance* vas = bg->GetSubstanceManager().GetSubstance("Vasopressin"); + vas->GetPlasmaConcentration().SetValue(0.0, MassPerVolumeUnit::ug_Per_L); + SESubstanceInfusion infuse(*vas); + + // Create data requests for each value that should be written to the output log as the engine is executing + // Physiology System Names are defined on the System Objects + // defined in the Physiology.xsd file + bg->GetEngineTrack()->GetDataRequestManager().CreateSubstanceDataRequest().Set(*vas, "PlasmaConcentration", MassPerVolumeUnit::ug_Per_L); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("HeartRate", FrequencyUnit::Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("SystolicArterialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("DiastolicArterialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("MeanArterialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("CardiacOutput", VolumePerTimeUnit::L_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("UrineProductionRate", VolumePerTimeUnit::mL_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("UrineOsmolarity", OsmolalityUnit::mOsm_Per_kg); + + bg->GetEngineTrack()->GetDataRequestManager().SetResultsFilename("VasopressinPD.txt"); + + bg->GetLogger()->Info("Beginning PD Scenario"); + + //Choose which test to run + PDCase test = LowDose; + + switch (test) { + case LowDose: + infuse.GetConcentration().SetValue(0.200, MassPerVolumeUnit::ug_Per_mL); + infuse.GetRate().SetValue(1.0, VolumePerTimeUnit::mL_Per_min); + bg->ProcessAction(infuse); + tracker.AdvanceModelTime(1200.0); + break; + case HighDose: + infuse.GetConcentration().SetValue(0.484, MassPerVolumeUnit::ug_Per_mL); + infuse.GetRate().SetValue(1.0, VolumePerTimeUnit::mL_Per_min); + bg->ProcessAction(infuse); + tracker.AdvanceModelTime(1200.0); + break; + case RampDose: + infuse.GetConcentration().SetValue(0.0417, MassPerVolumeUnit::ug_Per_mL); + infuse.GetRate().SetValue(1.0, VolumePerTimeUnit::mL_Per_min); + bg->ProcessAction(infuse); + tracker.AdvanceModelTime(1200.0); + infuse.GetConcentration().SetValue(0.1754, MassPerVolumeUnit::ug_Per_mL); + bg->ProcessAction(infuse); + tracker.AdvanceModelTime(1200.0); + infuse.GetConcentration().SetValue(0.518, MassPerVolumeUnit::ug_Per_mL); + bg->ProcessAction(infuse); + tracker.AdvanceModelTime(1200.0); + break; + } + + bg->GetLogger()->Info("Finished"); +} \ No newline at end of file diff --git a/src/sdk/howto/cpp/HowTo-SarinExposure.cpp b/src/sdk/howto/cpp/HowTo-SarinExposure.cpp new file mode 100644 index 0000000..7a1ff74 --- /dev/null +++ b/src/sdk/howto/cpp/HowTo-SarinExposure.cpp @@ -0,0 +1,183 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +#include "BioGearsEngineHowTo.h" + +// Include the various types you will be using in your code +#include "patient/SEPatient.h" +#include "bind/PatientData.hxx" +#include "bind/ActivePatientEventData.hxx" +#include "system/environment/SEEnvironmentalConditions.h" +#include "system/environment/actions/SEEnvironmentChange.h" +#include "system/physiology/SEBloodChemistrySystem.h" +#include "system/physiology/SECardiovascularSystem.h" +#include "system/physiology/SEEnergySystem.h" +#include "system/physiology/SERespiratorySystem.h" +#include "substance/SESubstanceManager.h" +#include "substance/SESubstanceCompound.h" +#include "substance/SESubstanceConcentration.h" +#include "properties/SEScalarFraction.h" +#include "properties/SEScalarFrequency.h" +#include "properties/SEScalarLengthPerTime.h" +#include "properties/SEScalarMass.h" +#include "properties/SEScalarMassPerVolume.h" +#include "properties/SEScalarPressure.h" +#include "properties/SEScalarTime.h" +#include "properties/SEScalarVolume.h" +#include "properties/SEScalarVolumePerTime.h" +#include "properties/SEScalarPower.h" +#include "properties/SEScalarAmountPerVolume.h" +#include "properties/SEScalarMassPerVolume.h" +#include "substance/SESubstanceFraction.h" +#include "engine/PhysiologyEngineTrack.h" +#include "compartment/SECompartmentManager.h" +#include "system/environment/conditions/SEInitialEnvironment.h" + +//-------------------------------------------------------------------------------------------------- +/// \brief +/// Usage for exposing the patient to Sarin, a toxic nerve agent +/// +/// \details +/// Refer to the SEEnvironmentChange class +/// Refer to the SEDrug Class +//-------------------------------------------------------------------------------------------------- +void OutputState(std::unique_ptr&bgOut); + +void HowToSarinExposure() +{ + // Create the engine and load the patient + std::unique_ptr bg = CreateBioGearsEngine("HowToSarinExposure.log"); + bg->GetLogger()->Info("HowToSarinExposure"); + + if (!bg->LoadState("./states/StandardMale@0s.xml")) + { + bg->GetLogger()->Error("Could not load state, check the error"); + return; + } + +//---Initialize all variables needed for scenario + + //Substances + SESubstance* Sarin = bg->GetSubstanceManager().GetSubstance("Sarin"); + SESubstance* CO2 = bg->GetSubstanceManager().GetSubstance("CarbonDioxide"); + SESubstance* O2 = bg->GetSubstanceManager().GetSubstance("Oxygen"); + + //Numerical values + double exposureTime = 5.0; //Establish how long the patient will be exposed to Sarin + double simulationTime = 20.0; //Establish the length of the simulation + double SarinAerosol_mg_Per_m3 = 15.0; //Establish concentration of ambient Sarin in mg/m^3 + double BaselineRbcAche_nM = 8.0; //The average baseline concentration of red blood cell acetylcholinesterase in the body + double RbcAcheConversionFactor_M_to_nM = 1e9; //Use to convert data calls in M to nM because nM is used in kinetics equations in BioGears Drugs file for Sarin + double FractionRbcAcheInhibited = 0.0; //The fraction of red blood cell acetylcholinesterase that has been inhibited by Sarin binding. This value will be used to trigger events. + + //System states + CDM::enumOnOff::value SarinActive; //Track whether the patient is currently being exposed to Sarin + std::vector eventList = {"Rhinorrhea","Nausea","Gastrointestinal Sounds" }; //The list of patient states you want to monitor--note that BioGears track many events already; this is a way to track other states you might want to add + std::map> eventMap; //Each state has a threshold level at which it is triggered and an associated on/off switch + eventMap["Rhinorrhea"] = std::make_pair(0.1, CDM::enumOnOff::Off); //Put the states in the map with the threshold inhibition at which they start and make them all inactive initially + eventMap["Nausea"] = std::make_pair(0.25, CDM::enumOnOff::Off); + eventMap["Gastrointestinal Sounds"] = std::make_pair(0.5, CDM::enumOnOff::Off); + + // The tracker is responsible for advancing the engine time and outputting the data requests below at each time step + HowToTracker tracker(*bg); + + // Create data requests for each value that should be written to the output log as the engine is executing + // Physiology System Names are defined on the System Objects + // defined in the Physiology.xsd file + + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("RespirationRate", FrequencyUnit::Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("TidalVolume", VolumeUnit::mL); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("TotalAlveolarVentilation", VolumePerTimeUnit::L_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("Aorta", *CO2, "PartialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("Aorta", *O2, "PartialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("OxygenSaturation"); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("HeartRate", FrequencyUnit::Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("RedBloodCellAcetylcholinesterase", AmountPerVolumeUnit::mol_Per_L); + + + bg->GetEngineTrack()->GetDataRequestManager().SetResultsFilename("HowToSarinExposure.txt"); + + // Advance some time to get some resting data + tracker.AdvanceModelTime(60); + + bg->GetLogger()->Info("The patient is nice and healthy"); + OutputState(bg); + + // We assume that Sarin is inhaled as an aerosol, therefore we have to instantiate an environmental conditions change (see HowTo-EnvironmentChange for more details) + // Sarin is introduced as an ambient aerosol and the concentration is set with units of mg/m^3 + SEEnvironmentChange env(bg->GetSubstanceManager()); + SEEnvironmentalConditions& conditions = env.GetConditions(); + conditions.GetAmbientAerosol(*Sarin).GetConcentration().SetValue(SarinAerosol_mg_Per_m3,MassPerVolumeUnit::mg_Per_m3); + SarinActive = CDM::enumOnOff::On; //Sarin exposure is active + bg->ProcessAction(env); + + // We maintain this loop so that we can periodically check events while advancing data tracker + while (bg->GetSimulationTime(TimeUnit::min) < simulationTime) + { + //Break out of loop if patient enters irreversible state + if (bg->GetPatient().IsEventActive(CDM::enumPatientEvent::IrreversibleState)) + break; + + //Check if we have reached the end of the exposure time to Sarin. If so, remove it from the environment and deactivate it + if ((bg->GetSimulationTime(TimeUnit::min) > exposureTime+1) && (SarinActive == CDM::enumOnOff::On)) + { + conditions.RemoveAmbientAerosol(*Sarin); + bg->ProcessAction(env); + bg->GetLogger()->Info(std::stringstream() << Sarin->GetName() + " removed from environment"); + SarinActive = CDM::enumOnOff::Off; + } + + //Get the fraction of inactive red blood cell acetylcholinesterase by converting active M concentration to nM, dividing by baseline, and subtracting from 1 + FractionRbcAcheInhibited = 1.0 - (bg->GetBloodChemistrySystem()->GetRedBloodCellAcetylcholinesterase(AmountPerVolumeUnit::mol_Per_L)*RbcAcheConversionFactor_M_to_nM / BaselineRbcAche_nM); + + //Cout statement outputs Fraction of RBC Inhibited in case you want to track that during simulation + std::cout << "Fraction of Red Blood Cell AChE Inhibited: " << FractionRbcAcheInhibited << std::endl; + + //Check each event in the event list specified above. An event is active if the fraction of red blood cell acetylcholinesterase inhibited + //is greater than the threshold provided for the event in the event map + for (auto e : eventList) + { + if ((FractionRbcAcheInhibited > eventMap[e].first) && (eventMap[e].second == CDM::enumOnOff::Off)) //Only display state when it is triggered, not every time point when the event is active + { + bg->GetLogger()->Info(std::stringstream() << "Patient is exhibiting " + e); + eventMap[e].second = CDM::enumOnOff::On; + } + if ((FractionRbcAcheInhibited < eventMap[e].first) && (eventMap[e].second == CDM::enumOnOff::On)) //Remove state if levels drop below threshold. Probably won't happen for Sarin because the red blood cell acetylcholinesterase is inhibited for a very long time (days to weeks) + { + bg->GetLogger()->Info(std::stringstream() << "Patient no longer exhibiting " + e); + eventMap[e].second = CDM::enumOnOff::Off; + } + } + + //Advance the tracker forward by 30 seconds. Change this time to adjust how frequently the simulation checks for events (will not effect how the engine runs) + + tracker.AdvanceModelTime(30.0); + } + + OutputState(bg); + + tracker.AdvanceModelTime(60); + + bg->GetLogger()->Info("Finished"); +} + +void OutputState(std::unique_ptr&bgOut) +{ + //Since we'll want this data repeatedly, make a function call so that we don't copy and paste like crazy + bgOut->GetLogger()->Info(std::stringstream() << "Respiration Rate : " << bgOut->GetRespiratorySystem()->GetRespirationRate(FrequencyUnit::Per_min) << "breaths/min"); + bgOut->GetLogger()->Info(std::stringstream() << "Tidal Volume : " << bgOut->GetRespiratorySystem()->GetTidalVolume(VolumeUnit::mL) << VolumeUnit::mL); + bgOut->GetLogger()->Info(std::stringstream() << "C02 Partial Pressure in Aorta : " << bgOut->GetBloodChemistrySystem()->GetArterialCarbonDioxidePressure(PressureUnit::mmHg) << PressureUnit::mmHg); + bgOut->GetLogger()->Info(std::stringstream() << "O2 Partial Pressure in Aorta : " << bgOut->GetBloodChemistrySystem()->GetArterialOxygenPressure(PressureUnit::mmHg) << PressureUnit::mmHg); + bgOut->GetLogger()->Info(std::stringstream() << "Oxygen Saturation : " << bgOut->GetBloodChemistrySystem()->GetOxygenSaturation() << "%"); + bgOut->GetLogger()->Info(std::stringstream() << "Heart Rate : " << bgOut->GetCardiovascularSystem()->GetHeartRate(FrequencyUnit::Per_min) << "bpm"); + bgOut->GetLogger()->Info(std::stringstream() << "Red Blood Cell Acetylcholinesterase Level : " << bgOut->GetBloodChemistrySystem()->GetRedBloodCellAcetylcholinesterase(AmountPerVolumeUnit::mol_Per_L) << AmountPerVolumeUnit::mol_Per_L); +} \ No newline at end of file diff --git a/src/sdk/howto/cpp/HowTo-ThreadedBioGears.cpp b/src/sdk/howto/cpp/HowTo-ThreadedBioGears.cpp index 5272019..07cf97e 100644 --- a/src/sdk/howto/cpp/HowTo-ThreadedBioGears.cpp +++ b/src/sdk/howto/cpp/HowTo-ThreadedBioGears.cpp @@ -27,6 +27,7 @@ specific language governing permissions and limitations under the License. #include "properties/SEScalarTime.h" #include "properties/SEScalarVolume.h" #include "properties/SEScalarVolumePerTime.h" +#include "properties/SEScalar0To1.h" #include void HowToDynamicHemorrhage() @@ -37,11 +38,12 @@ void HowToDynamicHemorrhage() // When it comes back, the engine will be running, waiting for your input int action; + double severity; double rate; - std::vector userMCIS; + std::string location; bool active = true; - int codeCount; - int codeIn; + std::string out; + do { bgThread.GetLogger()->Info("Enter Integer for Action to Perform : [1]Status, [2]Hemorrhage, [3]IVFluids, [4]Quit"); @@ -52,16 +54,16 @@ void HowToDynamicHemorrhage() bgThread.Status(); break; case 2: - codeCount = 0; - //See HowTo-Hemorrhage for description of MCIS code - bgThread.GetLogger()->Info("Provide 5-digit MCIS code (separate digits by spaces) followed by ENTER: "); - do { - std::cin >> codeIn; - userMCIS.push_back(codeIn); - codeCount++; - } while (codeCount <= 4); //MCIS code must be five digits long, so don't go past fourth index - bgThread.GetLogger()->Info(std::stringstream() << userMCIS[0] << userMCIS[1] << userMCIS[2] << userMCIS[3] << userMCIS[4]); - bgThread.SetHemorrhage(userMCIS); + bgThread.GetLogger()->Info("Type a location, then hit ENTER: "); + std::cin >> location; + bgThread.GetLogger()->Info("Type a severity (0-1 scale, 0 stops hemorrhage), then hit ENTER: "); + std::cin >> severity; + if (severity <= ZERO_APPROX) + out = "Stop hemorrhage in " + location; + else + out = "Hemorrhage in " + location + " with severity = " + std::to_string(severity); + bgThread.GetLogger()->Info(out); + bgThread.SetHemorrhage(location,severity); break; case 3: bgThread.GetLogger()->Info("Enter IV Fluids Rate in mL/min : "); @@ -104,10 +106,11 @@ BioGearsThread::~BioGearsThread() SAFE_DELETE(m_hemorrhage); } -void BioGearsThread::SetHemorrhage(const std::vector& mcisIn) +void BioGearsThread::SetHemorrhage(std::string& location, double& severity) { - m_hemorrhage->SetMCIS(mcisIn);//the rate of hemorrhage - m_hemorrhage->ProcessMCIS(); + m_hemorrhage->SetCompartment(location); + m_hemorrhage->GetSeverity().SetValue(severity); + m_hemorrhage->SetBleedPath(); m_mutex.lock(); m_bg->ProcessAction(*m_hemorrhage); m_mutex.unlock(); diff --git a/src/sdk/howto/cpp/HowTo-ThreadedBioGears.h b/src/sdk/howto/cpp/HowTo-ThreadedBioGears.h index bd3c7af..23331cf 100644 --- a/src/sdk/howto/cpp/HowTo-ThreadedBioGears.h +++ b/src/sdk/howto/cpp/HowTo-ThreadedBioGears.h @@ -30,7 +30,7 @@ class BioGearsThread BioGearsThread(const std::string& logfile); virtual ~BioGearsThread(); - void SetHemorrhage(const std::vector& mcisIn); + void SetHemorrhage(std::string& location, double& severity); void SetIVFluidsFlow_mL_Per_min(double rate); void Status(); diff --git a/src/sdk/howto/cpp/HowTo-VasopressinShockTherapy.cpp b/src/sdk/howto/cpp/HowTo-VasopressinShockTherapy.cpp new file mode 100644 index 0000000..b481747 --- /dev/null +++ b/src/sdk/howto/cpp/HowTo-VasopressinShockTherapy.cpp @@ -0,0 +1,152 @@ +/************************************************************************************** +Copyright 2015 Applied Research Associates, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the License +at: +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +**************************************************************************************/ + +#include "BioGearsEngineHowTo.h" + +// Include the various types you will be using in your code +#include "utils/SEEventHandler.h" +#include "patient/actions/SESubstanceBolus.h" +#include "patient/actions/SESubstanceInfusion.h" +#include "patient/actions/SESubstanceCompoundInfusion.h" +#include "system/physiology/SEBloodChemistrySystem.h" +#include "system/physiology/SECardiovascularSystem.h" +#include "system/physiology/SEEnergySystem.h" +#include "system/physiology/SERespiratorySystem.h" +#include "system/physiology/SEDrugSystem.h" +#include "system/physiology/SENervousSystem.h" +#include "substance/SESubstanceManager.h" +#include "patient/SEPatient.h" +#include "patient/actions/SEHemorrhage.h" +#include "properties/SEScalar.h" +#include "properties/SEScalarFraction.h" +#include "properties/SEScalarFrequency.h" +#include "properties/SEScalarMassPerVolume.h" +#include "properties/SEScalarPressure.h" +#include "properties/SEScalarTemperature.h" +#include "properties/SEScalarTime.h" +#include "properties/SEScalarVolume.h" +#include "properties/SEScalarMass.h" +#include "properties/SEScalar0To1.h" +#include "properties/SEScalarVolumePerTime.h" +#include "properties/SEScalarOsmolality.h" +#include "engine/PhysiologyEngineTrack.h" +#include "compartment/SECompartmentManager.h" + + +//-------------------------------------------------------------------------------------------------- +/// \brief +/// Usage for adminstering a substance to the patient via a continuous infusion +/// +/// \details +/// Refer to the SESubstanceInfusion class +/// Refer to the SESubstanceManager class +//-------------------------------------------------------------------------------------------------- +void HowToVasopressinShockTherapy() +{ + // Create the engine and load the patient + std::unique_ptr bg = CreateBioGearsEngine("ShockTherapy_Control.log"); + bg->GetLogger()->Info("ShockTherapy_Control"); + + if (!bg->InitializeEngine("StandardMale.xml")) + { + bg->GetLogger()->Error("Could not load initialize engine, check the error"); + return; + } + else + { + bg->GetLogger()->Info("Engine stabilization complete"); + } + + //The tracker is responsible for advancing the engine time and outputting the data requests below at each time step + HowToTracker tracker(*bg); + + //Set up substances. Initialized vasopressin plasma concentration to 0. Note that saline is technically a compound--this is + //so the engine knows to look for multiple components within the same substance file (i.e. Na, Cl, etc) + SESubstance* vas = bg->GetSubstanceManager().GetSubstance("Vasopressin"); + vas->GetPlasmaConcentration().SetValue(0.0, MassPerVolumeUnit::ug_Per_L); + SESubstanceCompound* sal = bg->GetSubstanceManager().GetCompound("Saline"); + + //Each infusion is managed by a separate object + //This object is the vasopressin infusion. It requires a concentration and an admin rate. The infusion will continue unabated until the + //rate of infusion is reset to 0.0 + SESubstanceInfusion vasInfuse(*vas); + vasInfuse.GetConcentration().SetValue(10.0, MassPerVolumeUnit::ug_Per_mL); + vasInfuse.GetRate().SetValue(0.33, VolumePerTimeUnit::mL_Per_min); + + //This object is the saline infusion that accompanies the vasopressin in the treatment group. Compounds have a separate infusion object + //that mimics an IV drip. Thus, the compound infusion object assumes intravenous injection. The user must specify the volume available to + //administer and the rate. We set the rate at 3.0 so that the combined vasopressin/saline mixture is given at 3.33 mL/min (or 200 mL/hr) + SESubstanceCompoundInfusion treatmentInfuse(*sal); + treatmentInfuse.GetBagVolume().SetValue(500.0, VolumeUnit::mL); + treatmentInfuse.GetRate().SetValue(3.0, VolumePerTimeUnit::mL_Per_min); + + //This is the saline infusion given to the control group. It is given a large bag volume to ensure that it will not run out during trial. + SESubstanceCompoundInfusion controlInfuse(*sal); + controlInfuse.GetBagVolume().SetValue(2000.0, VolumeUnit::mL); + controlInfuse.GetRate().SetValue(3.33, VolumePerTimeUnit::mL_Per_min); + + //The two hemorrhage objects (one for each wound location) are created below. Hemorrhages are stopped by setting the severity to 0.0 + SEHemorrhage venaCavaBleed; + venaCavaBleed.SetCompartment("Vena Cava"); + venaCavaBleed.GetSeverity().SetValue(1.0); + + // Create data requests for each value that should be written to the output log as the engine is executing + // Physiology System Names are defined on the System Objects in the Physiology.xsd file + bg->GetEngineTrack()->GetDataRequestManager().CreateSubstanceDataRequest().Set(*vas, "PlasmaConcentration", MassPerVolumeUnit::ug_Per_L); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("HeartRate", FrequencyUnit::Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("HeartStrokeVolume", VolumeUnit::mL); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("SystolicArterialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("DiastolicArterialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("MeanArterialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("CardiacOutput", VolumePerTimeUnit::mL_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("UrineProductionRate", VolumePerTimeUnit::mL_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("UrineOsmolality", OsmolalityUnit::mOsm_Per_kg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("CerbralPerfusionPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("IntracranialPressure", PressureUnit::mmHg); + bg->GetEngineTrack()->GetDataRequestManager().CreatePhysiologyDataRequest().Set("SystemicVascularResistance", FlowResistanceUnit::mmHg_s_Per_mL); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("SkinVasculature", "InFlow", VolumePerTimeUnit::mL_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("BrainVasculature", "InFlow", VolumePerTimeUnit::mL_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("MyocardiumVasculature", "InFlow", VolumePerTimeUnit::mL_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("MuscleVasculature", "InFlow", VolumePerTimeUnit::mL_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("SmallIntestineVasculature", "InFlow", VolumePerTimeUnit::mL_Per_min); + bg->GetEngineTrack()->GetDataRequestManager().CreateLiquidCompartmentDataRequest().Set("Ground", "InFlow", VolumePerTimeUnit::mL_Per_min); + + bg->GetEngineTrack()->GetDataRequestManager().SetResultsFilename("ShockTherapy_Control.txt"); + + bool control = true; //Use to flag which variation you want to run + + bg->GetLogger()->Info("Beginning Scenario"); + + //Initiate both hemorrhage actions and advance engine eight minutes + bg->ProcessAction(venaCavaBleed); + tracker.AdvanceModelTime(480); + + //Remove artery bleeding, begin initial fluid rescue, advance engine ten minutes + venaCavaBleed.GetSeverity().SetValue(0.0); + bg->ProcessAction(venaCavaBleed); + + //Begin either the control treatment (control = true) or experimental treatment (control = false). Toggle between using boolean above + if (control) + { + bg->ProcessAction(controlInfuse); + } + else + { + bg->ProcessAction(vasInfuse); + bg->ProcessAction(treatmentInfuse); + } + + //Allow fluid administration for 30 minutes + tracker.AdvanceModelTime(1800); + + bg->GetLogger()->Info("Finished"); +} \ No newline at end of file diff --git a/src/test/main.cpp b/src/test/main.cpp index 9f11a05..663513a 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -70,8 +70,10 @@ int main(int argc, char* argv[]) //bgeTest.InstantPlusSimpleDiffusionTest(bgDir); //bgeTest.SimpleDiffusionTwoCompartmentTest(bgDir); //bgeTest.SimpleDiffusionFourCompartmentTest(bgDir); + //bgeTest.ActiveTransportTest(bgDir); //bgeTest.SimpleDiffusionHierarchyTest(bgDir); - //bgeTest.FacilitatedDiffusionTest(bgDir); + // bgeTest.FacilitatedDiffusionTest(bgDir); + //bgeTest.ActiveIonTransportTest(bgDir); //bgeTest.BrainInjuryTest(bgDir); @@ -99,6 +101,7 @@ int main(int argc, char* argv[]) //bgeTest.FourCompartmentTestSimple(bgDir); //bgeTest.AcidBaseFourCompartmentTest(bgDir); //bgeTest.FiveCompartmentTestWithDiffusion(bgDir); + //bgeTest.FiveCompartmentTestWithActiveDiffusion(bgDir); //bgeTest.AcidBaseFourCompartmentTestWithProductionConsumption(bgDir); //bgeTest.AcidBaseFiveCompartmentTestWithDiffusion(bgDir); //bgeTest.AcidBaseFiveCompartmentTestWithProductionConsumptionAndDiffusion(bgDir); diff --git a/src/utils/PKHuisinga/GenericPBPKmodel.m b/src/utils/PKHuisinga/GenericPBPKmodel.m new file mode 100644 index 0000000..cd6100f --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel.m @@ -0,0 +1,211 @@ +%%% MAIN SCRIPT TO CONTROL THE SIMULATION +%%% +%%% Version: August 31st, 2012. +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments. +%%% +%%% This script specifies all inputs for the simulation of the +%%% pharmacokinetics of different drugs in a virtual population of +%%% human adults and children. +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + + +clear all; + +%%% ================================================================= +%%% The structure 'species' contains all the reference anatomical and +%%% physiological values that are used to generate individuals and +%%% compute tissue partition coefficients. +%%% species.name = 'human' ('rat' only partially implemented) + +species.type = 'human'; +[species,T] = GenericPBPKmodel_physiology(species.type); + + +%%% ================================================================= +%%% The structure 'drug' contains all physico-chemical drug properties +%%% as well as human and rat related PK parameters. +%%% Choose a drug from the list below, classified by its type: +%%% moderate-to-strong base, weak base, neutral, acid. +%%% The drug type determines how the tissue partition coefficients +%%% are computed. +%%% +%%% Moderate-to-strong bases: Amitriptyline, Caffeine, Desipramine, +%%% Diltiazem, Diphenhydramine, Imipramine, Lidocaine, Metoprolol, +%%% Pindolol, Sildefanil, Theophylline +%%% +%%% Weak bases: Alprazolam, Diazepam, Flunitrazepam, Midazolam, Triazolam +%%% +%%% Acids: Amobarbital, Diclofenac, Hexobarbital, Ibuprofen, Methohexital, +%%% Thiopental, Tolbutamide, Valproate, Warfarin + +drug.name = 'Midazolam'; +drug = GenericPBPKmodel_drugDatabase(drug.name,species); + + +%%% ================================================================= +%%% The structure 'study' contains information about the dose and +%%% the route of application (iv bolus, iv infusion, po), the +%%% simulation time in [min], the output time points, etc. +%%% Dosing can be fixed in unit [mg] or per body size descriptor (BSD) +%%% in unit [mg/per unit of the BSD], e.g., [mg/kg BW] +%%% Choose dosing per BSD = 'fixed' (fixed dose),'BW','LBW','BH','BMI','BSA', etc + +study.bolus.dose = 5; +study.bolus.per = 'fixed'; + +study.infusion.dose = 0; +study.infusion.per = 'fixed'; +study.infusion.tend = 60; + +study.po.dose = 0; +study.po.per = 'fixed'; + +study.observationTime = [0 :1: 10*60]; % start and end time in [min] +study.numberOfDosing = 1; % for a single dose + + +%%% ================================================================= +%%% Determine all parameters for the reference individuals: +%%% adults 35y (20-50y, m/f), and children 15y (m/f), 10y (u), 5y (u) +%%% with the last two being unisex (u) + +[reference] = GenericPBPKmodel_referenceIndividual(species,drug,study); + + +%%% ================================================================= +%%% Generate virtual population based on antropometric data. +%%% Approaches for generating the population: +%%% +%%% male = adult male reference individual +%%% adults = adult m/f reference individuals +%%% all = adult m/f, age15 m/f, age10, age5 +%%% Grandmaison = 355 male/329 female adults, with distribtion of BW & BH +%%% as in the autopsy study by de la Grandmaison et al (2001) +%%% random = 'size' many individuals of 'agesex' with mean BW & BH +%%% identical to reference.agesex and CV of BH & BMI +%%% as specified in de la Grandmaison et al (2001) +%%% obese = four individuals with BH identical to 'agesex' and +%%% BMI = [0.7 1 1.3 2]*BMI of reference 'agesex' +%%% identical = 'size' many individuals of 'agesex', only reasonalbe +%%% if combined with randomEffects + +population.how2generate = 'adults'; % 'male','adults','random','identical','all','children_small_adults','delaGrandmaison','obese' +population.agesex = 'age35m'; % only valid with 'random','identical' and 'obese' option +population.size = 35; % only valid with 'random' and 'identical' option +[individual,population] = GenericPBPKmodel_virtualPopulation(reference,study,population); + + +%%% ================================================================= +%%% Include random effects on hepatic (intrinsic) clearance and/or +%%% tissue-to-blood partition coefficients. Parameters are uniformly +%%% perturbed by a randon factor '1+facX', where faxX = 0 corresponds +%%% to no perturbation and facX>0 corresponds to a perturbation between +%%% 1/(1+facX)-fold and (1+facX)-fold of the original value + +randomEffects.facX.CLint.hep = 0; +randomEffects.facX.Ktb = 0; +[individual,population] = GenericPBPKmodel_randomEffects(individual,study,randomEffects,population); + + +%%% ================================================================= +%%% Simulate PBPK model for each individual +%%% + +for id=1:population.size + [sim(id),OK] = GenericPBPKmodel_simulatePBPKmodel(individual(id),drug,study); + if OK~=1 return; end; + individual(id).t = sim(id).t; individual(id).unit.t = sim(id).unit.t; + individual(id).C = sim(id).C; individual(id).unit.C = sim(id).unit.C; +end; +fprintf('\n\n') + + +%%% ================================================================= +%%% Graphical output. Specify below, which plots to be shown +%%% +ShowGraphics.allindividual.plasma = 'yes'; % 'yes' or 'no' +ShowGraphics.allindividual.YScale = 'log'; % 'log' or 'lin' +ShowGraphics.allindividual.figNr = 13; + +ShowGraphics.allTissues.individual = []; % specify id of individuals, e.g. [1 2 7]; [] = 'No plot' +ShowGraphics.allTissues.YScale = 'log'; % 'log' or 'lin' +ShowGraphics.allTissues.figNr = 10; +ShowGraphics.allTissues.oneFigOnly = 1; % 1 = all individual in one figure, 0 = use different figures for different individual + +ShowGraphics.individual.allTissues = []; % specify id of individual, e.g. [1]; [] = 'No plot' +ShowGraphics.individual.figNr = 21; + +ShowGraphics.percentiles.plasma = 'yes'; % applies only to a virtual population of size 10 or larger +ShowGraphics.percentiles.YScale = 'log'; +ShowGraphics.percentiles.figNr = 24; + +ShowGraphics.lowerLimitY = 0.01; % sets the lower value of the Y axis +GenericPBPKmodel_graphicalOutput(population,individual,drug,study,ShowGraphics); + + +%%% ================================================================= +%%% Lumping of PBPK models +%%% +%%% (i) mechanistic lumping +%%% +lumping = {[T.mus,T.adi],[T.bon],[T.ski],[T.bra,T.hea,T.kid],[T.gut,T.spl,T.liv],[T.lun,T.art,T.ven]}; + +ShowGraphics.lumping.individual = []; % specify id of individual, e.g. [1]; [] = 'No plot' +ShowGraphics.lumping.YScale = 'log'; % 'log' or 'lin' +ShowGraphics.lumping.figNr = 50; +ShowGraphics.lumping.type = 'mechanistically lumped model'; + +for id=ShowGraphics.lumping.individual + mecL(id) = GenericPBPKmodel_mechanisticLumping(individual(id),drug,study,lumping); +end; +if ~exist('mecL') mecL = []; end; +GenericPBPKmodel_graphicalOutputLumping(individual,mecL,drug,ShowGraphics) + + +%%% +%%% (ii) minimal lumping +%%% +lumping = {[T.mus,T.adi,T.bon,T.ski],[T.bra,T.hea,T.kid,T.gut,T.spl,T.liv,T.lun,T.art,T.ven]}; + +ShowGraphics.lumping.individual = []; % specify id of individual, e.g. [1]; [] = 'No plot' +ShowGraphics.lumping.YScale = 'log'; % 'log' or 'lin' +ShowGraphics.lumping.figNr = 60; +ShowGraphics.lumping.type = 'minimal lumped model'; + +for id=ShowGraphics.lumping.individual % 1:population.size % + minL(id) = GenericPBPKmodel_mechanisticLumping(individual(id),drug,study,lumping); +end; +if ~exist('minL') minL = []; end; +GenericPBPKmodel_graphicalOutputLumping(individual,minL,drug,ShowGraphics) + +fprintf('\n\n') + diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_drugDatabase.m b/src/utils/PKHuisinga/GenericPBPKmodel_drugDatabase.m new file mode 100644 index 0000000..0738a89 --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_drugDatabase.m @@ -0,0 +1,1150 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function specifies the physico-chemical parameter values +%%% of various drugs in addition to pharmacokinetic values for human +%%% and rat. +%%% +%%% NOTE: Not all data sets are complete, might contain generic default +%%% assumptions or rat values as surrogate for unknown human values. +%%% PLease check before using the corresponding drug. +%%% Called from the main script GenericPBPKmodel.m +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function drug = GenericPBPKmodel_drugDatabase(compound,species) + +fprintf(' & drug data (%s). ',compound) + +% drug data base (db) +% units: weight in [kg], volume in [l], time in [min] +% potential fields (unspecified NaN or with default values) are + +% physico-chemical drug properties -------------------------------------- +db.(compound).name = NaN; % name of drug +db.(compound).mode = NaN; % moderate to strong base 'B', weak base 'wB', neutral 'N', acid 'A' +db.(compound).MW = NaN; % molecular weight in [g/mol] (not required if unit is mg etc.) +db.(compound).pKa = NaN; % pKa value +db.(compound).logPow = NaN; % log octanol-water partition coefficient +db.(compound).logPvow = NaN; % log vegatible oil-water partition coefficient + % (will be estimated from logPov if not speciefied) + +% rat related PK parameters --------------------------------------------- +db.(compound).rat.fuP = NaN; % fraction unbound in plasma +db.(compound).rat.BP = NaN; % blood-to-plasma ratio +db.(compound).rat.CLblood_kgBW.hep = 0; % hepatic blood clearance in [L/min/kg BW] +db.(compound).rat.CLblood_kgBW.ren = 0; % renal blood clearance in [L/min/kg BW] +db.(compound).rat.lambda_po = 0; % first order oral absorption rate constant in [1/min] +db.(compound).rat.E.feces = 0; % fraction excreted unchanged in the feces +db.(compound).rat.E.gut = 0; % fraction metabolized in the gut + +% human related PK parameters ------------------------------------------- +db.(compound).human.fuP = NaN; % fraction unbound in plasma +db.(compound).human.BP = NaN; % blood-to-plasma ratio +db.(compound).human.CLblood_kgBW.hep = 0; % hepatic blood clearance in [L/min/kg BW] +db.(compound).human.CLblood_kgBW.ren = 0; % renal blood clearance in [L/min/kg BW] +db.(compound).human.lambda_po = 0; % first order oral absorption rate constant in [1/min] +db.(compound).human.E.feces = 0; % fraction excreted unchanged in the feces +db.(compound).human.E.gut = 0; % fraction metabolized in the gut +db.(compound).human.expData.age = 'age35m'; % age&sex that has been used to determine BP, fuP, CL.hep + +% units ----------------------------------------------------------------- +db.unit.MW = 'g/mol'; +db.unit.pKa = 'unitless'; +db.unit.logPow = 'unitless'; +db.unit.logPvow = 'unitless'; +db.unit.fuP = 'fraction'; +db.unit.BP = 'unitless'; +db.unit.CLblood_kgBW = 'L/min/kg BW'; +db.unit.po_lambda = '1/min'; +db.unit.po_Efeces = 'fraction'; +db.unit.po_Egut = 'fraction'; + + + +% --- Bases ------------------------------------------------------------ + +db.Acebutolol.name = 'Acebutolol'; +db.Acebutolol.mode = 'B'; +db.Acebutolol.MW = 336.43; +db.Acebutolol.pKa = 9.67; +db.Acebutolol.logPow = 1.87; +db.Acebutolol.logPvow = 0.74; +db.Acebutolol.human.fuP = 0.87; +db.Acebutolol.human.BP = 1.39; +db.Acebutolol.human.CLblood_kgBW.hep = 8.8/1000; + +db.AcebutololR.name = 'AcebutololR'; +db.AcebutololR.mode = 'B'; +db.AcebutololR.pKa = 9.7; +db.AcebutololR.logPow = 1.87; +db.AcebutololR.logPvow = 0.74; +db.AcebutololR.rat.fuP = 0.79; +db.AcebutololR.rat.BP = 1.09; +db.AcebutololR.rat.E.hep = NaN; + +db.AcebutololS.name = 'AcebutololS'; +db.AcebutololS.mode = 'B'; +db.AcebutololS.pKa = 9.7; +db.AcebutololS.logPow = 1.87; +db.AcebutololS.logPvow = 0.74; +db.AcebutololS.rat.fuP = 0.73; +db.AcebutololS.rat.BP = 1.01; +db.AcebutololS.rat.E.hep = NaN; + +db.Alprenolol.name = 'Alprenolol'; +db.Alprenolol.mode = 'B'; +db.Alprenolol.MW = 249.35; +db.Alprenolol.pKa = 9.53; +db.Alprenolol.logPow = 3.16; +db.Alprenolol.human.fuP = 0.2; +db.Alprenolol.human.BP = 0.76; + +db.Amitriptyline.name = 'Amitriptyline'; +db.Amitriptyline.mode = 'B'; +db.Amitriptyline.MW = 277.40; +db.Amitriptyline.pKa = 9.40; +db.Amitriptyline.logPow = 4.90; +db.Amitriptyline.rat.fuP = 0.056; +db.Amitriptyline.rat.BP = 0.86; +db.Amitriptyline.rat.E.hep = NaN; +db.Amitriptyline.human.fuP = 0.056; +db.Amitriptyline.human.BP = 0.86; +db.Amitriptyline.human.CLblood_kgBW.hep = 12/1000; % Obach99 + +db.Atenolol.name = 'Atenolol'; +db.Atenolol.mode = 'B'; +db.Atenolol.MW = 266.94; +db.Atenolol.pKa = 9.55; +db.Atenolol.logPow = 0.16; +db.Atenolol.human.fuP = 0.96; +db.Atenolol.human.BP = 1.11; +db.Atenolol.human.CLblood_kgBW.hep = 1.7/1000; % 1.3-2.1 + +db.BetaxololR.name = 'BetaxololR'; +db.BetaxololR.mode = 'B'; +db.BetaxololR.pKa = 9.4; +db.BetaxololR.logPow = 2.59; +db.BetaxololR.logPvow = 1.54; +db.BetaxololR.rat.fuP = 0.53; +db.BetaxololR.rat.BP = 2.06; + +db.BetaxololS.name = 'BetaxololS'; +db.BetaxololS.mode = 'B'; +db.BetaxololS.pKa = 9.4; +db.BetaxololS.logPow = 2.59; +db.BetaxololS.logPvow = 1.54; +db.BetaxololS.rat.fuP = 0.54; +db.BetaxololS.rat.BP = 1.91; + +db.Biperiden.name = 'Biperiden'; +db.Biperiden.mode = 'B'; +db.Biperiden.pKa = 8.8; +db.Biperiden.logPow = 4.25; +db.Biperiden.logPvow = 3.39; +db.Biperiden.rat.fuP = 0.17; +db.Biperiden.rat.BP = 1.19; +db.Biperiden.rat.E.hep = NaN; % to be transformed: 12/1000; % human value +db.Biperiden.human.fuP = 0.1; % RR07 +db.Biperiden.human.BP = 0.95; % RR07 +db.Biperiden.human.CLblood_kgBW.hep = 12/1000; % ml/min/kg -> l/min/kg +db.Biperiden.human.E.feces = 0.29; + +db.BisoprololR.name = 'BisoprololR'; +db.BisoprololR.mode = 'B'; +db.BisoprololR.pKa = 9.4; +db.BisoprololR.logPow = 1.87; +db.BisoprololR.logPvow = 0.74; +db.BisoprololR.rat.fuP = 0.85; +db.BisoprololR.rat.BP = 1.36; +db.BisoprololR.rat.E.hep = NaN; % to be transformed: 2.7/1000; % human value +db.BisoprololR.human.fuP = 0.7; +db.BisoprololR.human.BP = 1.36; % rat value +db.BisoprololR.human.CLblood_kgBW.hep = 2.7/1000; % 1.8-3.6 ml/min/kg -> l/min/kg +db.BisoprololR.human.lambda_po = 0.01; % 1/min +db.BisoprololR.human.E.feces = 0.85; % 80-90 % + +db.BisoprololS.name = 'BisoprololS'; +db.BisoprololS.mode = 'B'; +db.BisoprololS.pKa = 9.4; +db.BisoprololS.logPow = 1.87; +db.BisoprololS.logPvow = 0.74; +db.BisoprololS.rat.fuP = 0.85; +db.BisoprololS.rat.BP = 1.36; +db.BisoprololS.rat.E.hep = NaN; % to be transformed: 2.7/1000; % human value + +db.Bupivacaine.name = 'Bupivacaine'; +db.Bupivacaine.mode = 'B'; +db.Bupivacaine.MW = 288.43; +db.Bupivacaine.pKa = 8.13; +db.Bupivacaine.logPow = 3.41; +db.Bupivacaine.human.fuP = 0.045; +db.Bupivacaine.human.BP = 0.64; +db.Bupivacaine.human.CLblood_kgBW.hep = 7.5/1000; + +db.Caffeine.name = 'Caffeine'; +db.Caffeine.mode = 'B'; +db.Caffeine.MW = 194.19; +db.Caffeine.pKa = 10.4; +db.Caffeine.logPow = -0.091; +db.Caffeine.human.fuP = 0.7; +db.Caffeine.human.BP = 1.04; +db.Caffeine.human.CLblood_kgBW.hep = 1.4/1000; + +db.Carvedilol.name = 'Carvedilol'; +db.Carvedilol.mode = 'B'; +db.Carvedilol.MW = 406.47; +db.Carvedilol.pKa = 8.1; +db.Carvedilol.logPow = 4.19; +db.Carvedilol.logPvow = 3.32; +db.Carvedilol.human.fuP = 0.025; % RR07 +db.Carvedilol.human.BP = 0.71; % RR07 +db.Carvedilol.human.CLblood_kgBW.hep = 8.7/1000; % ml/min/kg -> l/min/kg + +db.CarvedilolR.name = 'CarvedilolR'; +db.CarvedilolR.mode = 'B'; +db.CarvedilolR.pKa = 8.1; +db.CarvedilolR.logPow = 4.19; +db.CarvedilolR.logPvow = 3.32; +db.CarvedilolR.rat.fuP = 0.019; +db.CarvedilolR.rat.BP = 0.81; +db.CarvedilolR.rat.E.hep = NaN; % to be transformed: 8.7/1000; % human value +db.CarvedilolR.human.fuP = 0.02; +db.CarvedilolR.human.BP = 0.71; % RR07 +db.CarvedilolR.human.CLblood_kgBW.hep = 8.7/1000; % ml/min/kg -> l/min/kg +db.CarvedilolR.human.E.feces = 0.31; + +db.Chlorpheniramine.name = 'Chlorpheniramine'; +db.Chlorpheniramine.mode = 'B'; +db.Chlorpheniramine.MW = 274.79; +db.Chlorpheniramine.pKa = 9.13; +db.Chlorpheniramine.logPow = 3.07; +db.Chlorpheniramine.human.fuP = 0.28; +db.Chlorpheniramine.human.BP = 1.34; +db.Chlorpheniramine.human.CLblood_kgBW.hep = 3/1000; % 1.4-4.7 + +db.Clomipramine.name = 'Clomipramine'; +db.Clomipramine.mode = 'B'; +db.Clomipramine.MW = 314.85; +db.Clomipramine.pKa = 9.38; +db.Clomipramine.logPow = 5.22; +db.Clomipramine.human.fuP = 0.068; +db.Clomipramine.human.BP = 1.05; +db.Clomipramine.human.CLblood_kgBW.hep = 8/1000; % 5.5-11 + +db.Cocaine.name = 'Cocaine'; +db.Cocaine.mode = 'B'; +db.Cocaine.MW = 303.35; +db.Cocaine.pKa = 8.66; +db.Cocaine.logPow = 2.3; +db.Cocaine.human.fuP = 0.09; +db.Cocaine.human.BP = 1; +db.Cocaine.human.CLblood_kgBW.hep = 29/1000; % 23-35 + +db.Desipramine.name = 'Desipramine'; +db.Desipramine.mode = 'B'; +db.Desipramine.MW = 266.38; +db.Desipramine.pKa = 10.32; +db.Desipramine.logPow = 4.9; +db.Desipramine.human.fuP = 0.19; +db.Desipramine.human.BP = 0.96; +db.Desipramine.human.CLblood_kgBW.hep = 12/1000; % Obach99 + +db.Diltiazem.name = 'Diltiazem'; +db.Diltiazem.mode = 'B'; +db.Diltiazem.MW = 414.52; +db.Diltiazem.pKa = 7.7; +db.Diltiazem.logPow = 2.67; +db.Diltiazem.human.fuP = 0.2; +db.Diltiazem.human.BP = 1.03; +db.Diltiazem.human.CLblood_kgBW.hep = 12/1000; % Obach99 + +db.Diphenhydramine.name = 'Diphenhydramine'; +db.Diphenhydramine.mode = 'B'; +db.Diphenhydramine.MW = 255.35; +db.Diphenhydramine.pKa = 8.98; +db.Diphenhydramine.logPow = 3.31; +db.Diphenhydramine.human.fuP = 0.089; +db.Diphenhydramine.human.BP = 0.8; +db.Diphenhydramine.human.CLblood_kgBW.hep = 9.5/1000; % Obach99 + +db.Doxepine.name = 'Doxepine'; +db.Doxepine.mode = 'B'; +db.Doxepine.MW = 279.38; +db.Doxepine.pKa = 9; +db.Doxepine.logPow = 4.01; +db.Doxepine.human.fuP = 0.19; +db.Doxepine.human.BP = 1.25; +db.Doxepine.human.CLblood_kgBW.hep = 14/1000; + +db.Etidocaine.name = 'Etidocaine'; +db.Etidocaine.mode = 'B'; +db.Etidocaine.pKa = 7.8; +db.Etidocaine.logPow = 3.45; +db.Etidocaine.human.fuP = 0.06; +db.Etidocaine.human.BP = 0.59; +db.Etidocaine.human.CLblood_kgBW.hep = 15.8/1000; + +db.Fentanyl.name = 'Fentanyl'; +db.Fentanyl.mode = 'B'; +db.Fentanyl.pKa = 9.0; +db.Fentanyl.logPow = 4.05; +db.Fentanyl.logPvow = 3.17; +db.Fentanyl.rat.fuP = 0.15; +db.Fentanyl.rat.BP = 0.89; +db.Fentanyl.rat.E.hep = NaN; % to be transformed: 2.12/60*species.BW; % human value +db.Fentanyl.human.fuP = 0.16; % RR07 +db.Fentanyl.human.BP = 1; % RR07 +db.Fentanyl.human.CLblood_kgBW.hep = 2.12/60; % l/h/kg -> l/min/kg + +db.Haloperidol.name = 'Haloperidol'; +db.Haloperidol.mode = 'B'; +db.Haloperidol.MW = 375.86; +db.Haloperidol.pKa = 8.48; +db.Haloperidol.logPow = 4.3; +db.Haloperidol.human.fuP = 0.1; +db.Haloperidol.human.BP = 1.08; +db.Haloperidol.human.CLblood_kgBW.hep = 12/1000; + +db.Imipramine.name = 'Imipramine'; +db.Imipramine.mode = 'B'; +db.Imipramine.MW = 280.41; +db.Imipramine.pKa = 9.5; +db.Imipramine.logPow = 4.80; +db.Imipramine.logPvow = 4.00; +db.Imipramine.rat.fuP = 0.24; +db.Imipramine.rat.BP = 1.67; +db.Imipramine.rat.CLblood_kgBW.hep = 12/1000; % human, ml/min/kg -> l/min/kg +db.Imipramine.rat.E.feces = 1; +db.Imipramine.human.fuP = 0.13; % RR07 +db.Imipramine.human.BP = 1.07; % RR07 +db.Imipramine.human.CLblood_kgBW.hep = 12/1000; % Obach99, ml/min/kg -> l/min/kg + +db.Inaperisone.name = 'Inaperisone'; +db.Inaperisone.mode = 'B'; +db.Inaperisone.pKa = 9.1; +db.Inaperisone.logPow = 3.72; +db.Inaperisone.logPvow = 2.79; +db.Inaperisone.rat.fuP = 0.24; +db.Inaperisone.rat.BP = 1.88; + +db.Ketamine.name = 'Ketamine'; +db.Ketamine.mode = 'B'; +db.Ketamine.MW = 237.73; +db.Ketamine.pKa = 7.5; +db.Ketamine.logPow = 2.18; +db.Ketamine.human.fuP = 0.77; +db.Ketamine.human.BP = 0.82; +db.Ketamine.human.CLblood_kgBW.hep = 20/1000; % Obach99 + +db.Ketanserine.name = 'Ketanserine'; +db.Ketanserine.mode = 'B'; +db.Ketanserine.pKa = 7.31; +db.Ketanserine.logPow = 3.29; +db.Ketanserine.human.fuP = 0.049; +db.Ketanserine.human.BP = 0.7; +db.Ketanserine.human.CLblood_kgBW.hep = 8/1000; % 6-10 + +db.Lidocaine.name = 'Lidocaine'; +db.Lidocaine.mode = 'B'; +db.Lidocaine.MW = 234.34; +db.Lidocaine.pKa = 8.01; +db.Lidocaine.logPow = 2.26; +db.Lidocaine.logPvow = 1.27; +db.Lidocaine.rat.fuP = 0.38; +db.Lidocaine.rat.BP = 1.27; +db.Lidocaine.rat.E.hep = NaN; % to be transformed: 15/1000; % human value +db.Lidocaine.rat.E.feces = 1; % default assumption +db.Lidocaine.human.fuP = 0.296; +db.Lidocaine.human.BP = 0.84; +db.Lidocaine.human.CLblood_kgBW.hep = 15/1000; % ml/min/kg -> l/min/kg +db.Lidocaine.human.E.feces = 0; % default assumption +db.Lidocaine.human.lambda_po = 0.018; % 1/min Boyes etal (1970), J Pharmacol Exp Ther 174:1?8 (dogs) + +db.Lorcainide.name = 'Lorcainide'; +db.Lorcainide.mode = 'B'; +db.Lorcainide.pKa = 9.5; +db.Lorcainide.logPow = 4.85; +db.Lorcainide.human.fuP = 0.16; +db.Lorcainide.human.BP = 0.77; +db.Lorcainide.human.CLblood_kgBW.hep = 18/1000; % Obach99 + +db.Maprotiline.name = 'Maprotiline'; +db.Maprotiline.mode = 'B'; +db.Maprotiline.MW = 277.4; +db.Maprotiline.pKa = 9.9; +db.Maprotiline.logPow = 4.75; +db.Maprotiline.human.fuP = 0.12; +db.Maprotiline.human.BP = 2.13; + +db.Meperidine.name = 'Meperidine'; +db.Meperidine.mode = 'B'; +db.Meperidine.MW = 247.33; +db.Meperidine.pKa = 8.64; +db.Meperidine.logPow = 2.72; +db.Meperidine.human.fuP = 0.38; +db.Meperidine.human.BP = 1.11; + +db.Mepivacaine.name = 'Mepivacaine'; +db.Mepivacaine.mode = 'B'; +db.Mepivacaine.MW = 246.35; +db.Mepivacaine.pKa = 7.76; +db.Mepivacaine.logPow = 1.88; +db.Mepivacaine.human.fuP = 0.23; +db.Mepivacaine.human.BP = 0.91; +db.Mepivacaine.human.CLblood_kgBW.hep = 11.1/1000; + +db.Metclopramide.name = 'Metclopramide'; +db.Metclopramide.mode = 'B'; +db.Metclopramide.MW = 299.8; +db.Metclopramide.pKa = 9.3; +db.Metclopramide.logPow = 2.6; +db.Metclopramide.human.fuP = 0.65; +db.Metclopramide.human.BP = 1.08; +db.Metclopramide.human.CLblood_kgBW.hep = 9.5/1000; % 8-11 + +db.Methadone.name = 'Methadone'; +db.Methadone.mode = 'B'; +db.Methadone.MW = 309.45; +db.Methadone.pKa = 8.94; +db.Methadone.logPow = 3.93; +db.Methadone.human.fuP = 0.13; +db.Methadone.human.BP = 0.77; +db.Methadone.human.CLblood_kgBW.hep = 15/1000; % 0.33-30 + +db.Metolazone.name = 'Metolazone'; +db.Metolazone.mode = 'B'; +db.Metolazone.MW = 365.84; +db.Metolazone.pKa = 9.7; +db.Metolazone.logPow = 1.8; +db.Metolazone.human.fuP = 0.057; +db.Metolazone.human.BP = 1.13; +db.Metolazone.human.CLblood_kgBW.hep = NaN; + +db.Metoprolol.name = 'Metoprolol'; +db.Metoprolol.mode = 'B'; +db.Metoprolol.MW = 267.36; +db.Metoprolol.pKa = 9.7; +db.Metoprolol.logPow = 2.15; +db.Metoprolol.human.fuP = 0.9; +db.Metoprolol.human.BP = 1.14; +db.Metoprolol.human.CLblood_kgBW.hep = 12.15/1000; + +db.Mexiletine.name = 'Mexiletine'; +db.Mexiletine.mode = 'B'; +db.Mexiletine.MW = 179.26; +db.Mexiletine.pKa = 9; +db.Mexiletine.logPow = 2.15; +db.Mexiletine.human.fuP = 0.34; +db.Mexiletine.human.BP = 1.12; +db.Mexiletine.human.CLblood_kgBW.hep = 7.3/1000; % 6.3-8.3 + +db.Morphine.name = 'Morphine'; +db.Morphine.mode = 'B'; +db.Morphine.MW = 285.34; +db.Morphine.pKa = 8.35; +db.Morphine.logPow = 0.89; +db.Morphine.human.fuP = 0.71; +db.Morphine.human.BP = 1.02; +db.Morphine.human.CLblood_kgBW.hep = 18/1000; % 8-27 + +db.Naloxone.name = 'Naloxone'; +db.Naloxone.mode = 'B'; +db.Naloxone.MW = 327.37; +db.Naloxone.pKa = 7.9; +db.Naloxone.logPow = 2.1; +db.Naloxone.human.fuP = 0.59; +db.Naloxone.human.BP = 1.22; +db.Naloxone.human.CLblood_kgBW.hep = 22/1000; + +db.Nicardipine.name = 'Nicardipine'; +db.Nicardipine.mode = 'B'; +db.Nicardipine.MW = 479.53; +db.Nicardipine.pKa = 8.6; +db.Nicardipine.logPow = 3.82; +db.Nicardipine.human.fuP = 0.04; +db.Nicardipine.human.BP = 0.71; +db.Nicardipine.human.CLblood_kgBW.hep = 12/1000; % 7-17 + +db.Nitrofurantoin.name = 'Nitrofurantoin'; +db.Nitrofurantoin.mode = 'B'; +db.Nitrofurantoin.MW = 238.16; +db.Nitrofurantoin.pKa = 7.2; +db.Nitrofurantoin.logPow = -0.47; +db.Nitrofurantoin.human.fuP = 0.39; +db.Nitrofurantoin.human.BP = 0.76; + +db.Nortriptyline.name = 'Nortriptyline'; +db.Nortriptyline.mode = 'B'; +db.Nortriptyline.MW = 263.38; +db.Nortriptyline.pKa = 9.7; +db.Nortriptyline.logPow = 4.28; +db.Nortriptyline.human.fuP = 0.05; +db.Nortriptyline.human.BP = 1.58; + +db.Ondansetron.name = 'Ondansetron'; +db.Ondansetron.mode = 'B'; +db.Ondansetron.MW = 293.36; +db.Ondansetron.pKa = 7.4; +db.Ondansetron.logPow = 1.92; +db.Ondansetron.logPvow = NaN; +db.Ondansetron.human.fuP = 028; +db.Ondansetron.human.BP = 0.83; +db.Ondansetron.human.CLblood_kgBW.hep = 5.9/1000; % 4.3-10 + +db.Oxprenolol.name = 'Oxprenolol'; +db.Oxprenolol.mode = 'B'; +db.Oxprenolol.MW = 265.35; +db.Oxprenolol.pKa = 9.5; +db.Oxprenolol.logPow = 2.18; +db.Oxprenolol.human.fuP = 0.14; +db.Oxprenolol.human.BP = 0.64; + +db.Pentazocine.name = 'Pentazocine'; +db.Pentazocine.mode = 'B'; +db.Pentazocine.pKa = 8.5; +db.Pentazocine.logPow = 3.31; +db.Pentazocine.logPvow = 2.34; +db.Pentazocine.rat.fuP = 0.46; +db.Pentazocine.rat.BP = 1.55; +db.Pentazocine.rat.E.hep = NaN; +db.Pentazocine.human.fuP = 0.37; % RR07 +db.Pentazocine.human.BP = 1.07; % RR07 +db.Pentazocine.human.CLblood_kgBW.hep = 18/1000; % 17-20 ml/min/kg -> l/min +db.Pentazocine.human.E.feces = 0.21; % 11-32 % + +db.Phencyclidine.name = 'Phencyclidine'; +db.Phencyclidine.mode = 'B'; +db.Phencyclidine.MW = 285.42; +db.Phencyclidine.pKa = 9.4; +db.Phencyclidine.logPow = 4.96; +db.Phencyclidine.logPvow = 4.10; +db.Phencyclidine.rat.fuP = 0.47; +db.Phencyclidine.rat.BP = 1.12; +db.Phencyclidine.rat.E.hep = 5.4/1000; % human value +db.Phencyclidine.human.fuP = 0.35; % 30-40 % +db.Phencyclidine.human.CLblood_kgBW.hep = 5.4/1000; % ml/min/kg -> l/min/kg +db.Phencyclidine.human.E.feces = 0.7; % 50-90 % + +db.Pindolol.name = 'Pindolol'; +db.Pindolol.mode = 'B'; +db.Pindolol.MW = 248.32; +db.Pindolol.pKa = 8.8; +db.Pindolol.logPow = 1.75; +db.Pindolol.human.fuP = 0.41; +db.Pindolol.human.BP = 0.81; +db.Pindolol.human.CLblood_kgBW.hep = 4.2/1000; + +db.Prilocaine.name = 'Prilocaine'; +db.Prilocaine.mode = 'B'; +db.Prilocaine.MW = 220.31; +db.Prilocaine.pKa = 7.9; +db.Prilocaine.logPow = 2.11; +db.Prilocaine.human.fuP = 0.61; +db.Prilocaine.human.BP = 1.18; +db.Prilocaine.human.CLblood_kgBW.hep = 34/1000; + +db.Prilocaine.name = 'Prilocaine'; +db.Prilocaine.mode = 'B'; +db.Prilocaine.MW = 220.31; +db.Prilocaine.pKa = 7.9; +db.Prilocaine.logPow = 2.11; +db.Prilocaine.human.fuP = 0.61; +db.Prilocaine.human.BP = 1.18; +db.Prilocaine.human.CLblood_kgBW.hep = 34/1000; + +db.Procainamide.name = 'Procainamide'; +db.Procainamide.mode = 'B'; +db.Procainamide.MW = 235.33; +db.Procainamide.pKa = 9.2; +db.Procainamide.logPow = 0.88; +db.Procainamide.logPvow = -0.37; +db.Procainamide.rat.fuP = 0.92; +db.Procainamide.rat.BP = 1.00; +db.Procainamide.rat.E.hep = 9.2/1000; % human value +db.Procainamide.human.fuP = 0.84; % RR07 +db.Procainamide.human.BP = 0.98; % RR07 +db.Procainamide.human.CLblood_kgBW.hep = 9.2/1000; % 8.6-9.8 ml/min/kg -> l/min/kg +db.Procainamide.human.E.feces = 0.8; % 75-85 % + +db.Propafenone.name = 'Propafenone'; +db.Propafenone.mode = 'B'; +db.Propafenone.MW = 341.44; +db.Propafenone.pKa = 9.74; +db.Propafenone.logPow = 4.24; +db.Propafenone.human.fuP = 0.05; +db.Propafenone.human.BP = 0.7; +db.Propafenone.human.CLblood_kgBW.hep = 19/1000; % Obach99 + +db.Propranolol.name = 'Propranolol'; +db.Propranolol.mode = 'B'; +db.Propranolol.MW = 259.34; +db.Propranolol.pKa = 9.45; +db.Propranolol.logPow = 3.65; +db.Propranolol.human.fuP = 0.11; +db.Propranolol.human.BP = 0.8; +db.Propranolol.human.CLblood_kgBW.hep = 13/1000; + +db.Remoxipride.name = 'Remoxipride'; +db.Remoxipride.mode = 'B'; +db.Remoxipride.MW = 371.27; +db.Remoxipride.pKa = 8.9; +db.Remoxipride.logPow = 2.1; +db.Remoxipride.human.fuP = 0.2; +db.Remoxipride.human.BP = 0.7; +db.Remoxipride.human.CLblood_kgBW.hep = 1.5/1000; + +db.Ropivacaine.name = 'Ropivacaine'; +db.Ropivacaine.mode = 'B'; +db.Ropivacaine.MW = 274.4; +db.Ropivacaine.pKa = 8.2; +db.Ropivacaine.logPow = 2.09; +db.Ropivacaine.human.fuP = 0.07; +db.Ropivacaine.human.BP = 0.69; +db.Ropivacaine.human.CLblood_kgBW.hep = 0.12/1000; + +db.Sildefanil.name = 'Sildefanil'; +db.Sildefanil.mode = 'B'; +db.Sildefanil.MW = 474.58; +db.Sildefanil.pKa = 7.6; +db.Sildefanil.logPow = 2.75; +db.Sildefanil.human.fuP = 0.04; +db.Sildefanil.human.BP = 0.62; +db.Sildefanil.human.CLblood_kgBW.hep = 6/1000; + +db.Theophylline.name = 'Theophylline'; +db.Theophylline.mode = 'B'; +db.Theophylline.MW = 180.16; +db.Theophylline.pKa = 8.71; +db.Theophylline.logPow = 0.26; +db.Theophylline.human.fuP = 0.6; +db.Theophylline.human.BP = 0.83; +db.Theophylline.human.CLblood_kgBW.hep = 0.65/1000; + +db.Tolamolol.name = 'Tolamolol'; +db.Tolamolol.mode = 'B'; +db.Tolamolol.MW = NaN; +db.Tolamolol.pKa = 7.94; +db.Tolamolol.logPow = 2.19; +db.Tolamolol.human.fuP = 0.09; +db.Tolamolol.human.BP = 0.76; + +db.Verapamil.name = 'Verapamil'; +db.Verapamil.mode = 'B'; +db.Verapamil.MW = 454.60; +db.Verapamil.pKa = 8.5; +db.Verapamil.logPow = 3.79; +db.Verapamil.logPvow = 2.88; +db.Verapamil.rat.fuP = 0.05; +db.Verapamil.rat.BP = 0.85; +db.Verapamil.rat.E.hep = NaN; +db.Verapamil.human.fuP = 0.082; % RR07 +db.Verapamil.human.BP = 0.84; % RR07 +db.Verapamil.human.CLblood_kgBW.hep = 19/1000; % Obach99 +db.Verapamil.human.E.feces = 0.22; % 12-33 % + +% --- Weak Bases -------------------------------------------------------- + +db.Alfentanil.name = 'Alfentanil'; +db.Alfentanil.mode = 'wB'; +db.Alfentanil.MW = 416.5172; +db.Alfentanil.pKa = 6.5; +db.Alfentanil.logPow = 2.2; +db.Alfentanil.rat.fuP = 0.11; +db.Alfentanil.rat.E.hep = NaN; +db.Alfentanil.human.fuP = 0.08; % 5-12 % +db.Alfentanil.human.BP = 1; +db.Alfentanil.human.CLblood_kgBW.hep = 4/1000; % 2.3-6.5 ml/min/kg -> l/min/kg + +db.Alprazolam.name = 'Alprazolam'; +db.Alprazolam.mode = 'wB'; +db.Alprazolam.MW = 416.5172; +db.Alprazolam.pKa = 2.4; +db.Alprazolam.logPow = 2.3; +db.Alprazolam.rat.fuP = 0.35; +db.Alprazolam.rat.E.hep = NaN; +db.Alprazolam.human.fuP = 0.32; % Obach99 +db.Alprazolam.human.BP = 0.78; % Obach99 +db.Alprazolam.human.CLblood_kgBW.hep = 0.76/1000; % Obach99 +db.Alprazolam.human.E.feces = 0.85; % 80-90 % + +db.Chlordiazepoxide.name = 'Chlordiazepoxide'; +db.Chlordiazepoxide.mode = 'wB'; +db.Chlordiazepoxide.MW = 299.755; +db.Chlordiazepoxide.pKa = 4.7; +db.Chlordiazepoxide.logPow = 2.4; +db.Chlordiazepoxide.rat.fuP = 0.15; +db.Chlordiazepoxide.rat.E.hep = NaN; +db.Chlordiazepoxide.human.fuP = 0.15; +db.Chlordiazepoxide.human.BP = 1.4; +db.Chlordiazepoxide.human.CLblood_kgBW.hep = 1.8/1000/73; % ml/min/(73 kg body weight) + +db.Diazepam.name = 'Diazepam'; +db.Diazepam.mode = 'wB'; +db.Diazepam.MW = 284.74; +db.Diazepam.pKa = 3.4; +db.Diazepam.logPow = 2.84; +db.Diazepam.rat.fuP = 0.14; +db.Diazepam.rat.E.hep = NaN; +db.Diazepam.human.fuP = 0.013; % Obach99 +db.Diazepam.human.BP = 0.71; % Obach99 +db.Diazepam.human.CLblood_kgBW.hep = 0.6/1000; % Obach99 +db.Diazepam.human.E.feces = 0.97; % 94-100 % + +db.Flunitrazepam.name = 'Flunitrazepam'; +db.Flunitrazepam.mode = 'wB'; +db.Flunitrazepam.MW = 313.2832; +db.Flunitrazepam.pKa = 1.8; +db.Flunitrazepam.logPow = 2.1; +db.Flunitrazepam.rat.fuP = 0.25; +db.Flunitrazepam.rat.E.hep = NaN; +db.Flunitrazepam.human.fuP = 0.25; +db.Flunitrazepam.human.BP = 1.2; +db.Flunitrazepam.human.CLblood_kgBW.hep = 9.8/1000/73; % ml/min/(73kg BW) -> l/min/kg; +db.Flunitrazepam.human.E.feces = 0.85; + +db.Midazolam.name = 'Midazolam'; +db.Midazolam.mode = 'wB'; +db.Midazolam.MW = 325.767; +db.Midazolam.pKa = 6.0; +db.Midazolam.logPow = 3.15; +db.Midazolam.rat.fuP = 0.059; +db.Midazolam.rat.E.hep = NaN; +db.Midazolam.human.fuP = 0.05; % Obach99 +db.Midazolam.human.BP = 0.567; % Obach99 (changed from 0.53 to 0.567) +db.Midazolam.human.CLblood_kgBW.hep = 8.7/1000; % Obach99 +db.Midazolam.human.lambda_po = 0.16; % 1/min +db.Midazolam.human.E.gut = 0.444; +db.Midazolam.human.E.feces = 0.4; % 28.2; 40-50 + +db.Triazolam.name = 'Triazolam'; +db.Triazolam.mode = 'wB'; +db.Triazolam.MW = 343.21; +db.Triazolam.pKa = 2.0; +db.Triazolam.logPow = 2.4; +db.Triazolam.rat.fuP = 0.28; +db.Triazolam.rat.E.hep = NaN; +db.Triazolam.human.fuP = 0.1; % Obach99 +db.Triazolam.human.BP = 0.62; % Obach99 +db.Triazolam.human.CLblood_kgBW.hep = 4.7/1000; % human value +db.Triazolam.human.E.feces = 1; + + +% --- Neutral ---------------------------------------------------------- + +db.Digoxin.name = 'Digoxin'; +db.Digoxin.mode = 'N'; +db.Digoxin.MW = 780.9385; +db.Digoxin.logPow = 1.23; +db.Digoxin.rat.fuP = 0.61; +db.Digoxin.rat.E.hep = NaN; % to be transformed: 1.8/1000; % human +db.Digoxin.human.fuP = 0.74; % RR07 +db.Digoxin.human.BP = 1; +db.Digoxin.human.CLblood_kgBW.hep = 12.2/60/73; % l/hr/(73 kg BW) -> l/min/kg +db.Digoxin.human.E.feces = 1; + +db.Ethoxybenzamide.name = 'Ethoxybenzamide'; +db.Ethoxybenzamide.mode = 'N'; +db.Ethoxybenzamide.logPow = 0.8; +db.Ethoxybenzamide.rat.fuP = 0.59; + +db.Ftorafur.name = 'Ftorafur'; +db.Ftorafur.mode = 'N'; +db.Ftorafur.logPow = -0.3; +db.Ftorafur.rat.fuP = 0.78; + + +% --- Acids ------------------------------------------------------------ + +db.Amobarbital.name = 'Amobarbital'; +db.Amobarbital.mode = 'A'; +db.Amobarbital.MW = 226.27; +db.Amobarbital.pKa = 7.9; +db.Amobarbital.logPow = 1.89; +db.Amobarbital.human.fuP = 0.39; +db.Amobarbital.human.BP = 1.5; +db.Amobarbital.human.CLblood_kgBW.hep = 0.35/1000; + +db.Cefazolin.name = 'Cefazolin'; +db.Cefazolin.mode = 'A'; +db.Cefazolin.MW = 454.507; +db.Cefazolin.pKa = 2.3; +db.Cefazolin.logPow = 0.3; +db.Cefazolin.rat.fuP = 0.16; + +db.Diclofenac.name = 'Diclofenac'; +db.Diclofenac.mode = 'A'; +db.Diclofenac.MW = 296.15; +db.Diclofenac.pKa = 4.15; +db.Diclofenac.logPow = 3.9; +db.Diclofenac.human.fuP = 0.005; +db.Diclofenac.human.BP = 0.567; +db.Diclofenac.human.CLblood_kgBW.hep = 7.6/1000; + +db.Dideoxyinosine.name = 'Dideoxyinosine'; +db.Dideoxyinosine.mode = 'A'; +db.Dideoxyinosine.MW = 236.2273; +db.Dideoxyinosine.pKa = 9.1; +db.Dideoxyinosine.logPow = -1.2; +db.Dideoxyinosine.rat.fuP = 0.97; +db.Dideoxyinosine.human.fuP = 0.96; % RR07 + +db.Etodolac.name = 'Etodolac'; +db.Etodolac.mode = 'A'; +db.Etodolac.MW = 287.3535; +db.Etodolac.pKa = 4.7; +db.Etodolac.logPow = 3.6; +db.Etodolac.human.fuP = 0.01; +db.Etodolac.human.CLblood_kgBW.hep = 0.68/1000; % ml/min/kg + +db.Hexobarbital.name = 'Hexobarbital'; +db.Hexobarbital.mode = 'A'; +db.Hexobarbital.MW = 236.27; +db.Hexobarbital.pKa = 8.29; +db.Hexobarbital.logPow = 1.74; +db.Hexobarbital.human.fuP = 0.53; +db.Hexobarbital.human.BP = 1; +db.Hexobarbital.human.CLblood_kgBW.hep = 3.6/1000; + +db.Ibuprofen.name = 'Ibuprofen'; +db.Ibuprofen.mode = 'A'; +db.Ibuprofen.MW = 206.28; +db.Ibuprofen.pKa = 4.7; +db.Ibuprofen.logPow = 4.06; +db.Ibuprofen.human.fuP = 0.01; +db.Ibuprofen.human.BP = 0.567; +db.Ibuprofen.human.CLblood_kgBW.hep = 1.5/1000; + +db.Methohexital.name = 'Methohexital'; +db.Methohexital.mode = 'A'; +db.Methohexital.MW = 262.3; +db.Methohexital.pKa = 8.51; +db.Methohexital.logPow = 1.72; +db.Methohexital.human.fuP = 0.27; +db.Methohexital.human.BP = 0.7; +db.Methohexital.human.CLblood_kgBW.hep = 16/1000; + +db.Penicillin.name = 'Penicillin'; +db.Penicillin.mode = 'A'; +db.Penicillin.MW = 350.39; +db.Penicillin.pKa = 2.7; +db.Penicillin.logPow = 1.6; +db.Penicillin.rat.fuP = 0.15; +db.Penicillin.human.fuP = 0.2; + +db.Phenobarbital.name = 'Phenobarbital'; +db.Phenobarbital.mode = 'A'; +db.Phenobarbital.MW = 232.2353; +db.Phenobarbital.pKa = 7.4; +db.Phenobarbital.logPow = 1.6; +db.Phenobarbital.rat.fuP = 0.64; +db.Phenobarbital.rat.E.hep = NaN; +db.Phenobarbital.human.fuP = 0.52; % RR07 +db.Phenobarbital.human.BP = 0.86; +db.Phenobarbital.human.CLblood_kgBW.hep = 3.8/60/1000; % ml/h/kg -> l/min/kg +db.Phenobarbital.human.lambda_po = 0.03; % 1/min +db.Phenobarbital.human.E.feces = 0.8; % 80,95-100 % + +db.Phenytoin.name = 'Phenytoin'; +db.Phenytoin.mode = 'A'; +db.Phenytoin.MW = 252.268; +db.Phenytoin.pKa = 8.3; +db.Phenytoin.logPow = 2.5; +db.Phenytoin.rat.fuP = 0.19; +db.Phenytoin.human.fuP = 0.12; % RR07 +db.Phenytoin.human.E.feces = 0.9; % 85-95 % + +db.SalicyclicAcid.name = 'SalicyclicAcid'; +db.SalicyclicAcid.mode = 'A'; +db.SalicyclicAcid.MW = 138.1207; +db.SalicyclicAcid.pKa = 3.0; +db.SalicyclicAcid.logPow = 2.0; +db.SalicyclicAcid.rat.fuP = 0.137; + +db.Thiopental.name = 'Thiopental'; +db.Thiopental.mode = 'A'; +db.Thiopental.MW = 242.338; +db.Thiopental.pKa = 7.5; +db.Thiopental.logPow = 2.9; +db.Thiopental.rat.fuP = 0.13; +db.Thiopental.rat.E.hep = NaN; +db.Thiopental.human.fuP = 0.18; % RR07 +db.Thiopental.human.BP = 0.88; % unknown source +db.Thiopental.human.CLblood_kgBW.hep = 2.02/1000; % 1.9-5.4 ml/min/kg -> l/min/kg + +db.Tolbutamide.name = 'Tolbutamide'; +db.Tolbutamide.mode = 'A'; +db.Tolbutamide.MW = 270.35; +db.Tolbutamide.pKa = 5.3; +db.Tolbutamide.logPow = 2.39; +db.Tolbutamide.rat.fuP = 0.27; +db.Tolbutamide.rat.E.hep = NaN; +db.Tolbutamide.human.fuP = 0.04; % Obach99 +db.Tolbutamide.human.BP = 0.567; % Obach99 +db.Tolbutamide.human.CLblood_kgBW.hep = 0.36/1000; % Obach99 +db.Tolbutamide.human.E.feces = 0.95; + +db.Valproate.name = 'Valproate'; +db.Valproate.mode = 'A'; +db.Valproate.MW = 144.2114; +db.Valproate.pKa = 4.6; +db.Valproate.logPow = 2.76; +db.Valproate.rat.fuP = 0.37; +db.Valproate.human.fuP = 0.099; % RR07 +db.Valproate.human.BP = 0.567; % E:P -> B:P +db.Valproate.human.CLblood_kgBW.hep = 0.11/1000; % 0.09-0.46 ml/min/kg -> l/min/kg + +db.Warfarin.name = 'Warfarin'; +db.Warfarin.mode = 'A'; +db.Warfarin.MW = 308.33; +db.Warfarin.pKa = 5.08; +db.Warfarin.logPow = 3; +db.Warfarin.human.fuP = 0.01; +db.Warfarin.human.BP = 0.567; +db.Warfarin.human.CLblood_kgBW.hep = 0.081/1000; + +% --- Zwitterions --------------------------------------------------------- + +db.Ofloxacin.name = 'Ofloxacin'; +db.Ofloxacin.mode = 'AB'; +db.Ofloxacin.pKa = [6.1 8.2]; +db.Ofloxacin.logPow = -0.4; +db.Ofloxacin.rat.fuP = 0.77; +db.Ofloxacin.rat.BP = 0.92; + +db.Enoxacin.name = 'Enoxacin'; +db.Enoxacin.mode = 'AB'; +db.Enoxacin.pKa = [6.1 8.2]; +db.Enoxacin.logPow = -0.4; +db.Enoxacin.rat.fuP = 0.77; +db.Enoxacin.rat.BP = 0.92; + +db.Lomefloxacin.name = 'Lomefloxacin'; +db.Lomefloxacin.mode = 'AB'; +db.Lomefloxacin.pKa = [6.1 8.2]; +db.Lomefloxacin.logPow = -0.4; +db.Lomefloxacin.rat.fuP = 0.77; +db.Lomefloxacin.rat.BP = 0.92; + +db.Pefloxacin.name = 'Pefloxacin'; +db.Pefloxacin.mode = 'AB'; +db.Pefloxacin.pKa = [6.1 8.2]; +db.Pefloxacin.logPow = -0.4; +db.Pefloxacin.rat.fuP = 0.77; +db.Pefloxacin.rat.BP = 0.92; + +db.PipemidicAcid.name = 'PipemidicAcid'; +db.PipemidicAcid.mode = 'AB'; +db.PipemidicAcid.pKa = [6.1 8.2]; +db.PipemidicAcid.logPow = -0.4; +db.PipemidicAcid.rat.fuP = 0.77; +db.PipemidicAcid.rat.BP = 0.92; + +db.Tetracycline.name = 'Tetracycline'; +db.Tetracycline.mode = 'AB'; +db.Tetracycline.pKa = [6.1 8.2]; +db.Tetracycline.logPow = -0.4; +db.Tetracycline.rat.fuP = 0.77; +db.Tetracycline.rat.BP = 0.92; + +db.Ceftazidime.name = 'Ceftazidime'; +db.Ceftazidime.mode = 'AB'; +db.Ceftazidime.pKa = [6.1 8.2]; +db.Ceftazidime.logPow = -0.4; +db.Ceftazidime.rat.fuP = 0.77; +db.Ceftazidime.rat.BP = 0.92; + +db.NalidixicAcid.name = 'NalidixicAcid'; +db.NalidixicAcid.mode = 'AwB'; +db.NalidixicAcid.pKa = [6.1 8.2]; +db.NalidixicAcid.logPow = -0.4; +db.NalidixicAcid.rat.fuP = 0.77; +db.NalidixicAcid.rat.BP = 0.92; + +db.Clonazepam.name = 'Clonazepam'; +db.Clonazepam.mode = 'AwB'; +db.Clonazepam.MW = 315.17; +db.Clonazepam.pKa = [1.9 2.53 3.75]; +db.Clonazepam.logPow = 2.41; +db.Clonazepam.human.fuP = 0.21; +db.Clonazepam.human.BP = 1.4; +db.Clonazepam.human.CLblood_kgBW.hep = NaN; % to be transformed: 3.4; % ml/min ! + +db.Lorazepam.name = 'Lorazepam'; +db.Lorazepam.mode = 'AwB'; +db.Lorazepam.MW = 321.16; +db.Lorazepam.pKa = [1.3 12.25]; +db.Lorazepam.logPow = 2.45; +db.Lorazepam.human.fuP = 0.09; +db.Lorazepam.human.BP = 1; +db.Lorazepam.human.CLblood_kgBW.hep = NaN; % to be transformed: 1.1./1000; + +db.Oxazepam.name = 'Oxazepam'; +db.Oxazepam.mode = 'AwB'; +db.Oxazepam.MW = 286.71; +db.Oxazepam.pKa = [2.03 11.6]; +db.Oxazepam.logPow = 2.24; +db.Oxazepam.human.fuP = 0.05; +db.Oxazepam.human.BP = 1.11; +db.Oxazepam.human.CLblood_kgBW.hep = NaN; % to be transformed: 1.1./1000; + +db.Tenoxicam.name = 'Tenoxicam'; +db.Tenoxicam.mode = 'AwB'; +db.Tenoxicam.MW = 337.37; +db.Tenoxicam.pKa = [1.01 5.3]; +db.Tenoxicam.logPow = 1.9; +db.Tenoxicam.human.fuP = 0.009; +db.Tenoxicam.human.BP = 0.67; +db.Tenoxicam.human.CLblood_kgBW.hep = NaN; % to be transformed: 0.03./1000; + +% ----- di-bases ------ + +db.Chlorpromazine.name = 'Chlorpromazine'; +db.Chlorpromazine.mode = 'BB'; +db.Chlorpromazine.MW = 318.86; +db.Chlorpromazine.pKa = [6.4 9.7]; +db.Chlorpromazine.logPow = .42; +db.Chlorpromazine.human.fuP = 0.036; +db.Chlorpromazine.human.BP = 1.17; +db.Chlorpromazine.human.CLblood_kgBW.hep = NaN; % to be transformed: 11/1000; % Obach99 + +db.Clozapine.name = 'Clozapine'; +db.Clozapine.mode = 'BB'; +db.Clozapine.MW = 326.82; +db.Clozapine.pKa = [3.7 7.7]; +db.Clozapine.logPow = 3.42; +db.Clozapine.human.fuP = 0.05; +db.Clozapine.human.BP = 0.86; +db.Clozapine.human.CLblood_kgBW.hep = NaN; % to be transformed: 2.9/1000; % Obach99 + +db.Ethambutol.name = 'Ethambutol'; +db.Ethambutol.mode = 'BB'; +db.Ethambutol.MW = 204.31; +db.Ethambutol.pKa = [6.3 9.5]; +db.Ethambutol.logPow = -0.4; +db.Ethambutol.human.fuP = 0.84; +db.Ethambutol.human.BP = 1.33; + +db.Promethazine.name = 'Promethazine'; +db.Promethazine.mode = 'BB'; +db.Promethazine.MW = 284.42; +db.Promethazine.pKa = [6.5 9.6]; +db.Promethazine.logPow = 4.96; +db.Promethazine.human.fuP = 0.16; +db.Promethazine.human.BP = 1.5; +db.Promethazine.human.CLblood_kgBW.hep = NaN; % to be transformed: 16/1000; + +db.Quinidine.name = 'Quinidine'; +db.Quinidine.mode = 'BB'; +db.Quinidine.MW = 324.42; +db.Quinidine.pKa = [5.4 10]; +db.Quinidine.logPow = 3.44; +db.Quinidine.human.fuP = 0.17; +db.Quinidine.human.BP = 0.96; +db.Quinidine.human.CLblood_kgBW.hep = NaN; % to be transformed: 2.7/1000; % Obach99 + +db.Sematilide.name = 'Sematilide'; +db.Sematilide.mode = 'BB'; +db.Sematilide.pKa = [7.6 9.5]; +db.Sematilide.logPow = 1.36; +db.Sematilide.human.fuP = 0.96; +db.Sematilide.human.BP = 1.09; +db.Sematilide.human.CLblood_kgBW.hep = NaN; % to be transformed: 3.7/1000; + +db.Sotalol.name = 'Sotalol'; +db.Sotalol.mode = 'BB'; +db.Sotalol.MW = 272.36; +db.Sotalol.pKa = [8.25 9.8]; +db.Sotalol.logPow = 0.53; +db.Sotalol.human.fuP = 1; +db.Sotalol.human.BP = 1.07; +db.Sotalol.human.CLblood_kgBW.hep = NaN; % to be transformed: 1.8/1000; % 1.5-2.1 + +db.Sulpiride.name = 'Sulpiride'; +db.Sulpiride.mode = 'BB'; +db.Sulpiride.MW = 341.43; +db.Sulpiride.pKa = [9.01 10.19]; +db.Sulpiride.logPow = 0.57; +db.Sulpiride.human.fuP = 1; +db.Sulpiride.human.BP = 1; +db.Sulpiride.human.CLblood_kgBW.hep = NaN; % to be transformed: 1.5/1000; + +db.Tiapamil.name = 'Tiapamil'; +db.Tiapamil.mode = 'BB'; +db.Tiapamil.pKa = [7 9]; +db.Tiapamil.logPow = 2.6; +db.Tiapamil.human.fuP = 0.33; +db.Tiapamil.human.BP = 1; + +db.Timolol.name = 'Timolol'; +db.Timolol.mode = 'BB'; +db.Timolol.MW = 316.42; +db.Timolol.pKa = [8.8 9.21]; +db.Timolol.logPow = 1.91; +db.Timolol.human.fuP = 0.4; +db.Timolol.human.BP = 0.81; +db.Timolol.human.CLblood_kgBW.hep = NaN; % to be transformed: 9.17/1000; + +db.Trimethoprim.name = 'Trimethoprim'; +db.Trimethoprim.mode = 'BB'; +db.Trimethoprim.MW = 290.32; +db.Trimethoprim.pKa = [6.6 7.16]; +db.Trimethoprim.logPow = 0.91; +db.Trimethoprim.human.fuP = 0.48; +db.Trimethoprim.human.BP = 1.28; + + +% --- virtual drug for testing purposes--------------------------------- + +db.drugA.name = 'drugX'; +db.drugA.mode = 'B'; +db.drugA.MW = 500; +db.drugA.pKa = 9; +db.drugA.logPow = 2.5; +db.drugA.human.fuP = 1; +db.drugA.human.BP = 1.1; +db.drugA.human.CLblood_kgBW.hep = NaN; +db.drugA.human.E.feces = 1; +db.drugA.human.lambda_po = 0.05; + + + +%%%%%%%%%%%%%%%% assign values for the model drug %%%%%%%%%%%%%%%%%%%%%%%% + +drug = db.(compound); drug.unit = db.unit; + +% renal blood clearance not implemented yet. +if db.(compound).human.CLblood_kgBW.ren ~= 0 + error('Renal clearance not yet implemented due to '); +end; + + diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_graphicalOutput.m b/src/utils/PKHuisinga/GenericPBPKmodel_graphicalOutput.m new file mode 100644 index 0000000..2563157 --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_graphicalOutput.m @@ -0,0 +1,240 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function plots the diverse graphics to illustrate the simulation +%%% results. +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function out = GenericPBPKmodel_graphicalOutput(population,individual,drug,study,ShowGraphics) + + +OK = 1; % for error reporting. Do not change. + +%%% Plot venous blood concentrations for entire population +%%% +if strcmp(ShowGraphics.allindividual.plasma,'yes') + + %%% plot venous blood concentrations for entire population + figNr = ShowGraphics.allindividual.figNr; + figure(figNr); clf; + T = individual(1).tissue; + color = char(individual(1).color); + t = individual(1).t; C = individual(1).C; + plot(t/60,C(:,T.ven),color,'LineWidth',1.3); + xlabel('t[h]'); ylabel('C [mg/l] in ven'); + title(sprintf('Compound = %s',drug.name)); + xlim([0, t(end)/60]); + ymin = ShowGraphics.lowerLimitY; + ymax = max(C(:,T.ven)); + hold on; + for id=2:length(individual) + T = individual(id).tissue; + t = individual(id).t; C = individual(id).C; + color = char(individual(id).color); + plot(t/60,C(:,T.ven),color,'LineWidth',1.3); + ymax = max([ymax, max(C(:,T.ven))]);; + end; + hold off; grid on; ylim([ymin ymax]); + + set(gca,'YScale',ShowGraphics.allindividual.YScale); + + fprintf('\n'); + +end; + + +%%% Give detailed plots for all tissues for specified individuals ----- +%%% +if ~isempty(ShowGraphics.allTissues.individual) + + if sum(ismember(ShowGraphics.allTissues.individual,1:population.size)==0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> ShowGraphics.allTissues.individual contains invalid ids! <---- \n\n'); + return; + end; + + colorVec = ['r','b','k','m','g','y','m','b','g','r','k','y','m']; + figNr = ShowGraphics.allTissues.figNr; figure(figNr); clf; figure(figNr+1); clf; + + for id = ShowGraphics.allTissues.individual + + figure(figNr); hold on; + T = individual(id).tissue; + t = individual(id).t; C = individual(id).C; + ymax = max(max(C(:,T.allTisExVen))); + color = colorVec(find(ShowGraphics.allTissues.individual==id)); + for n=1:12 + subplot(3,4,n); hold on; + tis = T.allTisAlphabetic(n); + plot(t/60,C(:,tis),'Color',color,'LineWidth',1.3); + xlabel('t[h]'); ylabel(sprintf('C_{%s} [mg/l]',T.name{tis})); + xlim([0, t(end)/60]); ylim([ymin ymax]); + set(gca,'YScale',ShowGraphics.allTissues.YScale); + end; + subplot(3,4,2); title(sprintf('drug %s',drug.name),'Fontsize',15); + set(gca,'YScale',ShowGraphics.allTissues.YScale); + + % vene only for individual 'id' + figure(figNr+1); hold on; + plot(t/60,C(:,T.ven),'Color',color,'LineWidth',1.3); + xlabel('t[h]'); ylabel('C [mg/l] in ven'); + xlim([0, t(end)/60]); ylim([ymin ymax]); + set(gca,'YScale',ShowGraphics.allTissues.YScale); + + if ShowGraphics.allTissues.oneFigOnly + title(sprintf('drug %s',drug.name),'Fontsize',15); + else + title(sprintf('individual %d, drug %s',id,drug.name)); + figNr = ShowGraphics.allTissues.figNr+id+2; + end; + + + end; + + +end; + + +%%% Plot all tissues for specified individuals in a single plot (not sclaed +%%% and scaled with the partition coefficient +%%% +if ~isempty(ShowGraphics.individual.allTissues) + + if sum(ismember(ShowGraphics.individual.allTissues,1:population.size)==0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> ShowGraphics.individual.allTissues contains invalid ids! <---- \n\n'); + return; + end; + figNr = ShowGraphics.individual.figNr; + style.adi = 'r--'; style.bon = 'b-'; + style.mus = 'b--'; style.ski = 'r-'; + style.ven = 'g-'; style.lun = 'g--'; + style.art = 'g-'; style.bra = 'm-'; + style.liv = 'k-'; style.gut = 'k--'; + style.spl = 'k--'; style.hea = 'm--'; + style.kid = 'm--'; + + + for id = ShowGraphics.individual.allTissues + + %%% plot all concentration-time profiles in a single plot + figure(figNr); clf; + subplot(1,2,1); hold on; + T = individual(id).tissue; + t = individual(id).t; C = individual(id).C; + ymax = max(max(C(:,T.allTis))); + ymin = ShowGraphics.lowerLimitY;; + for tis = T.allTis + plot(t/60,C(:,tis),style.(T.name{tis}),'LineWidth',1.3); + end; + xlabel('t[h]'); ylabel(sprintf('C_{tis} [mg/l]')); + xlim([0, t(end)/60]); ylim([ymin ymax]); + title(sprintf('individual(%d), drug %s',id,drug.name),'Fontsize',15); + set(gca,'YScale',ShowGraphics.allTissues.YScale); hold off; + + %%% plot all normalized concentration-time profiles in a single plot + subplot(1,2,2); hold on; + CdivKtb = C(:,T.allTis)*diag(1./individual(id).nKtb); + ymax = max(max(CdivKtb)); + ymin = ShowGraphics.lowerLimitY;; + for tis = T.allTis + plot(t/60,CdivKtb(:,tis),style.(T.name{tis}),'LineWidth',1.3); + end; + xlabel('t[h]'); ylabel(sprintf('C_{tis}/K_{tis} [mg/l]')); + xlim([0, t(end)/60]); ylim([ymin ymax]); + set(gca,'YScale',ShowGraphics.allTissues.YScale); + title(sprintf('Normalized drug concentration'),'Fontsize',15); hold off; + legend(T.name(T.allTis),'Location','SouthEast'); + figNr = figNr + 1; + + end; + +end; + + +%%% Plot percentiles for venous blood concentrations for entire population +%%% Only applicable for population of size>=10 +%%% +population.size = length(individual); +if strcmp(ShowGraphics.percentiles.plasma,'yes') && (population.size>=10) + + figure(ShowGraphics.percentiles.figNr); clf; + + subplot(1,2,1); + plot(population.BW,population.BH,'o'); + xlabel('BW'); ylabel('BH'); + + if length(study.observationTime)==2 + fprintf('\n\n --> study.observationTime only specified initial and final time point.'); + fprintf('\n For this option to work you have to specify a vector of observation times.'); + fprintf('\n Suggestion: Change study.observationTime = [0 study.Tend] to '); + fprintf('\n study.observationTime = [0:dt:study.Tend] where you replace dt with, e.g, 1'), + fprintf('\n corresponding to observations every minute\n\n'), + error(' DO IT!'); + end; + + subplot(1,2,2); + tspan = individual(1).t/60; + if length(tspan)<5 + fprintf(' Only %d time points specified in study.observationTime! Please increase number for more meaningful graphical representation!'); + beep; + end; + + ind = 2:length(tspan); + Cven_vs_t = NaN*ones(population.size,length(individual(1).t(ind))); + for id=1:length(individual) + Cven_vs_t(id,:) = individual(id).C(ind,T.ven)'; + end; + percentile = prctile(Cven_vs_t,[5 25 50 75 95])'; + hold on; + h = area(tspan(ind),[percentile(:,1)'; diff(percentile,1,2)']'); + set(get(h(1),'Children'),'FaceColor',[1 1 1],'LineStyle','none'); + set(get(h(2),'Children'),'FaceColor',[.9 .9 .9],'LineStyle','none'); + set(get(h(3),'Children'),'FaceColor',[.8 .8 .8],'LineStyle','none'); + set(get(h(4),'Children'),'FaceColor',[.8 .8 .8],'LineStyle','none'); + set(get(h(5),'Children'),'FaceColor',[.9 .9 .9],'LineStyle','none'); + plot(tspan(ind),percentile(:,[1 5]),':k',tspan(ind),percentile(:,[2 4]),'--k',tspan(ind),percentile(:,3),'-k'); + set(gca,'Layer','top'); + + xlabel('t[h]'); ylabel('C [mg/l] in ven'); + title(sprintf('Compound = %s',drug.name)); + xlim([0, tspan(end)]); + ymin = ShowGraphics.lowerLimitY; + ymax = max(max(Cven_vs_t)); + grid on; ylim([ymin ymax]); + set(gca,'YScale',ShowGraphics.percentiles.YScale); + + +end; + +fprintf('\n'); + + diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_graphicalOutputLumping.m b/src/utils/PKHuisinga/GenericPBPKmodel_graphicalOutputLumping.m new file mode 100644 index 0000000..05619af --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_graphicalOutputLumping.m @@ -0,0 +1,76 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function plots the diverse graphics to illustrate the simulation +%%% results for the lumped model. +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function out = GenericPBPKmodel_graphicalOutputLumping(individual,L,drug,ShowGraphics) + +if isempty(L) return; end; + + +%%% --- graphical output ------------------------------------------------- + +figNr = ShowGraphics.lumping.figNr; +for id=ShowGraphics.lumping.individual + + + % all tissues except for vein + figure(figNr); clf; hold on; + T = individual(id).tissue; + ymax = max(max(individual(id).C(:,T.allTisExVen))); + ymin = ShowGraphics.lowerLimitY; + + for n=1:12 + subplot(3,4,n); hold on; + tis = T.allTisAlphabetic(n); + plot(individual(id).t/60,individual(id).C(:,tis),'g',L(id).t/60,L(id).C_fullmodel(:,tis),'k--'); + xlabel('t[h]'); ylabel(sprintf('C_{%s} [mg/l]',T.name{tis})); + xlim([0, L(id).t(end)/60]); ylim([ymin ymax]); + set(gca,'YScale',ShowGraphics.allTissues.YScale); + end; + subplot(3,4,2); title(sprintf('individual %d, drug %s, %s',id,drug.name,ShowGraphics.lumping.type)); + set(gca,'YScale',ShowGraphics.lumping.YScale); + + % vein only + figure(figNr+1); clf; hold on; + plot(individual(id).t/60,individual(id).C(:,T.ven),'g',L(id).t/60,L(id).C_fullmodel(:,T.ven),'k--'); + xlabel('t[h]'); ylabel('C [mg/l] in ven'); + title(sprintf('individual %d, drug %s, %s',id,drug.name,ShowGraphics.lumping.type)); + xlim([0, L(id).t(end)/60]); ylim([ymin ymax]); + set(gca,'YScale',ShowGraphics.lumping.YScale); + figNr = figNr + 2; + +end; + diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_mechanisticLumping.m b/src/utils/PKHuisinga/GenericPBPKmodel_mechanisticLumping.m new file mode 100644 index 0000000..9301d87 --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_mechanisticLumping.m @@ -0,0 +1,191 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function determines all parameters for a lumped compartment model +%%% obtained from the 13-compartment PBPK model and simulates it. +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function L = GenericPBPKmodel_mechanisticLumping(individual,drug,study,lumping) + + +fprintf('\n Compute lumping for id = %d and simulate it. ',individual.id) + +T = individual.tissue; + +L.nKtb = individual.nKtb; +t = individual.t; %Tend = t(end); +nC = individual.C(:,T.allTis)*diag(1./L.nKtb); + +L.lumping = lumping; +L.noOrgans = length(L.lumping); + + +%%% --- determine lumped compartments containing ven, liv and kid +for n=1:L.noOrgans + if ismember(T.ven,L.lumping{n}) L.cen = n; end; + if ismember(T.liv,L.lumping{n}) L.Liv = n; end; + if ismember(T.kid,L.lumping{n}) L.Kid = n; end; +end; +L.not_cen = 1:L.noOrgans; L.not_cen(L.cen) = []; + +%%% --- determine lumped parameters +L.V = zeros(L.noOrgans,1); +L.Q = zeros(L.noOrgans,1); +L.Ktb = zeros(L.noOrgans,1); +for n=1:length(L.lumping) + ind = L.lumping{n}; + L.V(n) = sum(individual.V(ind)); + L.Q(n) = sum(individual.Q(ind)); + L.Ktb(n) = sum(individual.V(ind).*L.nKtb(ind))/L.V(n); +end; +L.Q(L.cen) = sum(L.Q(L.not_cen)); + +L.CL.hep = individual.CLblood.hep; +L.CL.ren = individual.CLblood.ren; + +L.bolus = individual.bolus; +L.infusion = individual.infusion; +L.po = individual.po; + +%%% --- simulate lumped model +C0 = zeros(L.noOrgans+1,1); +C0(L.cen) = L.bolus.dose/L.V(L.cen); +[L.t,L.C] = ode15s(@ODE_RHS,t,C0,[],individual,study,L); + + +C_tis = zeros(1,L.noOrgans); A_metab = 0; +L.t = []; L.C = []; L.A_metab = []; + +Tend = max(study.observationTime); +for d=1:study.numberOfDosing + + C_tis(end,L.cen) = C_tis(end,L.cen)+individual.bolus.dose/L.V(L.cen); + X0 = [C_tis(end,:), A_metab(end)]; + + %%% Simulate system of ODEs + [t,X] = ode15s(@ODE_RHS,study.observationTime,X0',[],individual,study,L); + + t = t + (d-1)*Tend; % transformation from relative time to absolute time + if d~=study.numberOfDosing + t(end) = t(end) - 1e-10; % modify time to account for trough value measurement + end; + C_tis = X(:,1:L.noOrgans); + A_metab = X(:,L.noOrgans+1); + + L.t = [L.t;t]; + L.C = [L.C;C_tis]; + L.A_metab = [L.A_metab;A_metab]; + +end; + + +%%% "unlump" concentrations to original set of tissues +[L.C_fullmodel,L.Atot_fullmodel] = map2detailedModel(L,individual); + + +%%% --- output tissues that are lumped together +fprintf('\n -> %s: lumped %d-cmp model: ',drug.name,L.noOrgans); +for k=1:L.noOrgans + fprintf('{'); + for m=1:length(L.lumping{k}) + fprintf('%s,',char(T.name(L.lumping{k}(m)))); + end; + fprintf('\b},'); +end; +fprintf('\b\n'); + + + +% ================================================================== +function [C_fullmodel,Atot_fullmodel] = map2detailedModel(L,individual) + +% compute predicted original tissue and organ concentrations +T = individual.tissue; +C_fullmodel = zeros(size(L.C(:,1:L.noOrgans),1),T.numberOfTis+1); + +for m = L.noOrgans:-1:1 + for tis = L.lumping{m} + C_fullmodel(:,tis) = L.C(:,m)/L.Ktb(m)*L.nKtb(tis); + end; +end; + +Atot_fullmodel = sum(C_fullmodel(:,T.allTis)*diag(individual.V(T.allTis)),2); + + + +% ================================================================== +function out = ODE_RHS(t,C,individual,study,L) + +T = individual.tissue; +Lmetab = L.noOrgans+1; +cen = L.cen; not_cen = L.not_cen; Liv = L.Liv; Kid = L.Kid; +CL = L.CL; +E = individual.E; +lambda_po = individual.lambda_po; + +% oral dosing +r_po = (1-E.gut)*(1-E.feces)*lambda_po*exp(-t*lambda_po)*(individual.po.dose); +if cen==Liv + r_po = (1-E.hep)*r_po; +end; + +% i.v. infusion +r_iv_infusion = 0; +if (study.infusion.tend > 0) && (t<=study.infusion.tend) + r_iv_infusion = (individual.infusion.dose)/study.infusion.tend; +end; + +% ODEs +VdC = zeros(L.noOrgans,1); +C_blood = C(cen)/L.Ktb(cen); +if L.noOrgans>1 + Cin_cen = sum(L.Q(not_cen).*C(not_cen)./L.Ktb(not_cen)) / L.Q(cen); + + VdC(not_cen) = L.Q(not_cen).*(C_blood - C(not_cen)./L.Ktb(not_cen)); + VdC(cen) = L.Q(cen)*(Cin_cen - C_blood) - CL.hep*C(Liv)/L.Ktb(Liv) -CL.ren*C(Kid)/L.Ktb(Kid) + r_iv_infusion; +else + VdC(cen) = -CL.hep*C(Liv)/L.Ktb(Liv) -CL.ren*C(Kid)/L.Ktb(Kid) + r_iv_infusion; +end; +VdC(Liv) = VdC(Liv) + r_po; + +% converting amounts to concentrations +dC = VdC./L.V; + +% metabolized and excreted compound (mass) +dA_metab = CL.hep*C(Liv)/L.Ktb(Liv) + CL.ren*C(Kid)/L.Ktb(Kid); + +% output vector +out = [dC;dA_metab]; + + + diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_physiology.m b/src/utils/PKHuisinga/GenericPBPKmodel_physiology.m new file mode 100644 index 0000000..33e9879 --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_physiology.m @@ -0,0 +1,863 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function specifies the physiological data for humans of +%%% different age and sex. +%%% Called from the main script GenericPBPKmodel.m +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function [species,T] = GenericPBPKmodel_physiology(speciestype) + +fprintf('\n\n Loading: physiology data') + +%%% ======================================================================================================== +%%% Indexing of organs, tissues and other spaces +%%% +lun = 1; tissue.lun = lun; tissue.name{lun} = 'lun'; +art = 2; tissue.art = art; tissue.name{art} = 'art'; +bra = 3; tissue.bra = bra; tissue.name{bra} = 'bra'; +adi = 4; tissue.adi = adi; tissue.name{adi} = 'adi'; +hea = 5; tissue.hea = hea; tissue.name{hea} = 'hea'; +kid = 6; tissue.kid = kid; tissue.name{kid} = 'kid'; +mus = 7; tissue.mus = mus; tissue.name{mus} = 'mus'; +bon = 8; tissue.bon = bon; tissue.name{bon} = 'bon'; +ski = 9; tissue.ski = ski; tissue.name{ski} = 'ski'; +gut = 10; tissue.gut = gut; tissue.name{gut} = 'gut'; +spl = 11; tissue.spl = spl; tissue.name{spl} = 'spl'; +liv = 12; tissue.liv = liv; tissue.name{liv} = 'liv'; +ven = 13; tissue.ven = ven; tissue.name{ven} = 'ven'; +rob = 14; tissue.rob = rob; tissue.name{rob} = 'rob'; +blo = 15; tissue.blo = blo; tissue.name{blo} = 'blo'; +pla = 16; tissue.pla = pla; tissue.name{pla} = 'pla'; +ery = 17; tissue.ery = ery; tissue.name{ery} = 'ery'; + +tissue.allTis = [lun art bra adi hea kid mus bon ski gut spl liv ven]; +tissue.allTisAndRob = [1:14]; +tissue.numberOfTis = length(tissue.allTis); + +tissue.allTisExVen = [lun art bra adi hea kid mus bon ski gut spl liv ]; +tissue.allTisExBlo = [lun bra adi hea kid mus bon ski gut spl liv ]; +tissue.allTisExAdi = [lun art bra hea kid mus bon ski gut spl liv ven]; +tissue.intoVen = [ bra adi hea kid mus bon ski liv ]; +tissue.intoLiv = [ art gut spl ]; +tissue.V1 = [lun art bra hea kid gut spl liv ven]; +tissue.V2 = [ adi mus bon ski ]; + +tissue.allTisAlphabetic = [adi art bon bra gut hea kid liv lun mus ski spl ven]; +tissue.allTisSVtisScaled = [lun art hea kid mus bon gut spl liv ven]; + +initialize = NaN*ones(1,length(tissue.name)); +tissue.initialize = initialize; + + + +%%% ======================================================================================================== +%%% == human = human = human = human = = human = human = human = human = = human = human = human = human === + +units = 'weight = kg; volume = L; time = min; BH = cm; BSA = m2; '; + +%%% ======================================================================================================== +%%% Model: Age and sex +%%% +%%% Children: +%%% newborn (nb), age 1 (age1u), 5 (age5u), 10 (age10u) , all uni sex +%%% age 15 male (age15m), age 15 female (age15f), +%%% Adults: +%%% age 20-50 male (age35m), age 20-50 female (age35f) +%%% As in the source, we associate an average age of 35 with the adult +%%% +%%% Source: ICRP report +%%% +newborn = 1; age1 = 2; age5 = 3; age10 = 4; age15m = 5; age15f = 6; age35m = 7; age35f = 8; +human.type = 'human'; +human.AgeSexClasses = {'newborn','age1','age5','age10','age15m','age15f','age35m','age35f'}; +human.allAgeClasses = [1:8]; +human.childrenAgeClasses = [1:6]; + + +%%% ======================================================================================================== +%%% Data : Body weight (BW), body height (BH) and body surface area (BSA) +%%% +%%% Units : BW in kg, BH in cm, BSA in m^2 +%%% Source: ICRP report (Table 2.9) +%%% +%%% nb age1 age5 age10 age15m age15f age35m age35f +ICRP.BW = [ 3.50 10.00 19.00 32.00 56.00 53.00 73.00 60.00]; +ICRP.BH = [51.00 76.00 109.00 138.00 167.00 161.00 176.00 163.00]; +ICRP.BSA = [ 0.24 0.48 0.78 1.12 1.62 1.55 1.90 1.66]; +ICRP.sex = {'uni' 'uni' 'uni' 'uni' 'male' 'female' 'male' 'female'}; +ICRP.age = [ 0 1 5 10 15 15 35 35]; + +%%% Assign values to all age/sex classes +for a = human.allAgeClasses + age = char(human.AgeSexClasses(a)); + human.(age).type = 'human'; + human.(age).age = ICRP.age(a); human.(age).unit.age = 'years'; + human.(age).sex = ICRP.sex{a}; + human.(age).tissue = tissue; + human.(age).BW = ICRP.BW(a); human.(age).unit.BW = 'kg'; + human.(age).BH = ICRP.BH(a)/100; human.(age).unit.BH = 'm'; % change of units: cm -> m + human.(age).BSA = ICRP.BSA(a); human.(age).unit.BSA = 'm^2'; + human.(age).BMI = human.(age).BW/human.(age).BH^2; human.(age).unit.BMI = 'kg/m^2'; + + human.(age).tis = tissue.name; +end; + + +%%% ======================================================================================================== +%%% Data : Organ weight, density and volume +%%% +%%% Unit : weight in g, density in g/l, volume in l +%%% Source: ICRP report (Table 2.8) +%%% +%%% nb age1 age5 age10 age15m age15f age35m age35f +ICRP.OW.adipose_tissue = [930 3800 5500 8600 12000 18700 18200 22500]; % entry duplicates other mass information +ICRP.OW.separable_adipose = [890 3600 5000 7500 9500 16000 14500 19000]; +ICRP.OW.adrenals = [ 6 4 5 7 10 9 14 13]; +% alimentary system +ICRP.OW.tongue = [ 3.5 10 19 32 56 53 73 60]; +ICRP.OW.salivary_glands = [ 6 24 34 44 68 65 85 70]; +ICRP.OW.oesophagus_wall = [ 2 5 10 18 30 30 40 35]; +ICRP.OW.stomach_wall = [ 7 20 50 85 120 120 150 140]; +ICRP.OW.stomach_contents = [ 40 67 83 117 200 200 250 230]; +ICRP.OW.small_intestine_wall = [ 30 85 220 370 520 520 650 600]; +ICRP.OW.small_intestine_contents = [ 56 93 117 163 280 280 350 280]; +ICRP.OW.large_intestine_right_colon_wall = [ 7 20 49 85 122 122 150 145]; +ICRP.OW.large_intestine_right_colon_contents = [ 24 40 50 70 120 120 150 160]; +ICRP.OW.large_intestine_left_colon_wall = [ 7 20 49 85 122 122 150 145]; +ICRP.OW.large_intestine_left_colon_contents = [ 12 20 25 35 60 60 75 80]; +ICRP.OW.large_intestine_rectosigmoid_wall = [ 3 10 22 40 56 56 70 70]; +ICRP.OW.large_intestine_rectosigmoid_content = [ 12 20 25 35 60 60 75 80]; +ICRP.OW.liver = [130 330 570 830 1300 1300 1800 1400]; +ICRP.OW.gallbladder_wall = [ 0.5 1.4 2.6 4.4 7.7 7.3 10 8]; +ICRP.OW.gallbladder_content = [ 2.8 8 15 26 45 42 58 48]; +ICRP.OW.pancreas = [ 6 20 35 60 110 100 140 120]; + +%%% Note: brain organ weight age5 = 1245 = mean value of 1310 (male) and 1180 (female) +%%% brain organ weight age10 = 1310 = mean value of 1400 (male) and 1220 (female) +ICRP.OW.brain = [380 950 1245 1310 1420 1300 1450 1300]; +ICRP.OW.breasts = [NaN NaN NaN NaN 15 250 25 500]; +% circulatory system +ICRP.OW.heart_with_blood = [ 46 98 220 370 660 540 840 620]; % entry duplicates other mass information +ICRP.OW.heart_tissue_only = [ 20 50 85 140 230 220 330 250]; +% Table sec 7.4 +ICRP.OW.total_blood = [270 500 1400 2400 4500 3300 5300 3900]; +ICRP.OW.eyes = [ 6 7 11 12 13 13 15 15]; +ICRP.OW.storage_fat = [370 2300 3600 6000 9000 14000 14600 18000]; % entry duplicates other mass information +ICRP.OW.skin = [175 350 570 820 2000 1700 3300 2300]; +ICRP.OW.skeletal_muscle = [800 1900 5600 11000 24000 17000 29000 17500]; +ICRP.OW.pituitary_gland = [ 0.1 0.15 0.25 0.35 0.5 0.5 0.6 0.6]; +% Respiratory system +ICRP.OW.larynx = [ 1.3 4 7 12 22 15 28 19]; +ICRP.OW.trachea = [ 0.5 1.5 2.5 4.5 7.5 6 10 8]; +ICRP.OW.lung_with_blood = [ 60 150 300 500 900 750 1200 950]; % entry duplicates other mass information +ICRP.OW.lung_tissue_only = [ 30 80 125 210 330 290 500 420]; +% skeletal system +ICRP.OW.total_skeleton = [370 1170 2430 4500 7950 7180 10500 7800]; % entry duplicates other mass information +ICRP.OW.bone_cortical = [135 470 1010 1840 3240 2960 4400 3200]; +ICRP.OW.bone_trabecular = [ 35 120 250 460 810 740 1100 800]; +% bone_total = bone_cortical + bone_trabecular +ICRP.OW.bone_total = [170 590 1260 2300 4050 3700 5500 4000]; % entry duplicates other mass information +ICRP.OW.marrow_active = [ 50 150 340 630 1080 1000 1170 900]; +ICRP.OW.marrow_inactive = [ 0 20 160 630 1480 1380 2480 1800]; +ICRP.OW.cartilage = [130 360 600 820 1140 920 1100 900]; +ICRP.OW.teeth = [ 0.7 5 15 30 45 35 50 40]; +ICRP.OW.skeleton_miscellaneous = [ 20 45 55 90 155 145 200 160]; +ICRP.OW.spleen = [ 9.5 29 50 80 130 130 150 130]; + + +%%% Note: thymus organ weight age10 = 37.5 = mean value of 40 (male) and 35 (female) +ICRP.OW.thymus = [ 13 30 30 37.5 35 30 25 20]; +ICRP.OW.thyroid = [ 1.3 1.8 3.4 7.9 12 12 20 17]; +ICRP.OW.tonsils = [ 0.1 0.5 2 3 3 3 3 3]; +% Urogenital system +ICRP.OW.kidneys = [ 25 70 110 180 250 240 310 275]; +ICRP.OW.ureters = [ 0.77 2.2 4.2 7.0 12 12 16 15]; +ICRP.OW.urinary_bladder = [ 4 9 16 25 40 35 50 40]; + +%%% Note: urethra organ weight nb = 0.31 = mean value of 0.48 (male) and 0.14 (female) +%%% urethra organ weight age1 = 0.91 = mean value of 1.4 (male) and 0.42 (female) +%%% urethra organ weight age5 = 1.69 = mean value of 2.6 (male) and 0.78 (female) +%%% urethra organ weight age10 = 2.85 = mean value of 4.4 (male) and 1.3 (female) +ICRP.OW.urethra = [ 0.31 0.91 1.69 2.85 7.7 2.3 10 3]; +ICRP.OW.testes = [ 0.85 1.5 1.7 2 16 0 35 0]; +ICRP.OW.epididymes = [ 0.25 0.35 0.45 0.6 1.6 0 4 0]; +ICRP.OW.prostate = [ 0.8 1.0 1.2 1.6 4.3 0 17 0]; +ICRP.OW.ovaries = [ 0.3 0.8 2.0 3.5 0 6 0 11]; +ICRP.OW.fallopian_tubes = [ 0.25 0.25 0.35 0.50 0 1.1 0 2.1]; +ICRP.OW.uterus = [ 4.0 1.5 3 4 0 30 0 80]; + + +%%% ======================================================================================================== +%%% Data : Regional blood volumes as percentage of total blood volume +%%% +%%% Unit : percent (%) +%%% Source: ICRP report (Table 2.14) +%%% +%%% nb age1 age5 age10 age15m age15f age35m age35f +ICRP.fVblood.fat = [NaN NaN NaN NaN NaN NaN 5.00 8.50]; +ICRP.fVblood.brain = [NaN NaN NaN NaN NaN NaN 1.20 1.20]; +ICRP.fVblood.stomach_and_oesophages = [NaN NaN NaN NaN NaN NaN 1.00 1.00]; +ICRP.fVblood.small_intestine = [NaN NaN NaN NaN NaN NaN 3.80 3.80]; +ICRP.fVblood.large_intestine = [NaN NaN NaN NaN NaN NaN 2.20 2.20]; +ICRP.fVblood.right_heart = [NaN NaN NaN NaN NaN NaN 4.50 4.50]; +ICRP.fVblood.left_heart = [NaN NaN NaN NaN NaN NaN 4.50 4.50]; +ICRP.fVblood.coronary_tissue = [NaN NaN NaN NaN NaN NaN 1.00 1.00]; +ICRP.fVblood.kidneys = [NaN NaN NaN NaN NaN NaN 2.00 2.00]; +ICRP.fVblood.liver = [NaN NaN NaN NaN NaN NaN 10.00 10.00]; +ICRP.fVblood.bronchial_tissue = [NaN NaN NaN NaN NaN NaN 2.00 2.00]; +ICRP.fVblood.skeletal_muscle = [NaN NaN NaN NaN NaN NaN 14.00 10.50]; +ICRP.fVblood.pancreas = [NaN NaN NaN NaN NaN NaN 0.60 0.60]; +ICRP.fVblood.skeleton.total = [NaN NaN NaN NaN NaN NaN 7.00 7.00]; +ICRP.fVblood.skeleton.red_marrow = [NaN NaN NaN NaN NaN NaN 4.00 4.00]; % entry duplicates other information +ICRP.fVblood.skeleton.trabecular_bone = [NaN NaN NaN NaN NaN NaN 1.20 1.20]; % entry duplicates other information +ICRP.fVblood.skeleton.cortical_bone = [NaN NaN NaN NaN NaN NaN 0.80 0.80]; % entry duplicates other information +ICRP.fVblood.skeleton.others = [NaN NaN NaN NaN NaN NaN 1.00 1.00]; % entry duplicates other information +ICRP.fVblood.skin = [NaN NaN NaN NaN NaN NaN 3.00 3.00]; +ICRP.fVblood.spleen = [NaN NaN NaN NaN NaN NaN 1.40 1.40]; +ICRP.fVblood.thyroid = [NaN NaN NaN NaN NaN NaN 0.06 0.06]; +ICRP.fVblood.lymph_nodes = [NaN NaN NaN NaN NaN NaN 0.20 0.20]; +ICRP.fVblood.gonads = [NaN NaN NaN NaN NaN NaN 0.04 0.02]; +ICRP.fVblood.adrenals = [NaN NaN NaN NaN NaN NaN 0.06 0.06]; +ICRP.fVblood.urinary_bladder = [NaN NaN NaN NaN NaN NaN 0.02 0.02]; +ICRP.fVblood.all_other_tissues = [NaN NaN NaN NaN NaN NaN 1.92 1.92]; + + +%%% ======================================================================================================== +%%% Data : Volume of blood plasma and red blood cells +%%% +%%% Unit : ml +%%% Source: ICRP report (Table 2.12) +%%% nb age1 age5 age10 age15m age15f age35m age35f +ICRP.erythrocyte_volume = [NaN NaN NaN NaN NaN NaN 2300 1500]; +ICRP.plasma_volume = [NaN NaN NaN NaN NaN NaN 3000 2400]; + + +%%% ======================================================================================================== +%%% Data : Distribution of blood in the vascular system +%%% +%%% Unit : Percentage (%) of total blood volume +%%% Source: ICRP report (Table 2.13) +%%% nb age1 age5 age10 age15m age15f age35m age35f +ICRP.fVblood.heart_chambers = [NaN NaN NaN NaN NaN NaN 9.00 NaN]; +ICRP.fVblood.pulmonary.total = [NaN NaN NaN NaN NaN NaN 10.50 NaN]; +ICRP.fVblood.pulmonary.arteries = [NaN NaN NaN NaN NaN NaN 3.00 NaN]; +ICRP.fVblood.pulmonary.capillaries = [NaN NaN NaN NaN NaN NaN 2.00 NaN]; +ICRP.fVblood.pulmonary.veins = [NaN NaN NaN NaN NaN NaN 5.50 NaN]; +ICRP.fVblood.systemic.total = [NaN NaN NaN NaN NaN NaN 80.50 NaN]; +ICRP.fVblood.systemic.aorta_large_arteries = [NaN NaN NaN NaN NaN NaN 6.00 NaN]; +ICRP.fVblood.systemic.small_arteries = [NaN NaN NaN NaN NaN NaN 10.00 NaN]; +ICRP.fVblood.systemic.capillaries = [NaN NaN NaN NaN NaN NaN 5.00 NaN]; +ICRP.fVblood.systemic.small_veins = [NaN NaN NaN NaN NaN NaN 41.50 NaN]; +ICRP.fVblood.systemic.large_veins = [NaN NaN NaN NaN NaN NaN 18.00 NaN]; + + +%%% ======================================================================================================== +%%% Data : Density of tissue +%%% +%%% Unit : kg/l +%%% Source: Brown 1997 and ICRP report +%%% +human.OrganDensity = ones(size(initialize)); % all organs except for adi and bon +human.OrganDensity(adi) = 0.92; % Brown, 1997 +human.OrganDensity(bon) = 1.3; % ICRP report + + +%%% ======================================================================================================== +%%% Model: Vascular tissue volumes (fraction vascular blood = fvB +%%% +%%% Unit : fraction of total blood +%%% +for a = human.allAgeClasses + + age = char(human.AgeSexClasses(a)); + human.(age).fvB = initialize; + human.(age).fvB(adi) = ICRP.fVblood.fat(a); + human.(age).fvB(bon) = ICRP.fVblood.skeleton.total(a); + human.(age).fvB(bra) = ICRP.fVblood.brain(a); + human.(age).fvB(gut) = ICRP.fVblood.small_intestine(a)+ICRP.fVblood.large_intestine(a); + human.(age).fvB(hea) = ICRP.fVblood.coronary_tissue(a); + human.(age).fvB(kid) = ICRP.fVblood.kidneys(a); + human.(age).fvB(liv) = ICRP.fVblood.liver(a); + human.(age).fvB(lun) = ICRP.fVblood.bronchial_tissue(a); + human.(age).fvB(mus) = ICRP.fVblood.skeletal_muscle(a); + human.(age).fvB(ski) = ICRP.fVblood.skin(a); + human.(age).fvB(spl) = ICRP.fVblood.spleen(a); + human.(age).fvB(blo) = 100 - sum(human.(age).fvB(tissue.allTisExBlo)); + human.(age).fvB(art) = (0.5*ICRP.fVblood.heart_chambers(a) +... + ICRP.fVblood.pulmonary.veins(a) + ... + 0.5*ICRP.fVblood.pulmonary.capillaries(a) +... + ICRP.fVblood.systemic.aorta_large_arteries(a) + ... + 0.5*ICRP.fVblood.systemic.capillaries(a) + ... + ICRP.fVblood.systemic.small_arteries(a))/100*human.(age).fvB(blo); + human.(age).fvB(ven) = (0.5*ICRP.fVblood.heart_chambers(a) + ... + ICRP.fVblood.pulmonary.arteries(a) +... + 0.5*ICRP.fVblood.pulmonary.capillaries(a) + ... + 0.5*ICRP.fVblood.systemic.capillaries(a) +... + ICRP.fVblood.systemic.small_veins(a) + ... + ICRP.fVblood.systemic.large_veins(a))/100*human.(age).fvB(blo); + + human.(age).fvB = human.(age).fvB/100; % change of units: % -> fraction + human.(age).unit.fvB = 'fraction'; + +end; +%%% +%%% DUE TO LACK OF DATA: +%%% +%%% For children age classes: age35m data were adopted for all fvB entries +%%% except for age15fm, where age35f data were taken +%%% For age35f:age35m data were adopted for fvB values of art and ven +%%% +human.('age35f').fvB([art ven]) = human.('age35m').fvB([art ven])./human.('age35m').fvB(blo)*... + human.('age35f').fvB(blo); +human.('newborn').fvB = human.('age35m').fvB; +human.('age1').fvB = human.('age35m').fvB; +human.('age5').fvB = human.('age35m').fvB; +human.('age10').fvB = human.('age35m').fvB; +human.('age15m').fvB = human.('age35m').fvB; +human.('age15f').fvB = human.('age35f').fvB; + + +%%% ======================================================================================================== +%%% Model: Organ weights (OW) and tissue volumes (V) +%%% +%%% Unit : OW in kg and V in L +%%% NOTE : total blood V(blo) is partitioned into artery blood V(art) and +%%% venous blood V(ven) according to fvB(art)/fvB(blo) and +%%% fvB(ven)/fvB(blo). I.e., all vascular spaces fvB(tis) are partitioned +%%% into an artery and venous part according to fvB(art)/fvB(blo) and +%%% fvB(ven)/fvB(blo) +%%% +for a = human.allAgeClasses + + age = char(human.AgeSexClasses(a)); + human.(age).OW = initialize; human.(age).unit.OW = 'kg'; + human.(age).OW(adi) = ICRP.OW.separable_adipose(a); + human.(age).OW(bon) = ICRP.OW.total_skeleton(a); % includes total bone and marrow + human.(age).OW(bra) = ICRP.OW.brain(a); + human.(age).OW(gut) = ICRP.OW.small_intestine_wall(a) + ICRP.OW.large_intestine_right_colon_wall(a) + ... + ICRP.OW.large_intestine_left_colon_wall(a) + ICRP.OW.large_intestine_rectosigmoid_wall(a); + human.(age).OW(hea) = ICRP.OW.heart_tissue_only(a); + human.(age).OW(kid) = ICRP.OW.kidneys(a); + human.(age).OW(liv) = ICRP.OW.liver(a); + human.(age).OW(lun) = ICRP.OW.lung_tissue_only(a); + human.(age).OW(mus) = ICRP.OW.skeletal_muscle(a); + human.(age).OW(ski) = ICRP.OW.skin(a); + human.(age).OW(spl) = ICRP.OW.spleen(a); + human.(age).OW(blo) = ICRP.OW.total_blood(a); + human.(age).OW(art) = (human.(age).fvB(art)/human.(age).fvB(blo)) *human.(age).OW(blo); + human.(age).OW(ven) = (human.(age).fvB(ven)/human.(age).fvB(blo)) *human.(age).OW(blo); + + human.(age).OW = human.(age).OW/1000; % change of units: g -> kg + + human.(age).OW(rob) = human.(age).BW - sum(human.(age).OW(tissue.allTis)); + human.(age).LBW = human.(age).BW - human.(age).OW(adi); human.(age).unit.LBW = 'kg'; + + human.(age).V = human.(age).OW./human.OrganDensity; human.(age).unit.V = 'L'; % organ weights -> volumes + + human.(age).V(ery) = ICRP.erythrocyte_volume(a)/1000; % change of units: ml -> l + human.(age).V(pla) = ICRP.plasma_volume(a)/1000; % change of units: ml -> l + human.(age).hct = human.(age).V(ery)/human.(age).V(blo); human.(age).unit.hct = 'unitless'; + +end; +%%% +%%% DUE TO LACK OF DATA: +%%% +%%% For all children age classes: age35m data were adopted for +%%% hematocrit value hct +%%% +%%% Age 15, male and age 10, 5, 1, newborn +for a = {'newborn','age1','age5','age10','age15f'} + age = char(a); + hct = human.('age35f').hct; % set identical to hct of female adult + human.(age).hct = hct; % in line with NHANES study (for age 5 and older) + human.(age).V(ery) = hct*human.(age).V(blo); + human.(age).V(pla) = (1-hct)*human.(age).V(blo); +end; +%%% Age 15, male +hct = human.('age35m').hct; % set identical to hct of male adult +human.('age15m').hct = hct; % in line with NHANES study +human.('age15m').V(ery) = hct*human.(age).V(blo); +human.('age15m').V(pla) = (1-hct)*human.(age).V(blo); + + +%%% ======================================================================================================== +%%% Data : Cardiac output +%%% +%%% Unit : L/min +%%% Source: ICRP report (Table 2.39) +%%% nb age1 age5 age10 age15m age15f age35m age35f +ICRP.co = [NaN NaN 3.4 5.0 6.1 6.1 6.5 5.9]; +ICRP.co([newborn age1]) = [44 109]/60; % Alverson et al 1987 (cited in Abraham et al 2005) + + +%%% ======================================================================================================== +%%% Data : blood flow rates +%%% +%%% Unit : percentage (%) of cardiac output +%%% Source: ICRP report (Table 2.40) +%%% nb age1 age5 age10 age15m age15f age35m age35f +ICRP.fQco.fat = [NaN NaN NaN NaN NaN NaN 5.00 8.50]; +ICRP.fQco.brain = [NaN NaN NaN NaN NaN NaN 12.00 12.00]; +ICRP.fQco.stomach_and_oesophagus = [NaN NaN NaN NaN NaN NaN 1.00 1.00]; % not used in PBPK model +ICRP.fQco.small_intestine = [NaN NaN NaN NaN NaN NaN 10.00 11.00]; +ICRP.fQco.large_intestine = [NaN NaN NaN NaN NaN NaN 4.00 5.00]; +ICRP.fQco.coronary_tissue = [NaN NaN NaN NaN NaN NaN 4.00 5.00]; +ICRP.fQco.kidney = [NaN NaN NaN NaN NaN NaN 19.00 17.00]; +ICRP.fQco.liver = [NaN NaN NaN NaN NaN NaN 25.50 27.00]; % total, arterial = [6.5 6.5] +ICRP.fQco.bronchial_tissue = [NaN NaN NaN NaN NaN NaN 2.50 2.50]; % not used in PBPK model +ICRP.fQco.skeletal_muscle = [NaN NaN NaN NaN NaN NaN 17.00 12.00]; +ICRP.fQco.pancreas = [NaN NaN NaN NaN NaN NaN 1.00 1.00]; % not used in PBPK model +ICRP.fQco.skeleton.total = [NaN NaN NaN NaN NaN NaN 5.00 5.00]; +ICRP.fQco.skeleton.red_marrow = [NaN NaN NaN NaN NaN NaN 3.00 3.00]; +ICRP.fQco.skeleton.trabecular_bone = [NaN NaN NaN NaN NaN NaN 0.90 0.90]; +ICRP.fQco.skeleton.cortical_bone = [NaN NaN NaN NaN NaN NaN 0.60 0.60]; +ICRP.fQco.skeleton.others = [NaN NaN NaN NaN NaN NaN 0.50 0.50]; +ICRP.fQco.skin = [NaN NaN NaN NaN NaN NaN 5.00 5.00]; +ICRP.fQco.spleen = [NaN NaN NaN NaN NaN NaN 3.00 3.00]; +ICRP.fQco.thyroid = [NaN NaN NaN NaN NaN NaN 1.50 1.50]; % not used in PBPK model +ICRP.fQco.lymph_nodes = [NaN NaN NaN NaN NaN NaN 1.70 1.70]; % not used in PBPK model +ICRP.fQco.gonads = [NaN NaN NaN NaN NaN NaN 0.05 0.02]; % not used in PBPK model +ICRP.fQco.adrenals = [NaN NaN NaN NaN NaN NaN 0.30 0.30]; % not used in PBPK model +ICRP.fQco.urinary_bladder = [NaN NaN NaN NaN NaN NaN 0.06 0.06]; % not used in PBPK model +ICRP.fQco.all_other_tissues = [NaN NaN NaN NaN NaN NaN 1.39 1.92]; % not used in PBPK model + + +%%% ======================================================================================================== +%%% Model: Tissue blood flow (fraction of cardiac output and absolut values) +%%% +%%% Unit : fraction and L/min +%%% +%%% +for a = human.allAgeClasses + + age = char(human.AgeSexClasses(a)); + human.(age).fQco = initialize; human.(age).unit.fQco = 'unitless'; + human.(age).Q = initialize; human.(age).unit.Q = 'L/min'; + human.(age).fQco(adi) = ICRP.fQco.fat(a); + human.(age).fQco(bon) = ICRP.fQco.skeleton.total(a); + human.(age).fQco(bra) = ICRP.fQco.brain(a); + human.(age).fQco(gut) = ICRP.fQco.small_intestine(a)+ICRP.fQco.large_intestine(a); + human.(age).fQco(hea) = ICRP.fQco.coronary_tissue(a); + human.(age).fQco(kid) = ICRP.fQco.kidney(a); + human.(age).fQco(liv) = ICRP.fQco.liver(a); + human.(age).fQco(mus) = ICRP.fQco.skeletal_muscle(a); + human.(age).fQco(ski) = ICRP.fQco.skin(a); + human.(age).fQco(spl) = ICRP.fQco.spleen(a); + + human.(age).fQco(rob) = 100 - sum(human.(age).fQco(tissue.intoVen)); + + human.(age).fQco(lun) = 100 - human.(age).fQco(rob); % ensures closed blood flow system + human.(age).fQco(art) = human.(age).fQco(lun); + human.(age).fQco(ven) = human.(age).fQco(lun); + + human.(age).fQco = human.(age).fQco/100; % change of units: [%] -> fraction + + human.(age).co = ICRP.co(a); human.(age).unit.co = 'L/min'; + human.(age).Q = human.(age).fQco*human.(age).co; + + +end; +%%% +%%% DUE TO LACK OF DATA: +%%% +%%% For all children age classes: we estimated the fQco values based on the +%%% approach presented in Abraham et al (2005) +%%% + +%%% For age 'newborn','age1', 'age5' , 'age10', 'age15m','age15f' +refAgeClass = {'age35m','age35m','age35m','age35m','age35m','age35m'}; +% Note: Scaling 'age15f' based on 'age35f' would result in liver blood of 'age15f' larger than for 'age35f' +Q_perKg_bra = [50 59 71 68 57 57]*10/1000; % brain: ml/min/100g -> L/min/kg, Chiron et al, 1992 +for a = human.childrenAgeClasses + age = char(human.AgeSexClasses(a)); + refAge = char(refAgeClass(a)); + + Q_perKg = human.(refAge).Q./human.(refAge).V; + Q_hat = Q_perKg.*human.(age).V; + + Q_tis = initialize; + Q_tis(kid) = Q_hat(kid); % kidneys: Grunert et al, 1990, Q per g tissue independent of age + Q_tis(bra) = Q_perKg_bra(a)*human.(age).V(bra); + + tis = [tissue.intoVen rob]; % all tissues with infow into the ven, including rob + tis(find([kid==tis])) = []; tis(find([bra==tis])) = []; % as above, but excluding bra and kid + normalization_Qco = sum(Q_hat(tis))/(human.(age).co - Q_tis(kid) - Q_tis(bra)); + if normalization_Qco >1 + error('Scaling blood flows to children resulted in cardiac output that is larger than the experimental reported one!'); + end; + Q_tis(tis) = Q_hat(tis)/normalization_Qco; + + Q_tis([art lun ven]) = human.(age).co - Q_tis(rob); + % pv = portal vein + Q_perKg_hepart = (human.(refAge).Q(liv)-human.(refAge).Q(gut)-human.(refAge).Q(spl))/human.(refAge).V(liv); + Q_hat_hepart = Q_perKg_hepart*human.(age).V(liv); + normalization_Qpv = (Q_hat_hepart+Q_hat(gut)+Q_hat(spl))/Q_tis(liv); + if normalization_Qpv >1 + error('Scaling blood flows to children resulted in liver blood flow that is larger than the experimental reported one!'); + end; + tis = [gut spl]; + Q_tis(tis) = Q_hat(tis)/normalization_Qpv; + + human.(age).Q = Q_tis; + human.(age).fQco = human.(age).Q/human.(age).co; + human.(age).normalization_Qco = normalization_Qco; + +end; + + + + +%%% ======================================================================================================== +%%% Data : Fraction of interstitial space (fVtis.int) in rat (based on +%%% tissue weight INCLUDING vascular blood!) +%%% +%%% Unit : fraction of tissue space +%%% Source: Kawai etal, J Pharmacokinet Biopharm, Vol 22, 1994 (Table B-I), +%%% based on measurements in nonbled rats +%%% +rat.fVtis.vas = initialize; +rat.fVtis.vas(adi) = 0.010; +rat.fVtis.vas(bon) = 0.041; +rat.fVtis.vas(bra) = 0.037; +rat.fVtis.vas(gut) = 0.024; +rat.fVtis.vas(hea) = 0.262; +rat.fVtis.vas(kid) = 0.105; +rat.fVtis.vas(liv) = 0.115; +rat.fVtis.vas(lun) = 0.262; +rat.fVtis.vas(mus) = 0.026; +rat.fVtis.vas(ski) = 0.019; +rat.fVtis.vas(spl) = 0.282; +rat.fVtis.vas(art) = 1; +rat.fVtis.vas(ven) = 1; + +rat.fVtis.res = initialize; +rat.fVtis.res(adi) = 0.005; +rat.fVtis.res(bon) = 0.019; +rat.fVtis.res(bra) = 0.014; +rat.fVtis.res(gut) = 0.010; +rat.fVtis.res(hea) = 0.061; +rat.fVtis.res(kid) = 0.046; +rat.fVtis.res(liv) = 0.057; +rat.fVtis.res(lun) = 0.175; +rat.fVtis.res(mus) = 0.004; +rat.fVtis.res(ski) = 0.002; +rat.fVtis.res(spl) = 0.321; +rat.fVtis.res(art) = 0; +rat.fVtis.res(ven) = 0; + +%%% ======================================================================================================== +%%% Data : Fraction of interstitial and intra-cellular space (fVtis.int, fVtis.cel) in rat +%%% (based on tissue weight INCLUDING vascular blood!) +%%% +%%% Unit : fraction of tissue space +%%% Source: Kawai etal, J Pharmacokinet Biopharm, Vol 22, 1994 (Table B-I) +%%% +rat.fVtis.int = initialize; +rat.fVtis.int(adi) = 0.135; +rat.fVtis.int(bon) = 0.100; +rat.fVtis.int(bra) = 0.004; +rat.fVtis.int(gut) = 0.094; +rat.fVtis.int(hea) = 0.100; +rat.fVtis.int(kid) = 0.200; +rat.fVtis.int(liv) = 0.163; +rat.fVtis.int(lun) = 0.188; +rat.fVtis.int(mus) = 0.120; +rat.fVtis.int(ski) = 0.302; +rat.fVtis.int(spl) = 0.150; +rat.fVtis.int(art) = 0; +rat.fVtis.int(ven) = 0; +%%% +rat.fVtis.cel = 1-rat.fVtis.vas-rat.fVtis.int; +%%% +%%% Determining fraction of interstitial and intra-cellular space with +%%% respect to tissue weight NOT INCLUDING vascular blood so that +%%% fVtis.int+fVtis.cel = 1 +%%% +m = tissue.allTisExBlo; +rat.fVtis.int(m) = rat.fVtis.int(m)./(1-rat.fVtis.vas(m)); +rat.fVtis.cel(m) = rat.fVtis.cel(m)./(1-rat.fVtis.vas(m)); + +%%% ======================================================================================================== +%%% Data : Total tissue water (Vwt) +%%% +%%% Unit : fraction of tissue volume +%%% Source: Rodgers and Rowland (2006) including correction for residual blood +%%% +rat.fVtis.wex = initialize; %%% tissue water (extra-cellular) +rat.fVtis.wex(adi) = 0.135; +rat.fVtis.wex(bon) = 0.100; +rat.fVtis.wex(bra) = 0.162; +rat.fVtis.wex(gut) = 0.282; +rat.fVtis.wex(hea) = 0.320; +rat.fVtis.wex(kid) = 0.273; +rat.fVtis.wex(liv) = 0.161; +rat.fVtis.wex(lun) = 0.336; +rat.fVtis.wex(mus) = 0.118; +rat.fVtis.wex(ski) = 0.382; +rat.fVtis.wex(spl) = 0.207; +rat.fVtis.wex(pla) = NaN; +rat.fVtis.wex(ery) = 0; +% +rat.fVtis.wic = initialize; %%% tissue water (intra-cellular) +rat.fVtis.wic(adi) = 0.017; +rat.fVtis.wic(bon) = 0.346; +rat.fVtis.wic(bra) = 0.620; +rat.fVtis.wic(gut) = 0.475; +rat.fVtis.wic(hea) = 0.456; +rat.fVtis.wic(kid) = 0.483; +rat.fVtis.wic(liv) = 0.573; +rat.fVtis.wic(lun) = 0.446; +rat.fVtis.wic(mus) = 0.630; +rat.fVtis.wic(ski) = 0.291; +rat.fVtis.wic(spl) = 0.579; +rat.fVtis.wic(pla) = NaN; +rat.fVtis.wic(ery) = NaN; + +%%% Source: Poulin & Theil, J Pharm Sci, Vol 91, 2002 and Poulin & Theil, J Pharm Sci, 2009 for ery value +%%% +human.fVtis.wtot = initialize; +human.fVtis.wtot(lun) = 0.811; +human.fVtis.wtot(kid) = 0.783; +human.fVtis.wtot(spl) = 0.778; +human.fVtis.wtot(bra) = 0.77; +human.fVtis.wtot(mus) = 0.76; +human.fVtis.wtot(hea) = 0.758; +human.fVtis.wtot(liv) = 0.751; +human.fVtis.wtot(gut) = 0.718; +human.fVtis.wtot(ski) = 0.718; +human.fVtis.wtot(bon) = 0.439; +human.fVtis.wtot(adi) = 0.18; +human.fVtis.wtot(pla) = 0.945; +human.fVtis.wtot(ery) = 0.63; +%%% +%%% DUE TO LACK OF DATA: +%%% +%%% Assume that ratio wex-to-wtot is the same as in RAT +%%% +r_wex_to_wtot = rat.fVtis.wex./(rat.fVtis.wex+rat.fVtis.wic); +%%% +human.fVtis.wex = initialize; %%% fraction of extra-celluar water (wex) +human.fVtis.wex = r_wex_to_wtot.*human.fVtis.wtot; +human.fVtis.wex(ery) = 0; +%%% +human.fVtis.wic = initialize; %%% fraction of intracellular water (wic) +human.fVtis.wic = human.fVtis.wtot-human.fVtis.wex; + + + +%%% ======================================================================================================== +%%% Data : Neutral lipids (fVtis.nlt) and neutal phospholipids (fVtis.npt) +%%% +%%% Unit : fraction of tissue volume +%%% Source: Poulin and Theil, J Pharm Sci, Vol. 91, 2002 +%%% Rodgers and Rowland, J Pharm Res, Vol. 24, 2007 (erythrocyte +%%% values only), (Table VII) +%%% +human.fVtis.nlt = initialize; %%% neutral lipids +human.fVtis.nlt(adi) = 0.79; +human.fVtis.nlt(bon) = 0.074; +human.fVtis.nlt(bra) = 0.051; +human.fVtis.nlt(gut) = 0.0487; +human.fVtis.nlt(hea) = 0.0115; +human.fVtis.nlt(kid) = 0.0207; +human.fVtis.nlt(liv) = 0.0348; +human.fVtis.nlt(lun) = 0.003; +human.fVtis.nlt(mus) = 0.0238; +human.fVtis.nlt(ski) = 0.0284; +human.fVtis.nlt(spl) = 0.0201; +human.fVtis.nlt(pla) = 0.0035; +human.fVtis.nlt(ery) = 0.0033; +%%% +human.fVtis.npt = initialize; %%% neutral phospholipids +human.fVtis.npt(bra) = 0.0565; +human.fVtis.npt(liv) = 0.0252; +human.fVtis.npt(spl) = 0.0198; +human.fVtis.npt(hea) = 0.0166; +human.fVtis.npt(gut) = 0.0163; +human.fVtis.npt(kid) = 0.0162; +human.fVtis.npt(ski) = 0.0111; +human.fVtis.npt(lun) = 0.009; +human.fVtis.npt(mus) = 0.0072; +human.fVtis.npt(adi) = 0.002; +human.fVtis.npt(bon) = 0.0011; +human.fVtis.npt(pla) = 0.00225; +human.fVtis.npt(ery) = 0.0012; + + +%%% Source: Graham etal, J Pharm Pharmacol (2011), +%%% based on: Rodgers and Rowland (2006) +%%% +rat.fVtis.nlt = initialize; %%% neutral lipids +rat.fVtis.nlt(adi) = 0.8530; +rat.fVtis.nlt(bon) = 0.0174; +rat.fVtis.nlt(bra) = 0.0391; +rat.fVtis.nlt(gut) = 0.0375; +rat.fVtis.nlt(hea) = 0.0135; +rat.fVtis.nlt(kid) = 0.0121; +rat.fVtis.nlt(liv) = 0.0135; +rat.fVtis.nlt(lun) = 0.0215; +rat.fVtis.nlt(mus) = 0.0100; +rat.fVtis.nlt(ski) = 0.0603; +rat.fVtis.nlt(spl) = 0.0071; +rat.fVtis.nlt(pla) = NaN; +rat.fVtis.nlt(ery) = NaN; +%%% +rat.fVtis.npt = initialize; %%% neutral lipids +rat.fVtis.npt(adi) = 0.0016; +rat.fVtis.npt(bon) = 0.0016; +rat.fVtis.npt(bra) = 0.0015; +rat.fVtis.npt(gut) = 0.0124; +rat.fVtis.npt(hea) = 0.0106; +rat.fVtis.npt(kid) = 0.0240; +rat.fVtis.npt(liv) = 0.0238; +rat.fVtis.npt(lun) = 0.0123; +rat.fVtis.npt(mus) = 0.0072; +rat.fVtis.npt(ski) = 0.0044; +rat.fVtis.npt(spl) = 0.0170; +rat.fVtis.npt(pla) = NaN; +rat.fVtis.npt(ery) = NaN; + + +%%% ======================================================================================================== +%%% Data : Intra-cellular acidic phospholipids (APmt) in rat +%%% +%%% Unit : mg/g tissue +%%% Source: Rodgers, Leahy, and Rowland, J Pharm Sci, Vol 94, 2005 +%%% +rat.fVtis.APmt = initialize; +rat.fVtis.APmt(kid) = 5.03; +rat.fVtis.APmt(liv) = 4.56; +rat.fVtis.APmt(lun) = 3.91; +rat.fVtis.APmt(spl) = 3.18; +rat.fVtis.APmt(gut) = 2.41; +rat.fVtis.APmt(hea) = 2.25; +rat.fVtis.APmt(mus) = 1.53; +rat.fVtis.APmt(ski) = 1.32; +rat.fVtis.APmt(bon) = 0.67; +rat.fVtis.APmt(bra) = 0.4; +rat.fVtis.APmt(adi) = 0.4; +rat.fVtis.APmt(pla) = NaN; +rat.fVtis.APmt(ery) = 0.5; +rat.fVtis.APmt = rat.fVtis.APmt/1000; % scaling from: mg/g --> percentage +%%% +%%% DUE TO LACK OF DATA: +%%% +%%% Rat values adopted for human (see Rodgers, Leahy, and Rowland, J Pharm +%%% Sci 94 (2005), page 925 (right, 2nd paragraph) +%%% +human.fVtis.APmt = rat.fVtis.APmt; + + +%%% ======================================================================================================== +%%% Data : Albumin (Atp) in rat +%%% +%%% Unit : Tissue-to-plasma ratio +%%% Source: Rodgers, and Rowland, J Pharm Sci 95 (2006) +%%% Note : Correction for residual blood applied according to +%%% Rodgers and Rowland, J Pharm Sci, Vol 95, 2006 (Appendix A), +%%% +rat.r.Atp = initialize; +rat.r.Atp(ski) = 0.277; +rat.r.Atp(lun) = 0.212; +rat.r.Atp(gut) = 0.158; +rat.r.Atp(hea) = 0.157; +rat.r.Atp(kid) = 0.130; +rat.r.Atp(bon) = 0.100; +rat.r.Atp(spl) = 0.097; +rat.r.Atp(liv) = 0.086; +rat.r.Atp(mus) = 0.064; +rat.r.Atp(adi) = 0.049; +rat.r.Atp(bra) = 0.048; +%%% +%%% DUE TO LACK OF DATA: +%%% +%%% Rat values adopted for human (see Rodgers, Leahy, and Rowland, J Pharm +%%% Sci 94 (2005), page 925 (right, 2nd paragraph) +%%% +human.r.Atp = rat.r.Atp; + + +%%% ======================================================================================================== +%%% Data : Lipoproteine (Ltp) in rat +%%% +%%% Unit : Tissue-to-plasma ratio +%%% Source: Rodgers, and Rowland, J Pharm Sci 95 (2006) +%%% Note : Correction for residual blood applied according to +%%% Rodgers and Rowland, J Pharm Sci, Vol 95, 2006 (Appendix A), +%%% +rat.r.Ltp = initialize; +rat.r.Ltp(spl) = 0.207; +rat.r.Ltp(lun) = 0.168; +rat.r.Ltp(hea) = 0.160; +rat.r.Ltp(gut) = 0.141; +rat.r.Ltp(liv) = 0.161; +rat.r.Ltp(kid) = 0.137; +rat.r.Ltp(ski) = 0.096; +rat.r.Ltp(adi) = 0.068; +rat.r.Ltp(mus) = 0.059; +rat.r.Ltp(bon) = 0.050; +rat.r.Ltp(bra) = 0.041; +%%% +%%% DUE TO LACK OF DATA: +%%% +%%% Rat values adopted for human (see Rodgers, Leahy, and Rowland, J Pharm +%%% Sci 94 (2005), page 925 (right, 2nd paragraph) +%%% +human.r.Ltp = rat.r.Ltp; + + +%%% ======================================================================================================== +%%% Model : Assignment of fractional volumes and ratio to all age classes +%%% +for a = human.allAgeClasses + + age = char(human.AgeSexClasses(a)); + human.(age).fVtis = human.fVtis; human.(age).unit.fVtis = 'fraction'; + human.(age).r = human.r; human.(age).unit.r = 'unitless'; + +end; + + +%%% ======================================================================================================== +%%% Model : Assignment of species and tissue +%%% +switch speciestype + case 'human' + species = human; + otherwise + error('Unknown species') +end; +%%% +T = tissue; + diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_randomEffects.m b/src/utils/PKHuisinga/GenericPBPKmodel_randomEffects.m new file mode 100644 index 0000000..ce21500 --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_randomEffects.m @@ -0,0 +1,167 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function introduces random perturbations to the hepatic blood +%%% clearance (via CLint) and the tissue-to-blood partition coefficients. +%%% The size of the perturbation is specified by facX, so that the ratio +%%% of the perturbed-to-unperturbed value lies between 1/(1+facX) and +%%% (1+facX). +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function [individual,population] = GenericPBPKmodel_randomEffects(individual,study,randomEffects,population) + + +facX.CLint.hep = randomEffects.facX.CLint.hep; +facX.Ktb = randomEffects.facX.Ktb; + +if facX.CLint.hep>0 + fprintf('\n --> Note: Random effect for CLint.hep considered!\n'); +elseif facX.CLint.hep<0 + error('Random effect CLint.hep is negative!'); +end; +if facX.Ktb>0 + fprintf('\n --> Note: Random effect for Ktb considered! \n'); +elseif facX.Ktb<0 + error('Random effect Ktb is negative!'); +end; + +%%% scale all tissue of individual 'id' with same random factor (=1) or each tissue +%%% of individual 'id' with different randomly chosen factor (=0) +randomEffects.facX.Ktb_allTisIdentical = 0; + + +if facX.CLint.hep>0 || facX.Ktb>0 + for id=1:population.size + + age = individual(id).age; + T = individual(id).tissue; + R_hep = individual(id).E.hep/(1-individual(id).E.hep); + Q_liv = individual(id).Q(T.liv); + K_liv = individual(id).Ktb(T.liv); + CLint.hep = R_hep*Q_liv/K_liv; + + + if rand<0.5 + CLint.hep = CLint.hep*(1+facX.CLint.hep*rand); % maximum of factor 'facX' increase + else + CLint.hep = CLint.hep/(1+facX.CLint.hep*rand); % maximum of factor 'facX' decrease + end; + + for m=T.allTisExBlo + + if randomEffects.facX.Ktb_allTisIdentical + rand1 = rand(1)*ones(1,T.numberOfTis); + rand2 = rand(1)*ones(1,T.numberOfTis); + else + rand1 = rand(1,T.numberOfTis); + rand2 = rand(1,T.numberOfTis); + + end; + + Ktb = individual(id).Ktb(m); + Ktb = [rand1<0.5].*Ktb.*(1+facX.Ktb*rand2) + [rand1>=0.5].*Ktb./(1+facX.Ktb*rand2); + + individual(id).Ktb(m) = Ktb(m); + + end; + + individual(id).CLint.liv = CLint.hep; + individual(id).CLint_perKg.liv = CLint.hep/individual(id).V(T.liv); + individual(id).E.hep = CLint.hep*individual(id).Ktb(T.liv)/(Q_liv+CLint.hep*individual(id).Ktb(T.liv)); + individual(id).CLblood.hep = individual(id).E.hep*Q_liv; + + individual(id).Ktup(T.allTisExBlo) = individual(id).Ktb(T.allTisExBlo)*individual(id).fuP/individual(id).BP; + individual(id).nKtb = individual(id).Ktb; + individual(id).nKtb(T.liv) = (1-individual(id).E.hep)*individual(id).Ktb(T.liv); + individual(id).nKtb(T.kid) = (1-individual(id).E.ren)*individual(id).Ktb(T.kid); + individual(id).nKtb(T.art) = 1; individual(id).nKtb(T.ven) = 1; + + % some PK parameters and characteristics + individual(id).Vss = sum(individual(id).V.*individual(id).nKtb); + individual(id).VssAdi = individual(id).V(T.adi)*individual(id).nKtb(T.adi); + individual(id).VssLBW = individual(id).Vss-individual(id).VssAdi; + individual(id).V1 = sum(individual(id).V(T.V1).*individual(id).nKtb(T.V1)); + individual(id).V2 = sum(individual(id).V(T.V2).*individual(id).nKtb(T.V2)); + + % dosing + for d = {'po','bolus','infusion'} + + route = char(d); + %individual(id).(route) = study.(route); + if ~strcmp(study.(route).per,'fixed') + BSD = study.(route).per; + individual(id).(route).dose = study.(route).dose*individual(id).(BSD); + end; + + end; + + end; + + +end; + +population = populationStatistics(population,individual); + +% ======================================================================== + +function population = populationStatistics(population,individual) + + +for id=1:population.size + population.age(id) = individual(id).age; + population.BH(id) = individual(id).BH; + population.BW(id) = individual(id).BW; + population.LBW(id) = individual(id).LBW; + population.BMI(id) = individual(id).BMI; + population.hct(id) = individual(id).hct; + population.BP(id) = individual(id).BP; + population.SVadi(id) = individual(id).SV.adi; + population.SVski(id) = individual(id).SV.ski; + population.SVtis(id) = individual(id).SV.tis; + population.BSA(id) = individual(id).BSA; + population.co(id) = individual(id).co; + population.E.hep(id) = individual(id).E.hep; + population.Qliv(id) = individual(id).Q(individual(id).tissue.liv); + population.CLblood.hep(id) = individual(id).CLblood.hep; + population.CLblood.ren(id) = individual(id).CLblood.ren; + population.Vss(id) = individual(id).Vss; + population.R(id) = individual(id).VssAdi/individual(id).Vss; + population.VssAdi(id) = individual(id).VssAdi; + population.VssLBW(id) = individual(id).VssLBW; + population.V1(id) = individual(id).V1; + population.V2(id) = individual(id).V2; + population.Q12(id) = individual(id).Q12; + population.po(id) = individual(id).po; + population.bolus(id) = individual(id).bolus; + population.infusion(id)= individual(id).infusion; +end; diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_referenceIndividual.m b/src/utils/PKHuisinga/GenericPBPKmodel_referenceIndividual.m new file mode 100644 index 0000000..58f3646 --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_referenceIndividual.m @@ -0,0 +1,324 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function assigns all parameter values to all reference individuals +%%% 'age5','age10','age15m','age15f','age35m','age35f'. +%%% +%%% Called from the main script GenericPBPKmodel.m +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function [reference] = GenericPBPKmodel_referenceIndividual(species,drug,study) + +fprintf('Parameterizing reference individuals. ') + +%%% determine erythrocyte-to-unbound plasma partition coefficient Keup and +%%% hepatic intrinsic clearance per kg liver volume (CLint_perKgLiver) +%%% +[CLint_perKg,Keup] = GenericPBPKmodel_CLintKeup(species,drug); + +for a = species.AgeSexClasses + + age = char(a); + T = species.(age).tissue; + reference.(age).unit = species.(age).unit; + reference.(age).type = species.(age).type; + reference.(age).tissue = T; + reference.(age).age = a; + reference.(age).sex = species.(age).sex; + reference.(age).BH = species.(age).BH; + reference.(age).BW = species.(age).BW; + reference.(age).BMI = species.(age).BW/species.(age).BH^2; + reference.(age).V = species.(age).V(T.allTis); + reference.(age).hct = species.(age).hct; + reference.(age).fVtis = species.(age).fVtis; + reference.(age).r = species.(age).r; + reference.(age).SV.adi = 1; + reference.(age).SV.ski = 1; + reference.(age).SV.tis = 1; + reference.(age).BSA = species.(age).BSA; + reference.(age).LBW = species.(age).LBW; + reference.(age).co = species.(age).co; + reference.(age).Q = species.(age).Q(T.allTis); + reference.(age).SQ = 1; + reference.(age).CLint_perKg.liv = CLint_perKg.liv; reference.(age).unit.CLint_perKg = 'L/min/kg OW'; + reference.(age).CLint.liv = CLint_perKg.liv*reference.(age).V(T.liv); reference.(age).unit.CLint = 'L/min'; + reference.(age).CLint_perKg.kid = CLint_perKg.kid; + reference.(age).CLint.kid = CLint_perKg.kid*reference.(age).V(T.kid); + reference.(age).Keup = Keup; reference.(age).unit.Keup = 'unitless'; + reference.(age).fuP = drug.(species.type).fuP; reference.(age).unit.fuP = drug.unit.fuP; + reference.(age).BP = reference.(age).hct*Keup*reference.(age).fuP + (1-reference.(age).hct); reference.(age).unit.BP = drug.unit.BP; + + [Ktup,Ktb] = GenericPBPKmodel_Ktb(reference.(age),drug); + reference.(age).Ktup = Ktup(T.allTis); reference.(age).unit.Ktup = 'unitless'; + reference.(age).Ktb = Ktb(T.allTis); reference.(age).unit.Ktb = 'unitless'; + + reference.(age).lambda_po = drug.(species.type).lambda_po; + [CLblood,E,nKtb,Vss,t_half]= GenericPBPKmodel_CLblood_etc(reference.(age),drug.(species.type).E); + + reference.(age).CLblood = CLblood; reference.(age).unit.CLblood = 'L/min'; + reference.(age).E = E; reference.(age).unit.E = 'fraction'; + reference.(age).nKtb = nKtb(T.allTis); reference.(age).unit.nKtb = 'unitless'; + reference.(age).t_half = t_half(T.allTis); reference.(age).unit.t_half = 'min'; + reference.(age).Vss = Vss; reference.(age).unit.Vss = 'L'; + reference.(age).VssAdi = reference.(age).nKtb(T.adi)*reference.(age).V(T.adi); + reference.(age).VssLBW = Vss-reference.(age).VssAdi; + reference.(age).Vres = species.(age).BW - sum(species.(age).V([T.adi T.bra T.ski])); + reference.(age).V1 = sum(reference.(age).V(T.V1).*reference.(age).nKtb((T.V1))); + reference.(age).V2 = sum(reference.(age).V(T.V2).*reference.(age).nKtb((T.V2))); + reference.(age).Q12 = sum(reference.(age).Q(T.V2)); + + reference.(age).id = a; + reference.(age).color = 'b'; + + for d = {'po','bolus','infusion'} + + route = char(d); + reference.(age).(route) = study.(route); + if ~strcmp(study.(route).per,'fixed') + BSD = study.(route).per; + reference.(age).(route).dose = study.(route).dose*reference.(age).(BSD); + end; + + end; + reference.(age).lambda_po = drug.(reference.(age).type).lambda_po; + +end; + + + +% ======================================================================== +function out = BSA(BW,BH) + +% Determine body surface area (BSA) in [m^2] +% Source: Mosteller, New Engl J Med, Vol 317, 1987 + +out = sqrt(BH*BW/36); + + +% ======================================================================== +function [CLint_perKg,Keup] = GenericPBPKmodel_CLintKeup(species,drug) + +%%% Experimental values of hepatic and renal blood clearance are assumed +%%% to correspond to the age = drug.(species.type).expData.age. +%%% Here, these values are converted to age-independent data +%%% CLint_kgLiv and CLren_kgKid +%%% + +% age to which experimental values of hct and CLblood correspond +age = drug.(species.type).expData.age; + +% determine erythrocyte-to-unbound plasma partition coefficient Keup +hct = species.(age).hct; +BP = drug.(species.type).BP; +fuP = drug.(species.type).fuP; + +Keup = (BP-(1-hct))/(hct*fuP); + +T = species.(age).tissue; +species.(age).fuP = drug.(species.type).fuP; +species.(age).BP = drug.(species.type).BP; +species.(age).Keup = Keup; +[Ktup,Ktb] = GenericPBPKmodel_Ktb(species.(age),drug); + +% determine hepatic intrinsic clearance per kg liver volume +% based on well-stirred liver model +CLblood.hep = drug.(species.type).CLblood_kgBW.hep * species.(age).BW; +Q_liv = species.(age).Q(T.liv); +K_liv = Ktb(T.liv); + +if CLblood.hep>Q_liv error(' CLblood.hep larger than Q_liv!'); end; + +CLint.liv = Q_liv*CLblood.hep/(K_liv*(Q_liv-CLblood.hep)); +CLint_perKg.liv = CLint.liv/species.(age).V(T.liv); + +% determine renal clearance per kg kidney volume (based on well-stirred +% tissue model and QSSA) +if drug.(species.type).CLblood_kgBW.ren > 0 + error('Renal clearance not implemented !'); +end; +% Below is just a DUMMY scaling that is NOT used and needs to be +% verified before eventually using it. Most likely, it is not good, +% so don't use it! +CLblood.ren = drug.(species.type).CLblood_kgBW.ren * species.(age).BW; +Q_kid = species.(age).Q(T.kid); +K_kid = Ktb(T.kid); + +if CLblood.ren>Q_kid error(' CLblood.kid larger than Q_kid!'); end; + +CLint.kid = Q_kid*CLblood.ren/(K_kid*(Q_kid-CLblood.ren)); +CLint_perKg.kid = CLint.kid/species.(age).V(T.kid); + + +% ======================================================================== +function [Ktup,Ktb] = GenericPBPKmodel_Ktb(individual,drug) + +%%% Uses method published by Rodgers and Rowland to determine +%%% tissue-to-unbound plasma partition coefficients. + + +fVtis = individual.fVtis; % volume fractions of interstitial and cellular space +Vr = individual.r; % tissue-to-plasma ratios of binding proteins +hct = individual.hct; % hematocrite +fuP = individual.fuP; % fraction unbound in plasma +BP = individual.BP; % blood-to-plasma ratio + +T = individual.tissue; + +if (isnan(drug.logPow)) + error('Octanol-to-water partition coefficient undefined!'); +end +% estimate logPvow based on logPow according to Poulin & Theil +if (isnan(drug.logPvow)) + drug.logPvow =1.115*drug.logPow-1.35; +end + +KA_TPR = zeros(size(T.initialize)); + +% ionization effects +pH_p = 7.4; pH_iw = 7.0; pH_e = 7.22; +fnC = GenericPBPKmodel_ionization(drug,pH_iw); +fnE = GenericPBPKmodel_ionization(drug,pH_e); +fnP = GenericPBPKmodel_ionization(drug,pH_p); + +% erythrocytes-to-unbound plasma partition coefficient +Keup = (BP-(1-hct))/(hct*fuP); + +% neutral lipids-to-water partition coefficient +Pow = 10^drug.logPow; +Pvow = 10^drug.logPvow; +Knlw = Pow*ones(size(T.initialize)); +Knlw(T.adi) = Pvow; + +% neutral phospholipids-to-water partition coefficient +Knpw = 0.3*Knlw+0.7; + +switch drug.mode + case {'B','AB'} % binding to acidic phospholipids (APmt) + KA_TPR = zeros(size(T.initialize)); + KA_AP = (Keup - fnP/fnE*fVtis.wic(T.ery) - fnP*Pow*fVtis.nlt(T.ery)... + - fnP*(0.3*Pow+0.7)*fVtis.npt(T.ery)) * fnE/((1-fnE)*fnP)/fVtis.APmt(T.ery); + case {'N'} % binding to lipoproteins (Ltp) + KA_TPR = (1/fuP -1 -fnP*Pow*fVtis.nlt(T.pla) ... + -fnP*(0.3*Pow+0.7)*fVtis.npt(T.pla))*Vr.Ltp; + KA_AP = 0; + case {'A','wB'} % binding to albumin (Atp) + KA_TPR = (1/fuP -1 -fnP*Pow*fVtis.nlt(T.pla) ... + -fnP*(0.3*Pow+0.7)*fVtis.npt(T.pla))*Vr.Atp; + KA_AP = 0; + otherwise + error(' Unknown type of compound to predict Ktup') +end + +Ktup = T.initialize; +m = T.allTisExBlo; +Ktup(m) = fVtis.wex(m) +fnP/fnC*fVtis.wic(m) + fnP*Knlw(m).*fVtis.nlt(m) ... + + fnP*Knpw(m).*fVtis.npt(m) + fnP*(1-fnC)/fnC*KA_AP*fVtis.APmt(m) + KA_TPR(m); + +Ktb = Ktup*fuP/BP; + + + +% ======================================================================== +function [CLblood,E,nKtb,Vss,t_half] = GenericPBPKmodel_CLblood_etc(individual,E) + +%%% Determines blood clearance values, extraction ratios, normalized partition +%%% coefficients, volume of distribution and half life. +%%% + +hct = individual.hct; % hematocrite +fuP = individual.fuP; % fraction unbound in plasma +BP = individual.BP; % blood-to-plasma ratio +Ktb = individual.Ktb; % tissue-to-blood partition coefficients +T = individual.tissue; + +% hepatic clearance +Q_liv = individual.Q(T.liv); +K_liv = individual.Ktb(T.liv); +CLint.liv = individual.CLint_perKg.liv * individual.V(T.liv); +E.hep = CLint.liv*K_liv/(Q_liv + CLint.liv*K_liv); +CLblood.hep = Q_liv * E.hep; + +% renal clearance +Q_kid = individual.Q(T.kid); +K_kid = individual.Ktb(T.kid); +CLint.kid = individual.CLint_perKg.kid * individual.V(T.kid); +E.ren = CLint.kid*K_kid/(Q_kid + CLint.kid*K_kid); +CLblood.ren = Q_kid * E.ren; + + +% extraction ratio normalized tissue-to-blood partition coefficients (nKtb) +nKtb = Ktb; +nKtb(T.liv) = (1-E.hep)*Ktb(T.liv); +nKtb(T.kid) = (1-E.ren)*Ktb(T.kid); +nKtb(T.art) = 1; +nKtb(T.ven) = 1; + +% predict volume of distribution at steady state +m = T.allTis; +Vss = sum(nKtb(m).*individual.V(m)); + +% predicted tissue distribution half-life +t_half = T.initialize; +m = T.allTis; +t_half(m) = log(2)*individual.V(m).*nKtb(m)./individual.Q(m); + + + +% ======================================================================== + +function fn = GenericPBPKmodel_ionization(drug,pH) + +% pka = pka values (vector) in decreasing order +% pH = pH value of the environment +% mode = 'A' for acid; 'B' for base; 'N' for neutral; +% 'AA' for diprotonic acid; +% 'BB' for diprotonic base; 'AB' for zwitterions + +switch drug.mode + case 'A' % acid + fn = 1/(1+10^(pH-drug.pKa(1))); + case {'B','wB'} % base + fn = 1/(1+10^-(pH-drug.pKa(1))); + case 'N' % neutral + fn = 1; + case 'AA'% di-acid + fn = 1/(1+10^(pH-drug.pKa(1))+10^(2*pH-drug.pKa(1)-drug.pKa(2))); + case 'BB'% di-base + fn = 1/(1+10^-(pH-drug.pKa(2))+ 10^-(2*pH-drug.pKa(1)-drug.pKa(2))); + case 'AB'% zwitter + fn = 1/(1+10^(pH-drug.pKa(2))+ 10^-(pH-drug.pKa(1))); +end + + + + diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_simulatePBPKmodel.m b/src/utils/PKHuisinga/GenericPBPKmodel_simulatePBPKmodel.m new file mode 100644 index 0000000..559eb93 --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_simulatePBPKmodel.m @@ -0,0 +1,289 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function simulates the generic 13-compartment PBPK model +%%% for some individual, a given drug and study design. +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function [sim,OK] = GenericPBPKmodel_simulatePBPKmodel(individual,drug,study) + + +if individual.id==1 + fprintf('\n Simulating PBPK model ...\n'); + fprintf('\n Individual(id): age; sex; height; weight; line type in output \n'); +end; + fprintf('\n Individual(%2.0f): %2.0f; %s; %1.2f m; %2.1f kg; ''%s''; ',... + individual.id,ageInYears(individual.age),individual.sex(1),individual.BH,individual.BW,char(individual.color)); + +%%% check for consistency of all PBPK parameters +OK = GenericPBPKmodel_concistencycheck(individual,drug,study); +if ~OK return; end; + +%%% Set initial values for ODE +T = individual.tissue; +C_tis = zeros(1,T.numberOfTis); A_GItract = 0; A_metab = 0; +sim.t = []; sim.C = []; sim.A_GItract = []; sim.A_metab = []; +Tend = max(study.observationTime); + +for d=1:study.numberOfDosing + + C_tis(end,T.ven) = C_tis(end,T.ven) + individual.bolus.dose/individual.V(T.ven); + A_GItract(end) = A_GItract(end) + (1-individual.E.gut)*(1-individual.E.feces)*individual.po.dose; + + X0 = [C_tis(end,:), A_GItract(end), A_metab(end)]; + + %%% Simulate system of ODEs + [t,X] = ode15s(@GenericPBPKmodel_RHS,study.observationTime,X0',[],individual,study); + + t = t + (d-1)*Tend; % transformation from relative time to absolute time + if d~=study.numberOfDosing + t(end) = t(end) - 1e-10; % modify time to account for trough value measurement + end; + C_tis = X(:,1:T.numberOfTis); + A_GItract = X(:,T.numberOfTis+1); + A_metab = X(:,T.numberOfTis+2); + + sim.t = [sim.t;t]; + sim.C = [sim.C;C_tis]; + sim.A_GItract = [sim.A_GItract;A_GItract]; + sim.A_metab = [sim.A_metab;A_metab]; + +end; + +sim.A = sim.C*diag(individual.V); % amounts +sim.unit.t = 'min'; sim.unit.C = 'mg/L'; sim.unit.A = 'mg'; + + +end + +% ------------------------------------------------------------------------ + + +function out = GenericPBPKmodel_RHS(t,C,individual,study) + +T = individual.tissue; % individual related indexing + +% tissue volumes, blood flows, extraction ratios, clearance etc. +V = individual.V; +Q = individual.Q; +Ktb = individual.Ktb; +CLint = individual.CLint; + +% GItract +A_GItract = C(T.numberOfTis+1); +lambda_po = individual.lambda_po; + +% venous concentration leaving tissue +C_vbl = zeros(T.numberOfTis,1); +C_vbl(T.allTisExBlo) = C(T.allTisExBlo)./Ktb(T.allTisExBlo)'; + +% dosing +r_iv_infusion = 0; +if (study.infusion.tend > 0) && (t<=study.infusion.tend) + r_iv_infusion = individual.infusion.dose/study.infusion.tend; +end; + +% concentration entering organs/tissues +Cin_ven = (Q(T.intoVen)*C_vbl(T.intoVen))/Q(T.ven); + +Qliv_art = Q(T.liv)-Q(T.spl)-Q(T.gut); +Cin_liv = (Qliv_art*C(T.art)+Q(T.gut)*C_vbl(T.gut)+Q(T.spl)*C_vbl(T.spl))/Q(T.liv); + +% rates of change for the tissue/organ concentrations +VdC = zeros(T.numberOfTis,1); + +% lung +VdC(T.lun) = Q(T.lun)*(C(T.ven)-C_vbl(T.lun)); +% artery +VdC(T.art) = Q(T.art)*(C_vbl(T.lun)-C(T.art)); +% brain +VdC(T.bra) = Q(T.bra)*(C(T.art)-C_vbl(T.bra)); +% adipose +VdC(T.adi) = Q(T.adi)*(C(T.art)-C_vbl(T.adi)); +% heart +VdC(T.hea) = Q(T.hea)*(C(T.art)-C_vbl(T.hea)); +% kidneys +VdC(T.kid) = Q(T.kid)*(C(T.art)-C_vbl(T.kid)); +% muscle +VdC(T.mus) = Q(T.mus)*(C(T.art)-C_vbl(T.mus)); +% bone +VdC(T.bon) = Q(T.bon)*(C(T.art)-C_vbl(T.bon)); +% skin +VdC(T.ski) = Q(T.ski)*(C(T.art)-C_vbl(T.ski)); +% gut +VdC(T.gut) = Q(T.gut)*(C(T.art)-C_vbl(T.gut)); +% spleen +VdC(T.spl) = Q(T.spl)*(C(T.art)-C_vbl(T.spl)); +% liver +VdC(T.liv) = Q(T.liv)*(Cin_liv-C_vbl(T.liv)) - CLint.liv*C(T.liv) + lambda_po*A_GItract; +% vein +VdC(T.ven) = Q(T.ven)*(Cin_ven-C(T.ven)) + r_iv_infusion; + +% converting amounts to concentrations +dC = VdC./V'; + +% drug amount in GI tract for absorption +dA_GItract = -lambda_po*A_GItract; + +% metabolized and excreted compound (mass) +dA_metab = CLint.liv*C(T.liv); + +% output vector +out = [dC;dA_GItract;dA_metab]; + +end + + +% ------------------------------------------------------------------------ + +function OK = GenericPBPKmodel_concistencycheck(individual,drug,study) + +T = individual.tissue; OK = 1; + +if sum(individual.V<0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> There are negative values in individual.V <---- \n\n'); +end; + +if sum(individual.Q<0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> There are negative values in individual.Q <---- \n\n'); +end; + +if sum(isnan(individual.Ktup(T.allTisExBlo))) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> There are undefined values in individual.Ktup(T.allTisExBlo) <---- \n\n'); +end; + +if individual.BP<(1-individual.hct) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Unphysiological condition: BP < (1-hct) <---- \n\n'); +end; + +if individual.Keup<0 + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Erythrocyte-to-unbound plasma partition coefficient <=0 <---- \n\n'); +end; + +if sum(individual.Ktup(T.allTisExBlo)<0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> There are negative values in drug.Ktup.pred <---- \n\n'); +end; + +if sum(isnan(individual.Ktb(T.allTisExBlo)))>0 + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> There are undefined values in drug.Ktb.pred <---- \n\n'); +end; + +if sum(individual.Ktb(T.allTisExBlo)<0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> There are negative values in drug.Ktb.pred <---- \n\n'); +end; + +if isnan(individual.fuP)>0 || (individual.fuP<0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> drug.fuP either NaN or negative <---- \n\n'); +end; + +if isnan(individual.BP)>0 || (individual.BP<0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> drug.BP either NaN or negative <---- \n\n'); +end; + +if isnan(drug.logPow)>0 + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> drug.logPow value is NaN <---- \n\n'); +end; + +if ~strcmp(drug.mode,'N') && isnan(drug.pKa) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> drug.pKa is NaN <---- \n\n'); +end; + +if (study.po.dose>0) && ( isnan(individual.lambda_po) || (individual.lambda_po<=0) ) + OK = warning('MATLAB:genericPBPKmodel','\n\n ----> drug.po.lambda either NaN or <=0 ! <---- \n\n'); +end; + +if sum(isnan(individual.V(T.allTis)<0))>0 | individual.V(T.allTis)<0 + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> There are NaN or negative values in individual.V <---- \n\n'); +end; + +if individual.CLblood.hep>individual.Q(T.liv) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Unphysiological condition: CL_blood > Q_liv <---- \n\n'); +end; + +if abs(sum(individual.Q(T.intoVen))-individual.Q(T.ven))>1e-10 + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Sum of blood flows into ven differnt from Qven <---- \n\n'); +end; + +if (individual.bolus.dose==0) && (individual.infusion.dose==0) && (individual.po.dose==0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> No drug administerd (iv and po dose=0)! <---- \n\n'); +end; + +if (individual.bolus.dose<0) || (individual.infusion.dose<0) || (individual.po.dose<0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Negative drug dose administerd ! <---- \n\n'); +end; + +if individual.infusion.tend > max(study.observationTime) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Infusion time larger than observation time ! <---- \n\n'); +end; + +if (individual.E.feces<0) || (individual.E.feces>1) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Violation of 0<=E.feces<=1 ! <---- \n\n'); +end; + +if (individual.E.gut<0) || (individual.E.gut>1) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Violation of 0<=E.gut<=1 ! <---- \n\n'); +end; + +if (individual.E.ren~=0) + OK = warning('MATLAB:genericPBPKmodel','\n\n ---> Renal clearnance not implemented ! <---- \n\n'); +end; + + +end + + +% ------------------------------------------------------------------------ + +function y = ageInYears(ageClass) + +a = char(ageClass); + +switch a + case 'age5'; + y = 5; + case 'age10'; + y = 10; + case 'age15m'; + y = 15; + case 'age15f'; + y = 15; + case 'age35m'; + y = 35; + case 'age35f'; + y = 35; + otherwise + error('Unknown ageclass in GenericPBPKmodel_simulatePBPKmodel!'); +end; + +end + diff --git a/src/utils/PKHuisinga/GenericPBPKmodel_virtualPopulation.m b/src/utils/PKHuisinga/GenericPBPKmodel_virtualPopulation.m new file mode 100644 index 0000000..c8c4995 --- /dev/null +++ b/src/utils/PKHuisinga/GenericPBPKmodel_virtualPopulation.m @@ -0,0 +1,290 @@ +%%% +%%% Version: August 31st, 2012 +%%% +%%% Check at URL: http://www.pharmacometrics.de +%%% for potential updates and comments +%%% +%%% This function generates a virtual population of individuals based on +%%% antropometric data and the LBW-based scaling approach. +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% For details on the INPUT and OUTPUT, please see the +%%% GenericPBPKmodel_README.txt file. +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +function [individual,population] = GenericPBPKmodel_virtualPopulation(reference,study,population) + +ref = 0; % do not change + +%%% use of 'ref' below: +%%% If chosen either for BH or for BW, then the corresponding +%%% reference values will be chosen. If chosen for BH and BW, then the +%%% complete parameterization of the reference individual will be chosen. +%%% The difference is mainly due to different LBW and skin. +%%% + +switch population.how2generate + case 'male' + population.age = {'age35m'}; + population.BH = [ref]; + population.BW = [ref]; + population.color = {'r-'}; + case 'adults' + population.age = {'age35m','age35f'}; + population.BH = [ref ref]; + population.BW = [ref ref]; + population.color = {'r-' 'b--'}; + case 'all' + population.age = {'age5','age10','age15m','age15f','age35m','age35f'}; + population.BH = [ref ref ref ref ref ref]; + population.BW = [ref ref ref ref ref ref]; + population.color = {'k--' 'k-' 'b--' 'r--' 'b-' 'r-'}; + case 'Grandmaison' + population.size = 355+329; + % male + idVec = 1:355; + for id=idVec + population.age(id) = {'age35m'}; + population.color(id) = {'k'}; + end; + muBH = 1.72; + sdBH = 0.075; + population.BH(idVec) = muBH + sdBH * randn(1,355); + muBMI = 22.8; + sdBMI = 3.3; + % transforming mu and sd to the scale of the underlying normal distribution + log_muBMI = log(muBMI^2/sqrt(muBMI^2+sdBMI^2)); + log_sdBMI = sqrt(log(sdBMI^2/muBMI^2+1)); + % BMI lognormal with mean=muBMI and sd=s + population.BMI(idVec) = exp(log_muBMI + log_sdBMI * randn(1,355)); + population.BW(idVec) = population.BMI(idVec) .* (population.BH(idVec).^2); + + % female + idVec = [1:329]+355; + for id=idVec + population.age(id) = {'age35f'}; + population.color(id) = {'b'}; + end; + muBH = 1.61; + sdBH = 0.075; + population.BH(idVec) = muBH + sdBH * randn(1,329); + muBMI = 22.5; + sdBMI = 4.5; + % transforming mu and sd to the scale of the underlying normal distribution + log_muBMI = log(muBMI^2/sqrt(muBMI^2+sdBMI^2)); + log_sdBMI = sqrt(log(sdBMI^2/muBMI^2+1)); + % BMI lognormal with mean=muBMI and sd=s + population.BMI(idVec) = exp(log_muBMI + log_sdBMI * randn(1,329)); + population.BW(idVec) = population.BMI(idVec) .* (population.BH(idVec).^2); + population.agesex = []; + case 'random' + for id=1:population.size + population.age(id) = {population.agesex}; + population.color(id) = {'k'}; + end; + muBH = reference.(population.agesex).BH; + sdBH = 0.075; + population.BH = muBH + sdBH * randn(1,population.size); + muBMI = reference.(population.agesex).BMI; + sdBMI = 3.3; + % transforming mu and sd to the scale of the underlying normal distribution + log_muBMI = log(muBMI^2/sqrt(muBMI^2+sdBMI^2)); + log_sdBMI = sqrt(log(sdBMI^2/muBMI^2+1)); + % BMI lognormal with mean=muBMI and sd=s + population.BMI = exp(log_muBMI + log_sdBMI * randn(1,population.size)); + population.BW = population.BMI .* (population.BH.^2); + case 'obese' + age = population.agesex; + population.age = {age,age,age,age}; + BH = reference.(age).BH; + population.BH = [BH BH BH BH]; + BMI = reference.(age).BMI; + population.BW = [0.7*BMI BMI 1.3*BMI 2*BMI].*BH^2; + population.color = {'m' 'r-' 'b--' 'k--'}; + case 'identical' + for id=1:population.size + population.age(id) = {population.agesex}; + population.BH(id) = reference.(population.agesex).BH; + population.BW(id) = reference.(population.agesex).BW; + population.color(id) = {'k'}; + end; + case 'children_small_adults' + population.age = {'age5','age35m','age10','age35m','age15m','age35m'}; + population.BH = [ref 1.09 ref 1.38 ref 1.67]; + population.BW = [ref 19 ref 32 ref 56]; + population.color = {'b' 'b--' 'k' 'k--' 'r' 'r--'}; + otherwise + error(' Unknow approach of generating a virtual population!'); +end; +population.size = length(population.BW); + +for id=1:population.size + + age = char(population.age(id)); + + BH = population.BH(id); BW = population.BW(id); + + if BH == ref && BW == ref + individual(id) = reference.(age); + individual(id).id = id; + individual(id).color = population.color(id); + else + T = reference.(age).tissue; adi = T.adi; bra = T.bra; ski = T.ski; liv = T.liv; kid = T.kid; + if BH == ref BH = reference.(age).BH; end; % set reference BH, if 'ref' is chosen + if BW == ref BW = reference.(age).BW; end; % set reference BW, if 'ref' is chosen + + % set tissue indexing and printing color + individual(id).id = id; + individual(id).type = reference.(age).type; + individual(id).tissue = reference.(age).tissue; + individual(id).color = population.color(id); + + % age, sex, body height, weight and BMI + individual(id).age = reference.(age).age; + individual(id).sex = reference.(age).sex; + individual(id).BH = BH; + individual(id).BW = BW; + individual(id).BMI = BW/BH^2; + + % hematocrit values and volume fractions etc + individual(id).hct = reference.(age).hct; + individual(id).fVtis = reference.(age).fVtis; + individual(id).r = reference.(age).r; + + % scaling of adipose tissue + individual(id).LBW = FFM(BW,BH,individual(id).sex); % Note: LBW approximated by FFM! + individual(id).SV.adi =(BW - individual(id).LBW) / (reference.(age).BW - reference.(age).LBW); + individual(id).V(adi) = individual(id).SV.adi * reference.(age).V(adi); + + % scaling of skin + individual(id).BSA = BSA(BW,BH); + individual(id).SV.ski = individual(id).BSA / reference.(age).BSA; + individual(id).V(ski) = individual(id).SV.ski * reference.(age).V(ski); + + % scaling of brain + individual(id).SV.bra = 1; + individual(id).V(bra) = individual(id).SV.bra * reference.(age).V(bra); + + % scaling of all remaining tissues + m = T.allTisSVtisScaled; + individual(id).Vres = individual(id).BW - sum(individual(id).V([adi bra ski])); + individual(id).SV.tis = individual(id).Vres/reference.(age).Vres; + individual(id).V(m) = individual(id).SV.tis * reference.(age).V(m); + + % scaling of blood flows + individual(id).SQ = individual(id).SV.tis; + individual(id).co = individual(id).SQ * reference.(age).co; + individual(id).Q = individual(id).SQ * reference.(age).Q; + + % parameters regarding distribution and protein binding + individual(id).Ktup = reference.(age).Ktup; + individual(id).Ktb = reference.(age).Ktb; + individual(id).nKtb = reference.(age).nKtb; + individual(id).Keup = reference.(age).Keup; + individual(id).fuP = reference.(age).fuP; + individual(id).BP = reference.(age).BP; + + % scaling the clearances. The below procedure results in constant E.hep + % and CLblood.hep scaled with liver blood flow Q(liv) + individual(id).E = reference.(age).E; % for E.feces and E.gut + individual(id).CLint_perKg = reference.(age).CLint_perKg; + individual(id).CLint.liv = individual(id).CLint_perKg.liv*individual(id).V(T.liv); + individual(id).E.hep = individual(id).CLint.liv*individual(id).Ktb(T.liv)/... + (individual(id).Q(T.liv) + individual(id).CLint.liv*individual(id).Ktb(liv)); + individual(id).CLblood.hep = individual(id).Q(T.liv) * individual(id).E.hep; + + individual(id).CLint.kid = individual(id).CLint_perKg.kid*individual(id).V(T.kid); + individual(id).E.ren = individual(id).CLint.kid*individual(id).Ktb(T.kid)/... + (individual(id).Q(T.kid) + individual(id).CLint.kid*individual(id).Ktb(kid)); + + individual(id).CLblood.ren = individual(id).Q(T.kid) * individual(id).E.ren; + + % some PK parameters and characteristics + individual(id).Vss = sum(individual(id).V.*individual(id).nKtb); + individual(id).VssAdi = individual(id).V(T.adi)*individual(id).nKtb(T.adi); + individual(id).VssLBW = individual(id).Vss-individual(id).VssAdi; + individual(id).V1 = sum(individual(id).V(T.V1).*individual(id).nKtb(T.V1)); + individual(id).V2 = sum(individual(id).V(T.V2).*individual(id).nKtb(T.V2)); + individual(id).Q12 = sum(individual(id).Q(T.V2)); + + + % dosing + individual(id).lambda_po = reference.(age).lambda_po; + for d = {'po','bolus','infusion'} + + route = char(d); + individual(id).(route) = reference.(age).(route); + if ~strcmp(study.(route).per,'fixed') + BSD = study.(route).per; + individual(id).(route).dose = study.(route).dose*individual(id).(BSD); + end; + + end; + + + end; % if BH == ref && BW == ref + +end; % for id=1:population.size + + +%%% =================================================================== + +function out = LBW(BW,BH,sex) + +% determine lean body weight (LBW) according to +% Green B, Duffull SB. Clin Pharmacol Ther Vol 72, 2002 + +BMI = BW/BH^2; +if strcmp(sex,'male') + out = 1.10 * BW - 0.012 * BMI * BW; +else + out = 1.07 * BW - 0.0148 * BMI * BW; +end; + + + +function out = FFM(BW,BH,sex) + +% determine fat free mass (FFM) according to +% Green B, Duffull SB. Clin Pharmacol Ther Vol 72, 2002 + +BMI = BW/BH^2; +if strcmp(sex,'male') + out = 9.27e3*BW/(6.68e3+216*BMI); +else + out = 9.27e3*BW/(8.78e3+244*BMI); +end; + + + +function out = BSA(BW,BH) + +% Determine body surface area (BSA) in [m^2] +% Source: Mosteller, New Engl J Med, Vol 317, 1987 + +out = sqrt(BH*BW/36); + + diff --git a/src/utils/PKHuisinga/README.TXT b/src/utils/PKHuisinga/README.TXT new file mode 100644 index 0000000..2dfdfab --- /dev/null +++ b/src/utils/PKHuisinga/README.TXT @@ -0,0 +1,208 @@ +%%% README.TXT +%%% +%%% Version: August 31st, 2012 +%%% +%%% This text specifies all inputs and outputs of the main script +%%% GenericPBPKmodel.m +%%% +%%% +%%% Online Supplement to: +%%% +%%% W. Huisinga, A. Solms, L. Fronton, S. Pilari, +%%% Modeling Interindividual Variability in Physiologically Based +%%% Pharmacokinetics and Its Link to Mechanistic Covariate Modeling, +%%% Pharmacometrics & Systems Pharmacology (2012) 1, e5; +%%% doi:10.1038/psp.2012.3 +%%% +%%% +%%% Copyright (C) 2012, Universitaet Potsdam, Germany +%%% Contact: W. Huisinga, huisinga@uni-potsdam.de +%%% +%%% The program is distributed under the terms of the +%%% Creative Commons License (CC BY-NC-SA 3.0): +%%% Attribution-NonCommercial-ShareAlike 3.0 Unported +%%% +%%% For a SHORT HUMAN-READABLE SUMMARY OF THE LEGAL CODE, see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/ +%%% +%%% For the Legal Code (the full license) see URL +%%% http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode +%%% +%%% + +ADVISE: First, have a look at GenericPBPKmodel.m and its structure in order to +get a rough idea of the overall structure/philosophy + + + +%%% ================================================================= +%%% VARIABLE: species +%%% ================================================================= + +species is a STRUCTURE with the FIELD specifying the age and sex, e.g., +'age5' (unisex), 'age10' (unisex), 'age15m', 'age15f, 'age35m', 'age35f' + +Each age/sex FIELD contains various SUBFIELDS containing all the reference +anatomical and physiological values for that specific age/sex class, e.g., + +species.age35m + +contains all information for a reference male human. Most entries +are self-explanatory. In addition, the units of the entries are +specified in species.age35m.units. The subfield 'tissue' contains +the indices of the different tissues and groups of tissues. Hence, +the reference value of the liver volume of a 15y old boy can be +accessed by + +species.age15m.V(liv) + +To look at all 13 volumes in a nicely formated way, use + +printNicely(species.age15m.V) + +The structure is assigned in GenericPBPKmodel_physiology.m + + + +%%% ================================================================= +%%% VARIABLE: drug +%%% ================================================================= + +drug is a STRUCTURE with the FIELD specifying the physico-chemical and +pharmacokinetic properties of the drug. Since the latter values are +species specific, drug.human and drug.rat contains the PK values related +to human and rat, respectively. + +The structure is assigned in GenericPBPKmodel_drugDatabase.m + + + +%%% ================================================================= +%%% VARIABLE: study +%%% ================================================================= + +study is a STRUCTURE with FIELDs specifying the dose and +the route of application (iv bolus, iv infusion, po), the simulation time +in [min], the output time points, etc. Dosing can be fixed in unit [mg] or +per body size descriptor (BSD) in unit [mg/per unit of the BSD]. +For a constant dose (e.g., 100mg), choose the option 'fixed'. + +The structure is assigned in GenericPBPKmodel.m + + + +%%% ================================================================= +%%% VARIABLE: reference +%%% ================================================================= + +reference is a STRUCTURE build from the species structure. While the +species structure is like a database containing all species information, +the reference structure merges the relevant information of the three +structures species, drug and study. One might think of the reference +structure representing a volunteer/patient that is given a drug in a +predescribed way. + +The substructure of reference is analogous to the species substructure. + +The structure is assigned in GenericPBPKmodel_referenceIndividual.m + + + +%%% ================================================================= +%%% VARIABLE: population +%%% ================================================================= + +population is a STRUCTURE containing all relevant information to +generate a virtual population comprising a specified number (size) +of individuals. The generation of an individual is based on the +following information + +age (class), sex, BH, BW + +Based on this information and the data of the reference (of that age/sex), +an individual is generated based on scaling (see article by Huisinga et al., +PSP 2012 referenced above for more details on the algorithm). + +The data regarding a single individual of the population +are stored in the vector individual (see below). + +The structure population contains a summary of the population +characteristics, e.g., + +population.BMI + +gives the body mass indices of all individuals of the population. + +For information on available virtual populations see GenericPBPKmodel.m. + +The structure is assigned in GenericPBPKmodel.m and filled with further +information in GenericPBPKmodel_randomEffects.m. + + + +%%% ================================================================= +%%% VARIABLE: individual +%%% ================================================================= + +individual is a VECTOR of STRUCTURES build from the reference structure +and target age, sex, BH, BW specified in population. For a population +of at least three individuals, + +individual(3) + +contains all information regarding individual No 3. Individual(3) is +a STRUCTURE analogous to the reference structure for given age/sex. + +The structure is assigned in GenericPBPKmodel_virtualPopulation.m, +potentially reassigned in GenericPBPKmodel_randomEffect.m. Predicted +concentration time data are generated in +GenericPBPKmodel_simulatePBPKmodel.m. + + + +%%% ================================================================= +%%% VARIABLE: randomEffects +%%% ================================================================= + +randomEffects is a STRUCTURE containing information about possible +random effects (perturbations) of the two parameters CL (blood clearance) +and Ktis (tissue-to-blood partition coefficient). The size of the +random perturbations is specified by facX. As a consequence, all +individuals will be assigned randomly perturbed CL and Ktis values, if +facX is different from 0. + +The random effects are realized in GenericPBPKmodel_randomEffects.m. + + + +%%% ================================================================= +%%% VARIABLE: ShowGraphics +%%% ================================================================= + +ShowGraphics is a STRUCTURE specifying the output options. + +The structure is assigned in GenericPBPKmodel.m. + + + +%%% ================================================================= +%%% VARIABLE: lumping +%%% ================================================================= + +lumping is a CELL specifying the grouping of the tissues into different +lumped compartments. The central compartment is defined as the one +that contains ven. For example, + +lumping = {[T.mus,T.adi,T.bon,T.ski],[T.bra,T.hea,T.kid,T.gut,T.spl,T.liv,T.lun,T.art,T.ven]}; + +generates a two compartment model with peripheral compartment containing +mus, adi, bon and ski, while the central compartment comprises the +remaining tissues. + +Based on the lumping variable, a lumped compartment model is constructed +and simulated. The results are assigned to the STRUCTURE L or minL. + +The cell lumping is assigned in GenericPBPKmodel.m. The structure L and +minL are assigned in GenericPBPKmodel_mechanisticLumping.m. + + diff --git a/src/utils/PKHuisinga/fett.m b/src/utils/PKHuisinga/fett.m new file mode 100644 index 0000000..b6f28b2 --- /dev/null +++ b/src/utils/PKHuisinga/fett.m @@ -0,0 +1,19 @@ +function out = fett(figNr) + +% Increases line width, font size, etc. +% Call by, e.g., fett(2) + +figure(figNr); + +lw = 2; %line width +fs = 18; %fontsize + +h = findobj(gca,'Type','line'); +set(h,'LineWidth',lw); + +set(gca,'FontSize',fs); +set(gca,'LineWidth',lw); +set(get(gca,'title'),'Fontsize',fs); +set(get(gca,'xlabel'),'Fontsize',fs); +set(get(gca,'ylabel'),'Fontsize',fs); +set(legend,'Fontsize',fs); \ No newline at end of file diff --git a/src/utils/PKHuisinga/printNicely.m b/src/utils/PKHuisinga/printNicely.m new file mode 100644 index 0000000..1c2e8f2 --- /dev/null +++ b/src/utils/PKHuisinga/printNicely.m @@ -0,0 +1,10 @@ +function out = printNicely(data) + +organName = ['lun';'art';'bra';'adi';'hea';'kid';'mus';'bon';'ski';... + 'gut';'spl';'liv';'ven']; + +fprintf('\n\n'); +for m=1:13 + fprintf(' %s = %3.4f\n',organName(m,:),data(m)) +end; +fprintf('\n\n'); diff --git a/src/utils/PlasmaConcentrationError.m b/src/utils/PlasmaConcentrationError.m new file mode 100644 index 0000000..f42ab9f --- /dev/null +++ b/src/utils/PlasmaConcentrationError.m @@ -0,0 +1,128 @@ +clear; +clc; +close all; +%%Calculate the LSQ Error of the plasma concentration curves for the +%%BioGears PK model and experimental human data + +%% Base Path Assignment +pathbase = [pwd,'\..\..']; + +%FileStoragePath = [pathbase, '\..\Publications\IEEEConference2016\']; +FileStoragePath = [pathbase, '\..\Publications\MMVR2016\']; + +%% List the drugs being compared and analyzed in an array - Can be used to loop over the drugs +% This character string array is used to cycle through each drug +NAMES = {'Fentanyl';'Ketamine';'Midazolam';'Morphine';'Naloxone';'Pralidoxime';... + 'Prednisone';'Propofol';'Rocuronium';'Succinylcholine';}; +% NAMES = {'Morphine'}; + +%%Single Drug Override of the array +%NAMES = {'Fentanyl';'Ketamine'}; + +%% Number of drugs being analyzed +Num_Files = length(NAMES); + +for i = 1:Num_Files + + %% Drug is the name of the drug as pulled from the looped array + Drug = char(NAMES(i)); + + %%Experimental Data file + Experimental = [pathbase,'\docs\Validation\Pharmacokinetic\', Drug, '.txt']; + %%BioGears Data File + BG = [pathbase,'\bin\Scenarios\Validation\', Drug, 'ValidationResults.txt']; + + %%Import Data + ExperimentalData = importdata(Experimental,'\t',1); + ExperimentalData = ExperimentalData.data; + + BGData = readtable(BG,'Delimiter',','); + + %%Get the table column headers and search for plasma concentration + ColumnHeaders = BGData.Properties.VariableNames; + match = 0; + for j=1:length(ColumnHeaders) + test = char(ColumnHeaders(j)); + match = ~isempty(strfind(test,[Drug,'_PlasmaConcentration_ug_mL_'])); + if match == 1 + break; + end + end + + %%Calculate Error between the experimental and computed data + for k=1:length(ExperimentalData) + time = ExperimentalData(k,1); + %Find the corresponding time in the BioGears Data + if(time == 0) + RowNumber = 1; + else + sampleTime = BGData{2,1} - BGData{1,1}; + if time < sampleTime + RowNumber = 1; + else + RowNumber = round(time/sampleTime); + end + end + + if RowNumber > length(BGData{:,1}) + break; + end + + if strcmp(BGData{RowNumber,j},'-1.#QNAN') + error(k) = (ExperimentalData(k,2) - 0)^2; + else + error(k) = (ExperimentalData(k,2) - BGData{RowNumber,j})^2; + end + end + RMSError(1,i) = sum(error)^0.5; + RMSError(2,i) = RMSError(1,i)/(max(ExperimentalData(:,2))-min(ExperimentalData(:,2))); + + + %%Plot experimental vs computed data and save the image in the correct + %%size and format (initially defined for IEEE standards) + %%FS = 8; %IEEE + FS = 16; %Posters + FN = 'Times New Roman'; + fig = figure; + semilogy(BGData{:,1},BGData{:,j},'LineStyle','-','LineWidth',2.0,'Color','k') + hold on + semilogy(ExperimentalData(:,1),ExperimentalData(:,2),'LineStyle','none','Marker','*','MarkerSize',8,'Color','b') + xlabel('Time (s)','FontName',FN, 'FontSize',FS) + ylabel('Plasma Concentration (ug/mL)','FontName',FN, 'FontSize',FS) + title(Drug,'FontName',FN, 'FontSize',FS) + %%legend('BioGears', 'Experimental Data') + ax=gca; + ax.FontSize = FS; + ax.FontName = FN; + ax.XGrid = 'on'; + ax.YGrid = 'on'; + ax.XLim = [0 max(BGData{:,1})]; + + %%Results Storage + fig.PaperUnits = 'inches'; + %fig.PaperPosition = [0 0 1.5 1.5]; %IEEE Size + fig.PaperPosition = [0 0 4 4]; + fig.PaperPositionMode = 'manual'; + FileStorage = [FileStoragePath,Drug,'Plot']; + if exist(FileStoragePath, 'dir') + print(FileStorage, '-dtiff'); + else + mkdir(FileStoragePath) + print(FileStorage, '-dtiff'); + end +end + +%%Save RMS Error Table +%%Create a table for the RMS Error Results +casenames = {'RMSError','NormalizedRMSError'}; +variablenames = NAMES'; +FileStorage = [FileStoragePath,'RMSErrorTable.txt']; +T = array2table(RMSError,'RowNames',casenames,'VariableNames',NAMES'); +if exist(FileStoragePath, 'dir') + writetable(T,FileStorage,'WriteRowNames',true); +else + mkdir(FileStoragePath) + writetable(T,FileStorage,'WriteRowNames',true); +end + +close all; \ No newline at end of file diff --git a/src/utils/UNC PK Analysis/PKDataShift.m b/src/utils/UNC PK Analysis/PKDataShift.m new file mode 100644 index 0000000..c4d705d --- /dev/null +++ b/src/utils/UNC PK Analysis/PKDataShift.m @@ -0,0 +1,22 @@ +function [holder] = PKDataShift(DAT); + + + + +m = max(DAT(:,2)); + + +loc = find(DAT(:,2) == m); + + + + +[L W] = size(DAT); +counter = 1; + +for i = 1:L + if i >= loc + holder(counter,:) = DAT(i,:); + counter = counter + 1; + end +end \ No newline at end of file diff --git a/src/utils/UNC PK Analysis/PK_Validation_Script.m b/src/utils/UNC PK Analysis/PK_Validation_Script.m new file mode 100644 index 0000000..8c4a523 --- /dev/null +++ b/src/utils/UNC PK Analysis/PK_Validation_Script.m @@ -0,0 +1,365 @@ + +close all; +clc; + +%% +%{ + + This MATLAB script reads in PK data provided by UNC and from the + BioGears scenario runs. This script then performs statistical analysis + of each dataset and makes comparisons. Residuals and percent error + are plotted in addition to the raw data and corresponding fits. Linear + regressions are found for the diffusion dominated and clearance + dominated portions of the BG concentration vs. time curves. These + linear fits are used to determine the viability of BG to represent the + scenarios presented in the UNC PK data. + +%} + + + +% UNC PK data is in a text file and consists of one header line +% and two columns of data --> (time, concentration) + + +%% + +% creates a path to the BG trunk + +pathbase = [pwd,'\..\..\..']; + +%% +%%%%%% create array of character strings for the drug names %%%%%% +% This character string array is used to cycle through each drug + +NAMES = {'Fentanyl';'Furosemide';'Ketamine';'Midazolam';'Morphine';'Naloxone';'Pralidoxime';... + 'Prednisone';'Propofol';'Rocuronium';'Succinylcholine';}; + +NAMES = {'Albuterol'}; + +%%%%%% create array of character strings for the BG and UNC data %%%%%% + +%%% Num_Files provides the upper limit for the file iteration loop +Num_Files = length(NAMES); + +EC50=0; + + +%% File iteration loop begins here. The for loop where the bulk of the script +% is contained simply calls the next drug files as i is incremented. This +% process is continued until all drug files have been analyzed. + +for i = 1:Num_Files + + + %% Current_NAME is saved to easily reference the current drug name + % It is used to create titles, column names, etc. + %%% save the name of the current drug being analyzed %%% + Current_NAME = char(NAMES(i)); + + %%% save the name of the current drug being analyzed %%% + + %% Create BG and UNC filenames + + Experimental = [Current_NAME, '.txt']; + + BG = [Current_NAME, 'ValidationResults.txt']; + + %% Create path to filenames in the bin directory + + BGdatapath = [pathbase, '\bin\Scenarios\Validation\',BG]; + Experimentaldatapath = [pathbase, '\docs\Validation\Pharmacokinetic\',Experimental]; + imagebase = [pathbase, '\docs\Doxygen\html\images\Drugs\']; + + + %% Check to see if file exists before opening + + if exist(BGdatapath,'file') == 0 || exist(Experimentaldatapath,'file') == 0 + continue + end + + %% Read in all the data for the current drug + %%%%%%%% Import Data from current directory %%%%%%%% + + Experimental = importdata(Experimentaldatapath,'\t',1); + Experimental = Experimental.data; + + BG = importdata(BGdatapath); + %%BG = readtable(BGdatapath,'Delimiter','\t'); + %%% use string search to find correct column of data + % the variable 'check' is saved as the column number that contains the + % plasma concentration data --> 'check' is now the reference column for + % the BG plasma concentration data + %%check = BG.Properties.VariableNames; + %%check = strsplit(char(BG.textdata)); + %%column_title = [Current_NAME,'_PlasmaConcentration_ug_mL_']; + %%check = find(strcmp(check,column_title) == 1); %finds correct column for plasma concentration data + check = 2; + %%% use string search to find correct column of data + + + %% Below is a logic check to ensure that the variable 'check' contains + %%% a number and is not empty. If 'check' is empty, there is not a + %%% column with the current drug's plasma concentration data and a + %%% warning is presented. The loop restarts with i incremented by 1, + %%% moving on to the next drug. + + tf = isempty(check); + if tf == 1 + msg1 = 'WARNING!!!'; + msg2 = ['THERE IS NO PLASMA CONCENTRATION DATA FOR: ', Current_NAME]; + disp(msg1) + disp(msg2) + + Avg_PercentError(i,1) = 1/0; + Max_PercentError(i,1) = 1/0; + Min_PercentError(i,1) = 1/0; + + Avg_Residuals(i,1) = 1/0; + Max_Residuals(i,1) = 1/0; + Min_Residuals(i,1) = 1/0; + + continue + end + + % convert the BG table into a cell array + %%BG = table2cell(BG); + [L,W] = size(BG.data); + BG = BG.data; + %%%%%%%% Import Data from current directory %%%%%%%% + + + %% clear the QNANs out of the data + % The BG output files contains cells populated by '-1.#QNAN' that need + % to be reassigned as zeros. This portion of the code replaces the + % '-1.#QNAN's with zeros and converts the remaining rows to numbers + % rather than characters + for j = 1:L + for k = 1:W + + %%% Reassign '-1.#QNAN' as zeros %%% + tf = strcmp(BG(j,k),'-1.#QNAN'); + if tf == 1 + BG{j,k} = 0; + end + + % Convert numbers from charater strings to reals in the case + % that there were QNANs present at the begining of the column + tf = iscellstr(BG(j,k)); + if tf == 1 + BG{j,k} = str2num(BG{j,k}); + end + + end + end + + % convert the BG cell array to a double array + %%BG = cell2mat(BG); + + + %% Apply a fit to the BG data to find the transition points + + %%[fitresult, gof] = createFit(BG(:,1),BG(:,check)); + + + + + + +% %% implement smooth function for BG here and use the result to find transition locations +% % this will take the BG data and smooth out each time step using a +% % running average, making the transition point determination more +% % accurate + +% [smooth_BG] = smoothBG(BG,check); +% +% +% %% Use the discrete_differentiation function to get a set of discrete +% % second derivatives in the bounds of BG in order to determine +% % transitional regions in the data +% [second_derivative] = discrete_differentiation(smooth_BG); + + +%%smooth_BG(:,1) = BG(:,1); +%smooth_BG(:,2) = BG(:,check); + +%[second_derivative] = discrete_differentiation(smooth_BG); +% +% % take the absolute value of the second derivative data +% % only need to look at the magnitude of transitions rather than the +% % direction +% second_derivative(:,:) = abs(second_derivative(:,:)); +% +% second_derivative(:,:) = BG(:,:); + +% % use lower bound function to find the lower bound of the diffusion +% % dominant region in the plasma concentration vs. time curve +% [lower_bound] = lowerbound(second_derivative,Current_NAME); + + + %% Find the maximum of the UNC data and set that as the first data point + %lower_bound = find(BG(:,check)==max(BG(:,check))); + + +% %% try smoothing out the second derivative results + +% [smooth_second_derivative] = smoothing(second_derivative); +% [smoother_second_derivative] = smoothing(smooth_second_derivative); +% [smootherer_second_derivative] = smoothing(smoother_second_derivative); +% +% clear second_derivative; +% second_derivative(:,:) = smootherer_second_derivative(:,:); +% clear smoother_second_derivative; +% clear smootherer_second_derivative; + + +% %% use the transition bounds function to find the first and second transition points +% [first_transition,second_transition] = transition_bounds(second_derivative,lower_bound,Current_NAME); + + + +% %% Use stats function to get Pointwise Residuals, Percent Error, and MSE +% [Pointwise_Residuals,Pointwise_PercentError,MSE_D,MSE_C,MSE_T,LSE_D,LSE_C,LSE_T] = stats(i,UNC,BG,first_transition,second_transition); +% +% % save the MSE and LSE for each drug for use in the doxy table +% MSE_diffusion(i,1) = MSE_D; +% MSE_clearance(i,1) = MSE_C; +% MSE_transition(i,1) = MSE_T; +% LSE_diffusion(i,1) = LSE_D; +% LSE_clearance(i,1) = LSE_C; +% LSE_transition(i,1) = LSE_T; +% +% +% +% %% creating table +% +% Avg_PercentError(i,1) = mean(Pointwise_PercentError); +% Max_PercentError(i,1) = max(Pointwise_PercentError); +% Min_PercentError(i,1) = min(Pointwise_PercentError); +% +% Avg_Residuals(i,1) = mean(Pointwise_Residuals); +% Max_Residuals(i,1) = max(Pointwise_Residuals); +% Min_Residuals(i,1) = min(Pointwise_Residuals); + + + + +% %% create text and line objects to put in each region on the plots +% +% %these are referenced as distance from the y-axis +% %bar1_upper = lower_bound + .05*(BG(end,1)); +% %bar2_upper = first_transition + .05*(BG(end,1)); +% +% bar1_upper = 0.024 * BG(end,1); +% bar2_upper = 0.024 * BG(end,1); +% +% +% %these are now y-idecies--> distance from the x-axis +% bar3_lower = BG(second_transition,check); +% bar3_upper = BG(second_transition,check) + .01*(max(BG(:,check))); +% bar4_lower = BG(end,check); +% bar4_upper = BG(end,check) + .01*(max(BG(:,check))); +% +% % special cases where the general size doesn't work +% +% if strcmp(Current_NAME,'Pralidoxime') == 1 +% bar4_upper = BG(end,check) + .00002*(max(BG(:,check))); +% end + + + + + +% % text box positions +% box1_xposition = lower_bound + .05*(BG(end,1)); +% box1_yposition = (BG(lower_bound,check) + BG(first_transition,check)) / 2.4; +% +% box2_xposition = (second_transition + BG(end,1)) / 2.4; +% box2_yposition = BG(end,check) + .03*(max(BG(:,check))); +% +% box3_xposition = (first_transition + second_transition) / 2.4; +% box3_yposition = (BG(first_transition,check) + BG(second_transition,check)) / 1.7; + + + + %% Plot literature data and BG data as well as transition points + + fig = figure('units','normalized','outerposition',[0 0 1 1],'Color', 'white'); + scatter(Experimental(:,1)/60,Experimental(:,2),'b', 'filled'); + set(gca,'yscale','log'); + hold on + semilogy(BG(:,1)/60,BG(:,check), 'LineWidth', 1); +% hold on +% bar1 = plot([0 bar1_upper],[BG(lower_bound,check) BG(lower_bound,check)], 'k', 'LineWidth', 2); +% hold on +% bar2 = plot([0 bar2_upper],[BG(first_transition,check) BG(first_transition,check)], 'k', 'LineWidth', 2); +% hold on +% bar3 = plot([second_transition second_transition],[bar3_lower bar3_upper], 'k', 'LineWidth', 2); +% hold on +% bar4 = plot([BG(end,1) BG(end,1)],[bar4_lower bar4_upper], 'k', 'LineWidth', 2); +% hold on +% box1 = text(box1_xposition, box1_yposition,'Diffusion Region'); +% box1.FontSize = 10; +% hold on +% box2 = text(box2_xposition, box2_yposition,'Clearance Region'); +% box2.FontSize = 10; +% hold on +% box3 = text(box3_xposition, box3_yposition, 'Transitional Region'); +% box3.FontSize = 10; +% hold off + + y_name = [Current_NAME,' Plasma Concentration (ug/mL)']; + if BG(end,1) > Experimental(end,1) + xlim([0 BG(end,1)/60]); + else + xlim([0 Experimental(end,1)/60]); + end + xlabel('Time (minutes)', 'FontSize', 10); + ylabel(y_name, 'FontSize', 10); + legend('Experimental','BioGears'); + + Image_NAME = [imagebase,Current_NAME,'PKValidation.png']; + img = getframe(fig); + imwrite(img.cdata, [Image_NAME]); + + % Find the cmax and EC50 for the drugs + Cmax = max(BG(:,check)); + EC50(length(EC50)+1)=Cmax/32.0; + +end + + + +% %% compile table +% inputTable = table(MSE_diffusion,MSE_clearance,MSE_transition,LSE_diffusion,LSE_clearance,LSE_transition,Avg_PercentError,Max_PercentError,Min_PercentError,Avg_Residuals,Max_Residuals,Min_Residuals,'RowNames',NAMES); +% tableName = 'BioGears PK Verification and Validation '; + + + +%% Overwrite NANs in the table as N/A ----> broken!!!!! + +%{ +[L,W] = size(inputTable); + +for j = 1:L + for k = 1:W + tf = isnan(inputTable{j,k}); + if tf == 1 + inputTable{j,k} = 'N/A'; + end + end +end + +%} +%% put talble into doxyTable function to get table ready for doxygen software + +% [doxyFormattedTable] = doxyTable(inputTable,tableName); + + + + + + + + + \ No newline at end of file diff --git a/src/utils/UNC PK Analysis/bars.m b/src/utils/UNC PK Analysis/bars.m new file mode 100644 index 0000000..0654a39 --- /dev/null +++ b/src/utils/UNC PK Analysis/bars.m @@ -0,0 +1,65 @@ +function [bar1,bar2,bar3,bar4] = bars(BG,lower_bound,first_transition,second_transition,check) + +% This function creates the horizontal and vertical lines used to delimit +% the diffustion, transitional, and clearance regions of the plasma +% concentration vs. time curve + + + +%{ +%these are referenced as distance from the y-axis +bar1_lower = 1; +bar1_upper = lower_bound + 250; +bar2_lower = 1; +bar2_upper = first_transition + 250; + +%these are now y-idecies--> distance from the x-axis +bar3_lower = 1; +bar3_upper = ceil(BG(second_transition,check) + .2*(max(BG(:,check)))); +bar4_lower = 1; +bar4_upper = ceil(BG(second_transition,check) + .2*(max(BG(:,end)))); + + +%% need to initialize the arrays with zeros + +bar1 = zeros(bar1_upper,2); +bar2 = zeros(bar2_upper,2); +bar3 = zeros(bar3_upper,2); +bar4 = zeros(bar4_upper,2); + + +%% x-values +% horizontal lines +bar1(:,1) = BG(bar1_lower:bar1_upper,1); +bar2(:,1) = BG(bar2_lower:bar2_upper,1); + + +% vertical lines +bar3(:,1) = BG(second_transition,1); +bar4(:,1) = BG(end,1); + + +%% y-values +% horizontal lines +bar1(:,2) = BG(lower_bound,check); +bar2(:,2) = BG(first_transition,check); + +% vertical lines +bar3(:,2) = BG(bar3_lower:bar3_upper,check); +bar4(:,2) = BG(bar4_lower:bar4_upper,check); + +%} + + +end + + + + + + + + + + + diff --git a/src/utils/UNC PK Analysis/createFit.m b/src/utils/UNC PK Analysis/createFit.m new file mode 100644 index 0000000..dea6017 --- /dev/null +++ b/src/utils/UNC PK Analysis/createFit.m @@ -0,0 +1,59 @@ +function [fitresult, gof] = createFit(x, y) +%CREATEFIT1(X,Y) +% Create a fit. +% +% Data for 'untitled fit 1' fit: +% X Input : x +% Y Output: y +% Output: +% fitresult : a fit object representing the fit. +% gof : structure with goodness-of fit info. +% +% See also FIT, CFIT, SFIT. + +% Auto-generated by MATLAB on 14-Aug-2015 16:33:11 + +%% trim the data for the fit + +holder = max(y); + +L = length(y); + +for i = 1:L + if y(i) == holder + break + end +end + +clearvars holder; + +holder(:,1) = x(i:end); +holder(:,2) = y(i:end); + +clearvars x y; + +x = holder(:,1); +y = holder(:,2); + + +%% Fit: 'createFit'. +[xData, yData] = prepareCurveData( x, y ); + +% Set up fittype and options. +ft = fittype( 'smoothingspline' ); +opts = fitoptions( 'Method', 'SmoothingSpline' ); +opts.SmoothingParam = 0.001; + +% Fit model to data. +[fitresult, gof] = fit( xData, yData, ft, opts ); + +% Plot fit with data. +figure( 'Name', 'untitled fit 1' ); +h = plot( fitresult, xData, yData ); +legend( h, 'y vs. x', 'untitled fit 1', 'Location', 'NorthEast' ); +% Label axes +xlabel x +ylabel y +grid on + + diff --git a/src/utils/UNC PK Analysis/discrete_differentiation.m b/src/utils/UNC PK Analysis/discrete_differentiation.m new file mode 100644 index 0000000..08c4419 --- /dev/null +++ b/src/utils/UNC PK Analysis/discrete_differentiation.m @@ -0,0 +1,33 @@ +function [second_derivative] = discrete_differentiation(smooth_BG) + +%this function takes the data from BG results and finds the +%second derivative of the data, allowing for the determination of +%transitional points in the concentration curves + +%since this function takes the second derivative at discrete points, two +%data points are lost as the derivative between data points can only be +%found --> the number of data points is so large that this is not a problem + +A(:,1) = smooth_BG(:,1); +A(:,2) = smooth_BG(:,2); + +[L,~] = size(A); + +%% first derivative +for i = 1:L-1 + B(i,1) = (A(i,1) + A(i+1,1)) / 2; + B(i,2) = (A(i,2) - A(i+1,2)) / (A(i,1) - A(i+1,1)); +end + + +[L,~] = size(B); + +%% second derivative +for i = 1:L-1 + C(i,1) = (B(i,1) + B(i+1,1)) / 2; + C(i,2) = (B(i,2) - B(i+1,2)) / (B(i,1) - B(i+1,1)); +end + +second_derivative = C(:,:); + +end diff --git a/src/utils/UNC PK Analysis/doxyTable.m b/src/utils/UNC PK Analysis/doxyTable.m new file mode 100644 index 0000000..8ec4030 --- /dev/null +++ b/src/utils/UNC PK Analysis/doxyTable.m @@ -0,0 +1,127 @@ +function [doxyFormattedTable] = doxyTable(inputTable,tableName) + +pathBase = pwd; +%% Get info about the inputTable into usable form +rows = inputTable.Properties.RowNames; +columns = inputTable.Properties.VariableNames; +cellArray = table2cell(inputTable); + +[CAlength, CAwidth] = size(cellArray); + +if(~isempty(columns)) %make variable names first row if they exist + for i = CAlength+1:-1:2 + cellArray(i,:) = cellArray(i-1,:); + end + cellArray(1,:) = columns; +end +[con loc] = ismember('PercentError', columns); + +if(con ~=0) + for i = 2:1:CAlength+1 + if(~isnan(str2double(cellArray{i,loc}))) + toHTML = str2double(cellArray{i,loc}); + if(toHTML <= 10) + cellArray{i,loc} = ['', char(num2str(toHTML,'%-5.2g')),'']; + elseif(toHTML > 10 && toHTML <= 30) + cellArray{i,loc} = ['', char(num2str(toHTML,'%-5.2g')),'']; + else + cellArray{i,loc} = ['', char(num2str(toHTML,'%-5.2g')),'']; + end + elseif(ischar(cellArray{i,loc})) + if(strcmp('Within range',cellArray{i,loc})) + cellArray{i,loc} = ['',cellArray{i,loc},'']; + else + cellArray{i,loc} = ['',cellArray{i,loc},'']; + end + end + end +end +if(~isempty(rows)) %make rownames first column if they exist + for i = CAwidth+1:-1:2 + cellArray(:,i) = cellArray(:,i-1); + end + cellArray(1,1) = {'Parameter Name'}; + cellArray(2:end,1) = rows; +end + +[CAlength, CAwidth] = size(cellArray); %refresh the size measurement + +%% Looks like 2nd row needs to be |---------------- | or something like that... so adding that in +% |------------------------ => string to add into 2nd row +for i = CAlength+1:-1:2 + cellArray(i,:) = cellArray(i-1,:); +end + +cellArray(2,:) = {'|------------------------'}; + +[CAlength, CAwidth] = size(cellArray); +%% Now make all cells characters, the same length per row, and add the | + +charCellArray = cell(CAlength,CAwidth); + +for i = 1:1:CAlength + for j = 1:1:CAwidth + if(isnumeric(cellArray{i,j})) + charCellArray{i,j} = char(num2str(cellArray{i,j},'%-5.2g')); + else + charCellArray{i,j} = char(cellArray{i,j}); + end + end +end + +%first add the '|' to every row except the 2ns + +for i = 1:1:CAlength + if(i == 2) + continue; + end + for j = 1:1:CAwidth + charArray = charCellArray{i,j}; + for k = length(charArray)+1:-1:2 + charArray(k) = charArray(k-1); + end + charArray(1) = char('|'); + charCellArray{i,j} = charArray; + end +end + +% make the same length +rowSize = 0; +for i = 1:1:CAwidth + for j = 1:1:CAlength + if(length(charCellArray{j,i})>rowSize) + rowSize = length(charCellArray{j,i}); + end + end + for j = 1:1:CAlength + charArray = charCellArray{j,i}; + while (length(charArray)<(rowSize + 5)) + charArray(end + 1) = ' '; + end + charCellArray{j,i} = charArray; + end +end +%% add a final '|' to the last column + +for i = 1:1:CAlength + charArray = charCellArray{i,end}; + charArray(end+1) = char('|'); + charCellArray{i,end}= charArray; +end + +doxyFormattedTable = charCellArray; +%% save it all +formatSpec = '%s '; +[DFTlength, DFTwidth] = size(doxyFormattedTable); +for i = 2:1:DFTwidth-1 + formatSpec(end+1:end+3) = '%s '; + if(i==DFTwidth-1) + formatSpec(end+1:end+4) = '%s\n'; + end +end +%savePath = [pathBase, '\..\..\..\docs\Validation\MatlabTables\', char(tableName), 'doxyTable.txt']; +savePath = [pathBase,'\',char(tableName), 'doxyTable.txt']; +fID = fopen(savePath, 'w'); +for i = 1:1:DFTlength + fprintf(fID,formatSpec,doxyFormattedTable{i,:}); +end \ No newline at end of file diff --git a/src/utils/UNC PK Analysis/lowerbound.m b/src/utils/UNC PK Analysis/lowerbound.m new file mode 100644 index 0000000..3f57e69 --- /dev/null +++ b/src/utils/UNC PK Analysis/lowerbound.m @@ -0,0 +1,37 @@ +function [lower_bound] = lowerbound(second_derivative,Current_NAME) + +% This function uses the second derivative to determine at what point the +% plasma concentration curve of a given drug becomes linear with respect to +% time. When the second derivative at a given time is below a tuned cutoff +% parameter, the concentration vs. time curve is linear. This search only +% needs the current time step to be less than the cutoff parameter due to +% the coarse (with respect to time) nature of the data + +% The lower bound found below also signifies the beginning of the +% diffusion-dominant portion of the concentration-time curve + +L = length(second_derivative); +epsT = 0.002; + +derp = strcmp(Current_NAME,'Pralidoxime'); +if derp ==1 + epsT = 0.1; +end + + + +tf = strcmp(Current_NAME,'Propofol'); +if tf == 1 + epsT = 0.1; +end + + +for i = 1:L + holder(i) = second_derivative(i,2); + if holder(i)= second_transition + C_counter = C_counter + 1; + LSE_C = (A(j,1) - UNC(j,2))^2 + LSE_C; + elseif UNC(j,1) < second_transition && UNC(j,1) > first_transition + T_counter = T_counter + 1; + LSE_T = (A(j,1) - UNC(j,2))^2 + LSE_T; + end +end + + +MSE_D = LSE_D / D_counter; +MSE_C = LSE_C / C_counter; +MSE_T = LSE_T / T_counter; + +end + + + + + + + + + + diff --git a/src/utils/UNC PK Analysis/transition_bounds.m b/src/utils/UNC PK Analysis/transition_bounds.m new file mode 100644 index 0000000..e953dbb --- /dev/null +++ b/src/utils/UNC PK Analysis/transition_bounds.m @@ -0,0 +1,148 @@ +function [first_transition,second_transition] = transition_bounds(second_derivative,lower_bound,Current_NAME) + +%this function takes the second derivative information and finds transition +%points that denote the diffusion dominated and clearance dominated +%portions of the plasma concentration vs. time curve + + +%% find the first transition point --> point that begins transition region + +L = length(second_derivative); +epsT = 0.001; +tf = 1; +first_transition = []; +index = floor((1 / 10) * second_derivative(end,1)); +counter = 0; + + + +% each drug has slightly different PK respsonses, thus each drug has +% different stopping criteria (epsT) that denotes transitions between +% characteristic portions of the plasma concentration vs. time curve + + +derp = strcmp(Current_NAME,'Ketamine'); +if derp == 1 + epsT = 0.00009; +end + +derp = strcmp(Current_NAME,'Propofol'); +if derp == 1 + epsT = 0.008; +end + +derp = strcmp(Current_NAME,'Prednisone'); +if derp ==1 + epsT = 0.00005; +end + +derp = strcmp(Current_NAME,'Morphine'); +if derp ==1 + epsT = 0.0001; +end + +derp = strcmp(Current_NAME,'Midazolam'); +if derp ==1 + epsT = 0.000008; +end + +derp = strcmp(Current_NAME,'Naloxone'); +if derp ==1 + epsT = 0.0000003; +end + +while tf == 1 + epsT = epsT * .9999; + for i = index:-1:lower_bound+1 + counter = counter + 1; + holder(i) = second_derivative(i,2); + if i <= index - 3 + if mean(holder(i:i + 2)) >= epsT + first_transition = i; + tf = 0; + break + end + end + end +end + + +%% find the second transition point --> where clearance is dominant + +iteration_number = 0; +epsT = 0.00003; +tf = 1; +second_transition = []; + +derp = strcmp(Current_NAME,'Ketamine'); +if derp == 1 + epsT = 0.0000045; +end + +derp = strcmp(Current_NAME,'Propofol'); +if derp == 1 + epsT = 0.001; +end + +derp = strcmp(Current_NAME,'Prednisone'); +if derp ==1 + epsT = 0.0000015; +end + +derp = strcmp(Current_NAME,'Morphine'); +if derp ==1 + epsT = 0.0000001; +end + +derp = strcmp(Current_NAME,'Midazolam'); +if derp ==1 + epsT = 0.0000003; +end + +derp = strcmp(Current_NAME,'Naloxone'); +if derp ==1 + epsT = 0.0000004; +end + + +% general criteria +g = 25 + +% special cases for a larger averaging samples +if strcmp(Current_NAME,'Midazolam') ==1 + g = 5000 +end + + + +'second_transition' +while tf == 1 + counter = 0; + for j = first_transition:L + counter = counter + 1; + holder(j) = second_derivative(j,2); + if counter >= g + 1 + if mean(holder((j-g):j)) <= epsT + second_transition = j; + tf = isempty(second_transition); + if tf == 0 + break + end + end + end + end + epsT = epsT + .0000001 +end + +end + + + + + + + + + + +