//////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2016-2017 Dawson Dean // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // //////////////////////////////////////////////////////////////////////////////// // // Main_InitHDSimModule // Main_ShowWindow // Main_OnCloseChildWindow // Main_ShowError // OnOptionButton // LogEvent // // Chart_DrawAxes // Chart_DrawLineSegment // Chart_DrawYThreshold // // Util_GetInputNumber // // Main_StartSim // Main_StopSim // Main_Resume // Main_SimulateOneTimePeriod // //////////////////////////////////////////////////////////////////////////////// ///////////////////////// // HTML Elements - many other HTML elements are declared in // These are the main windows that we show/hide to expose different subgroups of funtions and outputs. var g_SketchWindowDivElement = null; var g_AboutWindowDivElement = null; // The timer var g_IntervalNum = 0; var g_PrevXValue = 0; var g_CallbackID = -1; var g_PeriodInMilliSecs = 50; var g_SimulationIsRunning = false; // The canvas var g_FullFlowChartAndCanvasDiv = null; var g_MainCanvasElement = null; var g_MainCanvasElementContext = null; var g_CanvasWidth = -1; var g_CanvasHeight = -1; var g_XMovementPerTick = 10; var g_XMovementPerTickBetweenDialysis = 2; var g_XMovementPerTickDuringDialysis = 10; var g_GapLeftYAxis = 20; var g_GapBelowXAxis = 20; var g_MaxYValueInChart = 10; // For total levels in mg - 1000000 // This is the actual value for anti-CD3 antibody, called hu12F6mu. <> This will change for every antibody and protein var g_LightChainPermeabilityCoefficient = 0.000936; // MGUS is 0.3 g/dL var g_MGUSLevelInGramsPerDeciliter = 0.3; var g_MGUSLevelInAbsoluteProteins = -1; var g_MGUSLevelInMgPerMilliliter = -1; var g_DrawnMGUSThreshold; // The Body Parameters - These do not change after the simulation starts. var g_IsMale = 1; var g_WeightInKg; var g_HeightInCm; var g_GFR = -1; // Volumes - These do not change after the simulation starts. var g_BloodVolumeInLiters = -1; var g_InterstitialVolumeInLiters = -1; var g_InterstitialVolumeInMilliLiters = -1; var g_PlasmaVolumeInMilliLiters = -1; var g_ExtraCellularVolumeInMilliLiters = -1; var g_FractionOfECFInBlood = -1; // Cells var g_TotalNumCellsInTeraCells = -1; var g_ProteinSynthPerCellInPicoGramPerHour = -1; // Urine Protein var g_InitialUrineProteinInGramsPerDay = 10; var g_UrineProteinClearance = -1; // Protein Amounts var g_TotalProteinInBodyInMg = -1; var g_TotalProteinInInterstitialInMg = -1; var g_TotalProteinInPlasmaInMg = -1; // Protein Concentrations var g_ProteinConcentrationInExtracellFluidInMgPerMl = -1; var g_ProteinConcentrationInInterstitialInMgPerMl = -1; var g_ProteinConcentrationInPlasmaInMgPerMl = -1; // Previous state of changing variables. This lets me draw a line from the previous to the current state var g_PrevTotalNumCellsInTeraCells = -1; var g_PrevProteinSynthPerCellInPicoGramPerHour = -1; var g_PrevTotalProteinInBodyInMg = -1; var g_PrevTotalProteinInInterstitialInMg = -1; var g_PrevTotalProteinInPlasmaInMg = -1; var g_PrevProteinConcentrationInExtracellFluidInMgPerMl = -1; var g_PrevProteinConcentrationInInterstitialInMgPerMl = -1; var g_PrevProteinConcentrationInPlasmaInMgPerMl = -1; // These are just the concentrations in grams/dL which is the normal way of reading this. var g_ProteinConcentrationInExtracellFluidInGramsPerDeciLiter = -1; var g_ProteinConcentrationInInterstitialInGramsPerDeciLiter = -1; var g_ProteinConcentrationInPlasmaInGramsPerDeciLiter = -1; var g_PrevProteinConcentrationInExtracellFluidInGramsPerDeciLiter = -1; var g_PrevProteinConcentrationInInterstitialInGramsPerDeciLiter = -1; var g_PrevProteinConcentrationInPlasmaInGramsPerDeciLiter = -1; // The state of the simulation var g_OnDialysis; var g_OnPlasmaExchange; var g_ExchangeVolumeInLiters; var g_CounterHoursLeftInCurrentDialysis; var g_HoursPerDialysis; var g_DaysBetweenDialysis; var g_HoursBetweenDialysis; var g_CounterHoursBeforeNextDialysis; // Display Options // The initial values are set from the html code. var g_RunFast = true; var g_TransferCompartments = true; var g_RunIntermittentDialysis = true; var g_RunIntermittentPlasmaExchange = false; var g_UseTwoDialyzers = true; var g_PlasmaExchangeVolumeInLiters = 4; var g_ShowTotalProtein = false; var g_ShowInterstitial = false; var g_ShowVascularProtein = false; var g_SimSynthesizeProtein = false; var g_SimRenalClearance = false; //////////////////////////////////////////////////////////////////////////////// // // [Main_InitHDSimModule] // // This is called by the browser to initialize the entire UI. //////////////////////////////////////////////////////////////////////////////// function Main_InitHDSimModule() { //LogEvent("Main_InitHDSimModule"); g_RunIntermittentDialysis = Util_GetToggleState("DialysisOnStatus"); g_UseTwoDialyzers = Util_GetToggleState("UseTwoDialyzersStatus"); g_RunIntermittentPlasmaExchange = Util_GetToggleState("PlasmaExchangeOnStatus"); g_RunFast = Util_GetToggleState("RunFastStatus"); g_TransferCompartments = Util_GetToggleState("TransferCompartmentsStatus"); g_ShowInterstitial = Util_GetToggleState("ShowInterstitialStatus"); g_ShowVascularProtein = Util_GetToggleState("ShowVascularProteinStatus"); g_ShowTotalProtein = Util_GetToggleState("ShowTotalProteinStatus"); g_SimSynthesizeProtein = Util_GetToggleState("SynthesizeProteinStatus"); g_SimRenalClearance = Util_GetToggleState("RenalClearanceStatus"); // These are the main windows that we show/hide to expose different subgroups of funtions and outputs. g_SketchWindowDivElement = document.getElementById("SketchWindow"); g_AboutWindowDivElement = document.getElementById("AboutWindow"); /////////////////////////////////////// // Get the main canvas. g_FullFlowChartAndCanvasDiv = document.getElementById("FullFlowChartAndCanvasDiv"); g_MainCanvasElement = document.getElementById("myCanvas"); g_MainCanvasElementContext = g_MainCanvasElement.getContext("2d"); if ((!g_MainCanvasElement) || (!g_MainCanvasElementContext)) { Main_InternalError("StartUI: Null Canvas"); } g_CanvasWidth = g_MainCanvasElement.width; g_CanvasHeight = g_MainCanvasElement.height; Chart_DrawAxes(g_MainCanvasElement, g_GapBelowXAxis, g_GapLeftYAxis, g_MaxYValueInChart, 10); g_SketchWindowDivElement.style.display = "inline"; g_AboutWindowDivElement.style.display = "None"; } // Main_InitHDSimModule //////////////////////////////////////////////////////////////////////////////// // // [Main_ShowWindow] // //////////////////////////////////////////////////////////////////////////////// function Main_ShowWindow(windowNameStr) { //LogEvent("Main_ShowWindow: windowNameStr = " + windowNameStr); g_SketchWindowDivElement.style.display = "None"; g_AboutWindowDivElement.style.display = "None"; //////////////////////////////////// if (windowNameStr == "Sketch") { g_SketchWindowDivElement.style.display = "inline"; //////////////////////////////////// } else if (windowNameStr == "About") { Main_StopSim(); g_AboutWindowDivElement.style.display = "inline"; } } // Main_ShowWindow //////////////////////////////////////////////////////////////////////////////// // // [Main_OnCloseChildWindow] // //////////////////////////////////////////////////////////////////////////////// function Main_OnCloseChildWindow(button) { Main_ShowWindow("Sketch"); } // Main_OnCloseChildWindow //////////////////////////////////////////////////////////////////////////////// // // [Main_ShowError] // //////////////////////////////////////////////////////////////////////////////// function Main_ShowError(errMsg) { LogEvent("Main_ShowError. Error message: " + errMsg); } // Main_ShowError //////////////////////////////////////////////////////////////////////////////// // // [OnOptionButton] // //////////////////////////////////////////////////////////////////////////////// function OnOptionButton(button) { //LogEvent("OnOptionButton. button.id = " + button.id); if (button.id == "IntermittentDialysisButton") { g_RunIntermittentDialysis = Util_SwitchToggleState("DialysisOnStatus"); } else if (button.id == "UseTwoDialyzersButton") { g_UseTwoDialyzers = Util_SwitchToggleState("UseTwoDialyzersStatus"); } else if (button.id == "PlasmaExchangeButton") { g_RunIntermittentPlasmaExchange = Util_SwitchToggleState("PlasmaExchangeOnStatus"); } else if (button.id == "RunFastButton") { g_RunFast = Util_SwitchToggleState("RunFastStatus"); } else if (button.id == "TransferCompartmentsButton") { g_TransferCompartments = Util_SwitchToggleState("TransferCompartmentsStatus"); } else if (button.id == "ShowInterstitialProteinButton") { g_ShowInterstitial = Util_SwitchToggleState("ShowInterstitialStatus"); } else if (button.id == "ShowVascularProteinButton") { g_ShowVascularProtein = Util_SwitchToggleState("ShowVascularProteinStatus"); } else if (button.id == "ShowTotalProteinButton") { g_ShowTotalProtein = Util_SwitchToggleState("ShowTotalProteinStatus"); } else if (button.id == "SynthesizeProteinButton") { g_SimSynthesizeProtein = Util_SwitchToggleState("SynthesizeProteinStatus"); } else if (button.id == "RenalClearanceButton") { g_SimRenalClearance = Util_SwitchToggleState("RenalClearanceStatus"); } } // OnOptionButton //////////////////////////////////////////////////////////////////////////////// // // [Chart_DrawAxes] // //////////////////////////////////////////////////////////////////////////////// function Chart_DrawAxes(canvasElement, gapBelowXAxis, gapLeftYAxis, maxYValue, numYScales) { var width; var height; width = canvasElement.width; height = canvasElement.height; // Draw the X Axis g_MainCanvasElementContext.beginPath(); g_MainCanvasElementContext.moveTo(gapLeftYAxis, height - gapBelowXAxis); g_MainCanvasElementContext.lineTo(width, height - gapBelowXAxis); g_MainCanvasElementContext.lineWidth = 5; g_MainCanvasElementContext.strokeStyle = '#000000'; g_MainCanvasElementContext.stroke(); // Draw the Y-Axis g_MainCanvasElementContext.beginPath(); g_MainCanvasElementContext.moveTo(gapLeftYAxis, 0); g_MainCanvasElementContext.lineTo(gapLeftYAxis, height - gapBelowXAxis); g_MainCanvasElementContext.lineWidth = 5; g_MainCanvasElementContext.strokeStyle = '#000000'; g_MainCanvasElementContext.stroke(); if ((maxYValue > 0) && (numYScales > 0)) { //LogEvent("Chart_DrawAxes. Draw scale. maxYValue=" + maxYValue); var yIncrement = maxYValue / numYScales; var yCoordIncrement = (height - gapBelowXAxis) / numYScales; var index; //LogEvent("Chart_DrawAxes. Draw scale. yIncrement=" + yIncrement); var yLabel = 0; var yCoord = height - gapBelowXAxis; index = 0; while (index < numYScales) { g_MainCanvasElementContext.font = "10px Ariel"; g_MainCanvasElementContext.fillText(yLabel.toString(), 0, yCoord); yLabel = yLabel + yIncrement; yCoord = yCoord - yCoordIncrement; index += 1; } } // if (maxYValue > 0) } // Chart_DrawAxes //////////////////////////////////////////////////////////////////////////////// // // [Chart_DrawLineSegment] // //////////////////////////////////////////////////////////////////////////////// function Chart_DrawLineSegment(prevXCoord, prevYValue, xCoord, yValue, maxValue, color) { var yCoord; var prevYCoord; var yAxisHeight; //LogEvent("Chart_DrawLineSegment. prevYValue=" + prevYValue); //LogEvent("Chart_DrawLineSegment. yValue=" + yValue); // Scale the Y-value to the maxValue. // scaledYValue / height = yValue / maxValue // or after some algebra // scaledYValue = height * (yValue / maxValue) yAxisHeight = g_CanvasHeight - g_GapBelowXAxis; if (maxValue > 0) { //LogEvent("Chart_DrawLineSegment. yValue=" + yValue); //LogEvent("Chart_DrawLineSegment. maxValue=" + maxValue); //LogEvent("Chart_DrawLineSegment. (yValue / maxValue)=" + (yValue / maxValue)); //LogEvent("Chart_DrawLineSegment. g_CanvasHeight=" + g_CanvasHeight); //LogEvent("Chart_DrawLineSegment. g_CanvasHeight * (yValue / maxValue)=" + (g_CanvasHeight * (yValue / maxValue))); yValue = yAxisHeight * (yValue / maxValue); prevYValue = yAxisHeight * (prevYValue / maxValue); } yCoord = yAxisHeight - yValue; prevYCoord = yAxisHeight - prevYValue; //LogEvent("Chart_DrawLineSegment. prevXCoord=" + prevXCoord); //LogEvent("Chart_DrawLineSegment. prevYCoord=" + prevYCoord); //LogEvent("Chart_DrawLineSegment. xCoord=" + xCoord); //LogEvent("Chart_DrawLineSegment. yCoord=" + yCoord); g_MainCanvasElementContext.beginPath(); g_MainCanvasElementContext.moveTo(prevXCoord, prevYCoord); g_MainCanvasElementContext.lineTo(xCoord, yCoord); g_MainCanvasElementContext.lineWidth = 4; g_MainCanvasElementContext.strokeStyle = color; g_MainCanvasElementContext.stroke(); } // Chart_DrawLineSegment //////////////////////////////////////////////////////////////////////////////// // // [Chart_DrawYThreshold] // //////////////////////////////////////////////////////////////////////////////// function Chart_DrawYThreshold(canvasElement, yThreshold, maxValue) { var width; var height; width = canvasElement.width; height = canvasElement.height; var yAxisHeight = (g_CanvasHeight - g_GapBelowXAxis); yValue = yAxisHeight - (yAxisHeight * (yThreshold / maxValue)); //LogEvent("Chart_DrawYThreshold. maxValue=" + maxValue); //LogEvent("Chart_DrawYThreshold. yThreshold=" + yThreshold); //LogEvent("Chart_DrawYThreshold. yThreshold / maxValue=" + (yThreshold / maxValue)); //LogEvent("Chart_DrawYThreshold. (g_CanvasHeight - g_GapBelowXAxis)=" + (g_CanvasHeight - g_GapBelowXAxis)); //LogEvent("Chart_DrawYThreshold. yValue=" + yValue); // Draw the line parallel to the X Axis g_MainCanvasElementContext.save(); g_MainCanvasElementContext.beginPath(); g_MainCanvasElementContext.moveTo(g_GapLeftYAxis, yValue); g_MainCanvasElementContext.lineTo(width, yValue); g_MainCanvasElementContext.lineWidth = 2; g_MainCanvasElementContext.strokeStyle = '#888888'; g_MainCanvasElementContext.setLineDash([10, 4]); g_MainCanvasElementContext.stroke(); g_MainCanvasElementContext.restore(); } // Chart_DrawYThreshold //////////////////////////////////////////////////////////////////////////////// // // [Util_GetInputNumber] // //////////////////////////////////////////////////////////////////////////////// function Util_GetInputNumber(elementName, defaultValue) { if (!elementName) { return(defaultValue); } var inputElement = document.getElementById(elementName); if (!inputElement) { return(defaultValue); } var inputText = inputElement.value; if (inputText == "") { return(defaultValue); } return(parseFloat(inputText)); } // Util_GetInputNumber //////////////////////////////////////////////////////////////////////////////// // // [Main_StartSim] // //////////////////////////////////////////////////////////////////////////////// function Main_StartSim() { var inputText; //LogEvent("Main_StartSim"); g_WeightInKg = Util_GetInputNumber("InputWt", 70); g_HeightInCm = Util_GetInputNumber("InputHt", 180); //g_GFR = Util_GetInputNumber("InputGFR", 60); g_HoursPerDialysis = Util_GetInputNumber("InputDialysisDurationInHrs", 4); g_DaysBetweenDialysis = Util_GetInputNumber("InputDaysBetweenDialysisR", 2); g_ExchangeVolumeInLiters = Util_GetInputNumber("InputExchangeVolume", 3.5); g_InitialUrineProteinInGramsPerDay = Util_GetInputNumber("InputDailyProteinuria", 0); g_TotalNumCellsInTeraCells = Util_GetInputNumber("InputInitCells", 1.8); var picoGramsPerCellPerDay = Util_GetInputNumber("InputProtSynthRate", 9.5); g_LightChainPermeabilityCoefficient = Util_GetInputNumber("PermeabilityCoefficient", 0.000936); //LogEvent("Main_StartSim. g_LightChainPermeabilityCoefficient=" + g_LightChainPermeabilityCoefficient); g_ProteinSynthPerCellInPicoGramPerHour = picoGramsPerCellPerDay / 24; var proteinConcentrationInGramsPerDeciLiter = Util_GetInputNumber("InputInitProtConc", 6); // Multiply by 1000 to go from grams to mg, and divide by 100 to go from dL to mL g_ProteinConcentrationInExtracellFluidInMgPerMl = (proteinConcentrationInGramsPerDeciLiter * 1000) / 100; //LogEvent("Main_StartSim. g_WeightInKg=" + g_WeightInKg); //LogEvent("Main_StartSim. g_HeightInCm=" + g_HeightInCm); //LogEvent("Main_StartSim. g_GFR=" + g_GFR); //LogEvent("Main_StartSim. g_HoursPerDialysis=" + g_HoursPerDialysis); //LogEvent("Main_StartSim. g_DaysBetweenDialysis=" + g_DaysBetweenDialysis); //LogEvent("Main_StartSim. g_ExchangeVolumeInLiters=" + g_ExchangeVolumeInLiters); //LogEvent("Main_StartSim. g_TotalNumCellsInTeraCells=" + g_TotalNumCellsInTeraCells); //LogEvent("Main_StartSim. picoGramsPerCellPerDay=" + picoGramsPerCellPerDay); //LogEvent("Main_StartSim. g_ProteinSynthPerCellInPicoGramPerHour=" + g_ProteinSynthPerCellInPicoGramPerHour); //LogEvent("Main_StartSim. g_ProteinConcentrationInExtracellFluidInMgPerMl=" + g_ProteinConcentrationInExtracellFluidInMgPerMl); ////////////////////////////////////// // Compute the volumes. // // Nadler's Formula for Adult blood volume: // Males = (0.006012 x H3) + (14.6 x W) + 604 // Females = (0.005835 x H3) + (15 x W) + 183 // This returns volume in mL, but it takes height in inches and weight in pounds. var weightInPounds = g_WeightInKg * 2.2; var heightInInches = g_HeightInCm * 0.3937; var cubedHeightInInches = heightInInches * heightInInches * heightInInches; //LogEvent("Main_StartSim. weightInPounds=" + weightInPounds); //LogEvent("Main_StartSim. heightInInches=" + heightInInches); //LogEvent("Main_StartSim. cubedHeightInInches=" + cubedHeightInInches); var totalBloodVolumeInMl = 0; if (g_IsMale) { totalBloodVolumeInMl = (0.006012 * cubedHeightInInches) + (14.6 * weightInPounds) + 604; } else { totalBloodVolumeInMl = (0.005835 * cubedHeightInInches) + (15 * weightInPounds) + 183; } g_BloodVolumeInLiters = totalBloodVolumeInMl / 1000; // Really, plasma = TBV × (1 – hematocrit), but I don't want to change this since things like the lab // valaues are probable mg/dL of blood, not plasma. That is why there is pseudo-hyponatremia. g_PlasmaVolumeInMilliLiters = totalBloodVolumeInMl; //LogEvent("Main_StartSim. totalBloodVolumeInMl=" + totalBloodVolumeInMl); //LogEvent("Main_StartSim. totalBloodVolumeInMl=" + totalBloodVolumeInMl); //LogEvent("Main_StartSim. g_BloodVolumeInLiters=" + g_BloodVolumeInLiters); //LogEvent("Main_StartSim. g_PlasmaVolumeInMilliLiters=" + g_PlasmaVolumeInMilliLiters); // Multiply by 1000 to go from grams to mg, and divide by 100 to go from dL to mL g_MGUSLevelInMgPerMilliliter = (g_MGUSLevelInGramsPerDeciliter * 1000) / 100; g_MGUSLevelInAbsoluteProteins = g_MGUSLevelInMgPerMilliliter * g_PlasmaVolumeInMilliLiters; g_DrawnMGUSThreshold = false; //LogEvent("Main_StartSim. g_MGUSLevelInGramsPerDeciliter=" + g_MGUSLevelInGramsPerDeciliter); //LogEvent("Main_StartSim. g_MGUSLevelInMgPerMilliliter=" + g_MGUSLevelInMgPerMilliliter); //LogEvent("Main_StartSim. g_MGUSLevelInAbsoluteProteins=" + g_MGUSLevelInAbsoluteProteins); // I use the Abraham formula of ECV, see: // Alison G. Abraham, Alvaro Munoz, Susan L. Furth, Bradley Warady, George J. Schwartz, "Extracellular Volume and Glomerular Filtration Rate in Children with Chronic Kidney Disease", CJASN April 2011 vol. 6 no. 4 741-747 // ECV (in L) = sqrt(weightInKg) * heightInMeters var heightInMeters = g_HeightInCm / 100; var extraCellularVolumeInLiters = Math.sqrt(g_WeightInKg) * heightInMeters; g_ExtraCellularVolumeInMilliLiters = extraCellularVolumeInLiters * 1000; g_InterstitialVolumeInMilliLiters = g_ExtraCellularVolumeInMilliLiters - g_PlasmaVolumeInMilliLiters; g_InterstitialVolumeInLiters = g_InterstitialVolumeInMilliLiters / 1000; g_FractionOfECFInBlood = g_PlasmaVolumeInMilliLiters / g_ExtraCellularVolumeInMilliLiters; //LogEvent("Main_StartSim. heightInMeters=" + heightInMeters); //LogEvent("Main_StartSim. extraCellularVolumeInLiters=" + extraCellularVolumeInLiters); //LogEvent("Main_StartSim. g_ExtraCellularVolumeInMilliLiters=" + g_ExtraCellularVolumeInMilliLiters); //LogEvent("Main_StartSim. g_InterstitialVolumeInMilliLiters=" + g_InterstitialVolumeInMilliLiters); //LogEvent("Main_StartSim. g_FractionOfECFInBlood=" + g_FractionOfECFInBlood); ////////////////////////////////////// // Initialize the protein concentrations // When we initialize, we start with concentrations and then derive total amounts. // When we iteratively update, we will first update total amounts, and then use that to update concentrations. // We start in equilibrium, so blood and interstitial fluid are all at the same concentration. g_ProteinConcentrationInInterstitialInMgPerMl = g_ProteinConcentrationInExtracellFluidInMgPerMl; g_ProteinConcentrationInPlasmaInMgPerMl = g_ProteinConcentrationInExtracellFluidInMgPerMl; // Use the volumes and concentrations to compute the g_TotalProteinInInterstitialInMg = g_ProteinConcentrationInInterstitialInMgPerMl * g_InterstitialVolumeInMilliLiters; g_TotalProteinInPlasmaInMg = g_ProteinConcentrationInPlasmaInMgPerMl * g_PlasmaVolumeInMilliLiters; g_TotalProteinInBodyInMg = g_TotalProteinInInterstitialInMg + g_TotalProteinInPlasmaInMg; //LogEvent("Main_StartSim. g_ProteinConcentrationInInterstitialInMgPerMl=" + g_ProteinConcentrationInInterstitialInMgPerMl); //LogEvent("Main_StartSim. g_ProteinConcentrationInPlasmaInMgPerMl=" + g_ProteinConcentrationInPlasmaInMgPerMl); //LogEvent("Main_StartSim. g_TotalProteinInInterstitialInMg=" + g_TotalProteinInInterstitialInMg); //LogEvent("Main_StartSim. g_TotalProteinInPlasmaInMg=" + g_TotalProteinInPlasmaInMg); //LogEvent("Main_StartSim. g_TotalProteinInBodyInMg=" + g_TotalProteinInBodyInMg); // Convert to the standard units for concentration. This is for display only. // Divide by 1000 to go from mg to grams, and multiply by 100 to go from mL to dL g_ProteinConcentrationInExtracellFluidInGramsPerDeciLiter = (g_ProteinConcentrationInExtracellFluidInMgPerMl * 100) / 1000; g_ProteinConcentrationInInterstitialInGramsPerDeciLiter = (g_ProteinConcentrationInInterstitialInMgPerMl * 100) / 1000; g_ProteinConcentrationInPlasmaInGramsPerDeciLiter = (g_ProteinConcentrationInPlasmaInMgPerMl * 100) / 1000; ////////////////////////////////////// // Initialize the urine clearance. // This assumes the GFR does not change. var mgProteinuriaPerDay = g_InitialUrineProteinInGramsPerDay * 1000; var mgProteinuriaPerMinute = mgProteinuriaPerDay / 1440; g_UrineProteinClearance = mgProteinuriaPerMinute / g_ProteinConcentrationInPlasmaInMgPerMl; ////////////////////////////////////// // Update the derived body state //g_FractionalCreatinineClearance = g_GFR * (60 / g_BloodVolumeInLiters) * 1000; // g_FractionalMetabolicRate is catabolic rate + proteinuric rate //g_FractionalMetabolicRate = (0.081 * g_FractionalCreatinineClearance) + 0.028; // g_FractionalProteinuriaRate is 2.53% per hr metabolic rate and 0.778% * g_FractionalCreatinineClearance //g_FractionalProteinuriaRate = 0.0253 - (0.00778 * g_FractionalCreatinineClearance); ////////////////////////////////////// // Reset the graphic state g_IntervalNum = 0; g_PrevXValue = 0; g_XMovementPerTick = g_XMovementPerTickBetweenDialysis; g_MainCanvasElementContext.clearRect(0, 0, g_CanvasWidth, g_CanvasHeight); Chart_DrawAxes(g_MainCanvasElement, g_GapBelowXAxis, g_GapLeftYAxis, g_MaxYValueInChart, 10); ////////////////////////////////////// // Initialize the timer state and start callbacks. g_OnDialysis = false; g_OnPlasmaExchange = false; g_HoursBetweenDialysis = (g_DaysBetweenDialysis * 24) - g_HoursPerDialysis; g_CounterHoursLeftInCurrentDialysis = g_HoursPerDialysis; g_CounterHoursBeforeNextDialysis = g_HoursBetweenDialysis; g_SimulationIsRunning = true; // Start Callbacks // The initial values are set from the html code. if (g_RunFast) { while (g_SimulationIsRunning) { Main_SimulateOneTimePeriod(); } } else { g_CallbackID = window.setInterval(Main_SimulateOneTimePeriod, g_PeriodInMilliSecs); } } // Main_StartSim //////////////////////////////////////////////////////////////////////////////// // // [Main_StopSim] // //////////////////////////////////////////////////////////////////////////////// function Main_StopSim() { //LogEvent("Main_StopSim"); if (g_CallbackID != -1) { window.clearInterval(g_CallbackID); g_CallbackID = -1; } g_SimulationIsRunning = false; } // Main_StopSim //////////////////////////////////////////////////////////////////////////////// // // [Main_Resume] // //////////////////////////////////////////////////////////////////////////////// function Main_Resume() { //LogEvent("Main_Resume"); if (!g_RunFast) { g_CallbackID = window.setInterval(Main_SimulateOneTimePeriod, g_PeriodInMilliSecs); } } // Main_Resume //////////////////////////////////////////////////////////////////////////////// // // [Main_SimulateOneTimePeriod] // // Simulate one step, which currently is 1 hour of time. //////////////////////////////////////////////////////////////////////////////// function Main_SimulateOneTimePeriod() { //LogEvent("Main_SimulateOneTimePeriod. g_IntervalNum=" + g_IntervalNum); var xCoord; var bloodVolumeInMLClearedInOneHour; ////////////////////////////////////// // Update the counters. This will tell us if there is anything to do on this iteration. // // Uncomment this if you want several callbacks per hour. //g_CountdownToNextHour = g_CountdownToNextHour - 1; //if (g_CountdownToNextHour > 0) { // return //} //g_CountdownToNextHour = g_NumTicksPerHour; // As this point, we are starting a new hour. // Check if we are starting or stopping a dialysis or starting or stopping plasma exchange. //LogEvent("Main_SimulateOneTimePeriod. g_OnDialysis=" + g_OnDialysis); if (g_OnDialysis) { g_CounterHoursLeftInCurrentDialysis = g_CounterHoursLeftInCurrentDialysis - 1; if (g_CounterHoursLeftInCurrentDialysis <= 0) { //LogEvent("Main_SimulateOneTimePeriod. Stop Dialysis"); g_OnDialysis = false; g_XMovementPerTick = g_XMovementPerTickBetweenDialysis; g_CounterHoursBeforeNextDialysis = g_HoursBetweenDialysis; } // if (g_CounterHoursLeftInCurrentDialysis <= 0) } else if ((!g_OnDialysis) && (g_RunIntermittentDialysis)) { //LogEvent("Main_SimulateOneTimePeriod. g_CounterHoursBeforeNextDialysis=" + g_CounterHoursBeforeNextDialysis); g_CounterHoursBeforeNextDialysis = g_CounterHoursBeforeNextDialysis - 1; if (g_CounterHoursBeforeNextDialysis <= 0) { //LogEvent("Main_SimulateOneTimePeriod. Start Dialysis"); g_OnDialysis = true; g_XMovementPerTick = g_XMovementPerTickDuringDialysis; g_CounterHoursLeftInCurrentDialysis = g_HoursPerDialysis; } // if (g_CounterHoursBeforeNextDialysis <= 0) } // if (!g_OnDialysis) if (g_OnPlasmaExchange) { g_CounterHoursLeftInCurrentDialysis = g_CounterHoursLeftInCurrentDialysis - 1; if (g_CounterHoursLeftInCurrentDialysis <= 0) { //LogEvent("Main_SimulateOneTimePeriod. Stop Dialysis"); g_OnPlasmaExchange = false; g_XMovementPerTick = g_XMovementPerTickBetweenDialysis; g_CounterHoursBeforeNextDialysis = g_HoursBetweenDialysis; } // if (g_CounterHoursLeftInCurrentDialysis <= 0) } else if ((!g_OnPlasmaExchange) && (g_RunIntermittentPlasmaExchange)) { //LogEvent("Main_SimulateOneTimePeriod. g_CounterHoursBeforeNextDialysis=" + g_CounterHoursBeforeNextDialysis); g_CounterHoursBeforeNextDialysis = g_CounterHoursBeforeNextDialysis - 1; if (g_CounterHoursBeforeNextDialysis <= 0) { //LogEvent("Main_SimulateOneTimePeriod. Start Dialysis"); g_OnPlasmaExchange = true; g_XMovementPerTick = g_XMovementPerTickDuringDialysis; g_CounterHoursLeftInCurrentDialysis = g_HoursPerDialysis; } // if (g_CounterHoursBeforeNextDialysis <= 0) } // if (!g_OnPlasmaExchange) ////////////////////////////////////// // Save the current state into the previous state. // Previous state of changing variables. This lets me draw a line from the previous to the current state g_PrevTotalNumCellsInTeraCells = g_TotalNumCellsInTeraCells; g_PrevProteinSynthPerCellInPicoGramPerHour = g_ProteinSynthPerCellInPicoGramPerHour; g_PrevTotalProteinInBodyInMg = g_TotalProteinInBodyInMg; g_PrevTotalProteinInInterstitialInMg = g_TotalProteinInInterstitialInMg; g_PrevTotalProteinInPlasmaInMg = g_TotalProteinInPlasmaInMg; g_PrevProteinConcentrationInExtracellFluidInMgPerMl = g_ProteinConcentrationInExtracellFluidInMgPerMl; g_PrevProteinConcentrationInInterstitialInMgPerMl = g_ProteinConcentrationInInterstitialInMgPerMl; g_PrevProteinConcentrationInPlasmaInMgPerMl = g_ProteinConcentrationInPlasmaInMgPerMl; g_PrevProteinConcentrationInExtracellFluidInGramsPerDeciLiter = g_ProteinConcentrationInExtracellFluidInGramsPerDeciLiter; g_PrevProteinConcentrationInInterstitialInGramsPerDeciLiter = g_ProteinConcentrationInInterstitialInGramsPerDeciLiter; g_PrevProteinConcentrationInPlasmaInGramsPerDeciLiter = g_ProteinConcentrationInPlasmaInGramsPerDeciLiter; ////////////////////////////////////// // Move to the next X-coordinate. If we hit the end of the canvas, then stop the sim. xCoord = g_PrevXValue + g_XMovementPerTick; if (xCoord > g_CanvasWidth) { //LogEvent("Main_SimulateOneTimePeriod. Stop sim. xCoord=" + xCoord); Main_StopSim(); return; } ////////////////////////////////////// // Update the current state // Step 1. // The cells have created more protein in the past hour. // Number of cells is in 10**12 cells, but protein per cell is in 10**-12 grams. // This leaves the units in grams. var proteinMadeInPastHour = g_TotalNumCellsInTeraCells * g_ProteinSynthPerCellInPicoGramPerHour; // Now, convert from grams to milligrams proteinMadeInPastHour = proteinMadeInPastHour * 1000; // Initially, all of the protein is intracellular. However, it will be able to third-space. // For now, divide this. The protein is made continuously over the past hour, so it has a better chance to equilibrate. var newProteinAddedToBlood = g_FractionOfECFInBlood * proteinMadeInPastHour; var newProteinAddedToInterstitial = proteinMadeInPastHour - newProteinAddedToBlood; if (g_SimSynthesizeProtein) { // For now, just update the totals. We recompute concentrations below. g_TotalProteinInBodyInMg = g_TotalProteinInBodyInMg + proteinMadeInPastHour; g_TotalProteinInInterstitialInMg = g_TotalProteinInInterstitialInMg + newProteinAddedToInterstitial; g_TotalProteinInPlasmaInMg = g_TotalProteinInPlasmaInMg + newProteinAddedToBlood; //LogEvent("Main_SimulateOneTimePeriod. proteinMadeInPastHour=" + proteinMadeInPastHour); //LogEvent("Main_SimulateOneTimePeriod. newProteinAddedToBlood=" + newProteinAddedToBlood); //LogEvent("Main_SimulateOneTimePeriod. newProteinAddedToInterstitial=" + newProteinAddedToInterstitial); //LogEvent("Main_SimulateOneTimePeriod. g_TotalProteinInBodyInMg=" + g_TotalProteinInBodyInMg); //LogEvent("Main_SimulateOneTimePeriod. g_TotalProteinInInterstitialInMg=" + g_TotalProteinInInterstitialInMg); //LogEvent("Main_SimulateOneTimePeriod. g_TotalProteinInPlasmaInMg=" + g_TotalProteinInPlasmaInMg); } // if (g_SimSynthesizeProtein) // Step 2. Eliminate some proteins through the kidney. if (g_SimRenalClearance) { // We only clear the intravascular volume. bloodVolumeInMLClearedInOneHour = 60 * g_UrineProteinClearance; var fractionOfBloodCleared = bloodVolumeInMLClearedInOneHour / g_PlasmaVolumeInMilliLiters; // StartAmount * (1 - %Cleared) = amountLeft var proteinRemovedFromBlood = g_TotalProteinInPlasmaInMg * fractionOfBloodCleared; g_TotalProteinInPlasmaInMg = g_TotalProteinInPlasmaInMg - proteinRemovedFromBlood; //LogEvent("Main_SimulateOneTimePeriod. Dialysis. g_TotalProteinInPlasmaInMg=" + g_TotalProteinInPlasmaInMg); } // if (g_SimRenalClearance) // Step 3. Clear some blood. //Clearance 17.15 mL/min if single HD //Clearance 41.25 mL/min if two HD units in series //Clearance 37.6 mL/min if two HD units with a filter change for lambda and 19 mL/min two HD units with a filter change for kappa //Clearance for kappa light chains reduced if UF at the same time if (g_OnDialysis) { var clearanceRate = 17.15; if (g_UseTwoDialyzers) { clearanceRate = 41.25; } //LogEvent("Main_SimulateOneTimePeriod. Dialysis. clearanceRate=" + clearanceRate); bloodVolumeInMLClearedInOneHour = 60 * clearanceRate; //LogEvent("Main_SimulateOneTimePeriod. Dialysis. bloodVolumeInMLClearedInOneHour=" + bloodVolumeInMLClearedInOneHour); } if (g_OnPlasmaExchange) { // Plasma exchange daily x5 days, each session replaces 3-4L plasma with NS+Albumin+FFP bloodVolumeInMLClearedInOneHour = (1000 * g_PlasmaExchangeVolumeInLiters) / 4; //LogEvent("Main_SimulateOneTimePeriod. Dialysis. bloodVolumeInMLClearedInOneHour=" + bloodVolumeInMLClearedInOneHour); } if ((g_OnDialysis) || (g_OnPlasmaExchange)) { // We only clear the intravascular volume. var fractionOfBloodCleared = bloodVolumeInMLClearedInOneHour / g_PlasmaVolumeInMilliLiters; //LogEvent("Main_SimulateOneTimePeriod. Dialysis. fractionOfBloodCleared=" + fractionOfBloodCleared); // StartAmount * (1 - %Cleared) = amountLeft var proteinRemovedFromBlood = g_TotalProteinInPlasmaInMg * fractionOfBloodCleared; g_TotalProteinInPlasmaInMg = g_TotalProteinInPlasmaInMg - proteinRemovedFromBlood; //LogEvent("Main_SimulateOneTimePeriod. Dialysis. g_TotalProteinInPlasmaInMg=" + g_TotalProteinInPlasmaInMg); } // Step 4. // Update the concentrations g_ProteinConcentrationInExtracellFluidInMgPerMl = g_TotalProteinInBodyInMg / g_ExtraCellularVolumeInMilliLiters; g_ProteinConcentrationInInterstitialInMgPerMl = g_TotalProteinInInterstitialInMg / g_InterstitialVolumeInMilliLiters; g_ProteinConcentrationInPlasmaInMgPerMl = g_TotalProteinInPlasmaInMg / g_PlasmaVolumeInMilliLiters; // Step 5. // Shift protein between interstitial and vascular compartments if (g_TransferCompartments) { // Some day we may want to scale this based on LVEF. var cardiacOutputInLitersPerMin = 5.6; // This is the amount of flow whose content proteins are passed through the membrane. // It is like renal clearance, but it is the amount of blood that sends all protein across the membrane. var distributionClearanceInLitersPerMin = cardiacOutputInLitersPerMin * g_LightChainPermeabilityCoefficient; //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. cardiacOutputInLitersPerMin=" + cardiacOutputInLitersPerMin); //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. g_LightChainPermeabilityCoefficient=" + g_LightChainPermeabilityCoefficient); //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. distributionClearanceInLitersPerMin=" + distributionClearanceInLitersPerMin); var fractionOfPlasmaClearedPerMin = distributionClearanceInLitersPerMin / g_BloodVolumeInLiters; var fractionOfInterstitialClearedPerMin = distributionClearanceInLitersPerMin / g_InterstitialVolumeInLiters; //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. fractionOfPlasmaClearedPerMin=" + fractionOfPlasmaClearedPerMin); //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. fractionOfInterstitialClearedPerMin=" + fractionOfInterstitialClearedPerMin); // Compute the concentration gradients first, do this before we start changing the concentrations. var concentrationGradientFromInterstitialToPlasmaInMgPerMl = g_ProteinConcentrationInInterstitialInMgPerMl - g_ProteinConcentrationInPlasmaInMgPerMl; var concentrationGradientFromPlasmaToInterstitialInMgPerMl = g_ProteinConcentrationInPlasmaInMgPerMl - g_ProteinConcentrationInInterstitialInMgPerMl; //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. concentrationGradientFromInterstitialToPlasmaInMgPerMl=" + concentrationGradientFromInterstitialToPlasmaInMgPerMl); //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. concentrationGradientFromPlasmaToInterstitialInMgPerMl=" + concentrationGradientFromPlasmaToInterstitialInMgPerMl); var changeToPlasmaConcentrationInMgPerMlPerMin = fractionOfPlasmaClearedPerMin * concentrationGradientFromInterstitialToPlasmaInMgPerMl; var changeToPlasmaConcentrationInMgPerMlPerHour = changeToPlasmaConcentrationInMgPerMlPerMin * 60; //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. changeToPlasmaConcentrationInMgPerMlPerHour=" + changeToPlasmaConcentrationInMgPerMlPerHour); g_ProteinConcentrationInPlasmaInMgPerMl = g_ProteinConcentrationInPlasmaInMgPerMl + changeToPlasmaConcentrationInMgPerMlPerHour; var changeToInterstitialConcentrationInMgPerMlPerMin = fractionOfInterstitialClearedPerMin * concentrationGradientFromPlasmaToInterstitialInMgPerMl; var changeToInterstitialConcentrationInMgPerMlPerHour = changeToInterstitialConcentrationInMgPerMlPerMin * 60; //LogEvent("Main_SimulateOneTimePeriod. Shift Compartments. changeToInterstitialConcentrationInMgPerMlPerHour=" + changeToInterstitialConcentrationInMgPerMlPerHour); g_ProteinConcentrationInInterstitialInMgPerMl = g_ProteinConcentrationInInterstitialInMgPerMl + changeToInterstitialConcentrationInMgPerMlPerHour; // Use the updated concentrations to derive total amounts. g_TotalProteinInInterstitialInMg = g_ProteinConcentrationInInterstitialInMgPerMl * g_InterstitialVolumeInMilliLiters; g_TotalProteinInPlasmaInMg = g_ProteinConcentrationInPlasmaInMgPerMl * g_PlasmaVolumeInMilliLiters; g_TotalProteinInBodyInMg = g_TotalProteinInInterstitialInMg + g_TotalProteinInPlasmaInMg; } // if (g_TransferCompartments) // Step 6. // Convert to the standard units for concentration. This is for display only. // Divide by 1000 to go from mg to grams, and multiply by 100 to go from mL to dL g_ProteinConcentrationInExtracellFluidInGramsPerDeciLiter = (g_ProteinConcentrationInExtracellFluidInMgPerMl * 100) / 1000; g_ProteinConcentrationInInterstitialInGramsPerDeciLiter = (g_ProteinConcentrationInInterstitialInMgPerMl * 100) / 1000; g_ProteinConcentrationInPlasmaInGramsPerDeciLiter = (g_ProteinConcentrationInPlasmaInMgPerMl * 100) / 1000; ////////////////////////////////////// // Draw the line segments. // A line segment is between 2 valid points. Don't draw a line if this is the first valid point. if (g_IntervalNum == 0) { //LogEvent("Main_SimulateOneTimePeriod. Dont draw first segment. xCoord=" + xCoord); g_IntervalNum += 1; g_PrevXValue = g_GapLeftYAxis; return; } g_IntervalNum += 1; if (g_ShowTotalProtein) { //LogEvent("Main_SimulateOneTimePeriod. Draw dialysis line. Prev Extracell=" + g_PrevProteinConcentrationInExtracellFluidInGramsPerDeciLiter); //LogEvent("Main_SimulateOneTimePeriod. Draw dialysis line. Extracell=" + g_ProteinConcentrationInExtracellFluidInGramsPerDeciLiter); Chart_DrawLineSegment(g_PrevXValue, g_PrevProteinConcentrationInExtracellFluidInGramsPerDeciLiter, xCoord, g_ProteinConcentrationInExtracellFluidInGramsPerDeciLiter, g_MaxYValueInChart, '#0000ff'); } if (g_ShowInterstitial) { //LogEvent("Main_SimulateOneTimePeriod. Draw dialysis line. Prev Interstitial=" + g_PrevProteinConcentrationInInterstitialInGramsPerDeciLiter); //LogEvent("Main_SimulateOneTimePeriod. Draw dialysis line. Interstitial=" + g_ProteinConcentrationInInterstitialInGramsPerDeciLiter); Chart_DrawLineSegment(g_PrevXValue, g_PrevProteinConcentrationInInterstitialInGramsPerDeciLiter, xCoord, g_ProteinConcentrationInInterstitialInGramsPerDeciLiter, g_MaxYValueInChart, '#00ff00'); } if (g_ShowVascularProtein) { //LogEvent("Main_SimulateOneTimePeriod. Draw dialysis line. Prev Plasma=" + g_PrevProteinConcentrationInPlasmaInGramsPerDeciLiter); //LogEvent("Main_SimulateOneTimePeriod. Draw dialysis line. Plasma=" + g_ProteinConcentrationInPlasmaInGramsPerDeciLiter); Chart_DrawLineSegment(g_PrevXValue, g_PrevProteinConcentrationInPlasmaInGramsPerDeciLiter, xCoord, g_ProteinConcentrationInPlasmaInGramsPerDeciLiter, g_MaxYValueInChart, '#ff0000'); } if (!g_DrawnMGUSThreshold) { Chart_DrawYThreshold(g_MainCanvasElement, g_MGUSLevelInGramsPerDeciliter, g_MaxYValueInChart); g_DrawnMGUSThreshold = true; } // As a guide, mark the times when we are on dialysis with a blue line on the X-axis. if ((g_OnDialysis) || (g_OnPlasmaExchange)) { //LogEvent("Main_SimulateOneTimePeriod. Draw dialysis line. g_IntervalNum=" + g_IntervalNum); Chart_DrawLineSegment(g_PrevXValue, 0, xCoord, 0, -1, '#ff00ff'); } // Get ready for the next iteration. g_PrevXValue = xCoord; } // Main_SimulateOneTimePeriod