00001 /* 00002 00003 Copyright (C) University of Oxford, 2005-2011 00004 00005 University of Oxford means the Chancellor, Masters and Scholars of the 00006 University of Oxford, having an administrative office at Wellington 00007 Square, Oxford OX1 2JD, UK. 00008 00009 This file is part of Chaste. 00010 00011 Chaste is free software: you can redistribute it and/or modify it 00012 under the terms of the GNU Lesser General Public License as published 00013 by the Free Software Foundation, either version 2.1 of the License, or 00014 (at your option) any later version. 00015 00016 Chaste is distributed in the hope that it will be useful, but WITHOUT 00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00018 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00019 License for more details. The offer of Chaste under the terms of the 00020 License is subject to the License being interpreted in accordance with 00021 English Law and subject to any action against the University of Oxford 00022 being under the jurisdiction of the English Courts. 00023 00024 You should have received a copy of the GNU Lesser General Public License 00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>. 00026 00027 */ 00028 00029 #include <cmath> 00030 #include <sstream> 00031 #include <cassert> 00032 00033 #include "CellProperties.hpp" 00034 #include "Exception.hpp" 00035 00036 00037 enum APPhases { BELOWTHRESHOLD , ABOVETHRESHOLD }; 00038 00039 void CellProperties::CalculateProperties() 00040 { 00041 // Check we have some suitable data to process 00042 if (mrTime.size() < 1) 00043 { 00044 EXCEPTION("Insufficient time steps to calculate physiological properties."); 00045 } 00046 00047 if (mrTime.size() != mrVoltage.size()) 00048 { 00049 EXCEPTION("Time and Voltage series should be the same length. Time.size() = " << mrTime.size() << ", Voltage.size() = " << mrVoltage.size()); 00050 } 00051 00052 00053 double max_upstroke_velocity = -DBL_MAX; 00054 double current_time_of_upstroke_velocity = 0; 00055 double current_resting_value=DBL_MAX; 00056 double current_peak=-DBL_MAX; 00057 double current_minimum_velocity=DBL_MAX; 00058 double prev_voltage_derivative=0; 00059 unsigned ap_counter = 0; 00060 unsigned counter_of_plateau_depolarisations = 0; 00061 //boolean to keep track whether we are switching phase from BELOWTHRESHOLD to ABOVETHRESHOLD 00062 bool switching_phase = false; 00063 bool found_a_flat_bit=false; 00064 APPhases ap_phase = BELOWTHRESHOLD; 00065 00066 unsigned time_steps = mrTime.size()-1; //The number of time steps is the number of intervals 00067 00068 double v = mrVoltage[0]; 00069 double t = mrTime[0]; 00070 double prev_v = v; 00071 double prev_t = t; 00072 double voltage_derivative; 00073 const double resting_potential_gradient_threshold = 1e-2; 00074 00075 for (unsigned i=1; i<=time_steps; i++) 00076 { 00077 v = mrVoltage[i]; 00078 t = mrTime[i]; 00079 voltage_derivative = (t == prev_t) ? 0.0 : (v - prev_v) / (t - prev_t); 00080 00081 // Look for the max upstroke velocity and when it happens (could be below or above threshold). 00082 if (voltage_derivative >= max_upstroke_velocity) 00083 { 00084 max_upstroke_velocity = voltage_derivative; 00085 current_time_of_upstroke_velocity = t; 00086 00087 } 00088 00089 switch (ap_phase) 00090 { 00091 case BELOWTHRESHOLD: 00092 //while below threshold, find the resting value by checking where the velocity is minimal 00093 //i.e. when it is flattest. If we can't find a flat bit, instead go for the minimum voltage 00094 //seen before the threshold. 00095 if (fabs(voltage_derivative)<=current_minimum_velocity && fabs(voltage_derivative)<=resting_potential_gradient_threshold) 00096 { 00097 current_minimum_velocity=fabs(voltage_derivative); 00098 current_resting_value = prev_v; 00099 found_a_flat_bit=true; 00100 } 00101 else if(prev_v < current_resting_value && !found_a_flat_bit) 00102 { 00103 current_resting_value = prev_v; 00104 } 00105 00106 // If we cross the threshold, this counts as an AP 00107 if ( v>mThreshold && prev_v <= mThreshold ) 00108 { 00109 //register the resting value and re-initialise the minimum velocity 00110 mRestingValues.push_back(current_resting_value); 00111 current_minimum_velocity = DBL_MAX; 00112 current_resting_value = DBL_MAX; 00113 00114 //Register the onset time. Linear interpolation. 00115 mOnsets.push_back(prev_t + (t-prev_t)/(v-prev_v)*(mThreshold-prev_v)); 00116 00117 //If it is not the first AP, calculate cycle length for the last two APs 00118 if (ap_counter>0) 00119 { 00120 mCycleLengths.push_back( mOnsets[ap_counter]-mOnsets[ap_counter-1] ); 00121 } 00122 00123 switching_phase = true; 00124 found_a_flat_bit = false; 00125 ap_phase = ABOVETHRESHOLD; 00126 // no break here - deliberate fall through to next case 00127 } 00128 else 00129 { 00130 break; 00131 } 00132 00133 case ABOVETHRESHOLD: 00134 //While above threshold, look for the peak potential for the current AP 00135 if (v>current_peak) 00136 { 00137 current_peak = v; 00138 } 00139 00140 // we check whether we have above threshold depolarisations 00141 // and only if if we haven't just switched from below threshold at this time step. 00142 // The latter is to avoid recording things depending on resting behaviour (in case of sudden upstroke from rest) 00143 if (prev_voltage_derivative<=0 && voltage_derivative>0 && !switching_phase) 00144 { 00145 counter_of_plateau_depolarisations++; 00146 } 00147 00148 // From the next time step, we are not "switching phase" any longer 00149 // (we want to check for above threshold deolarisations) 00150 switching_phase = false; 00151 00152 // If we cross the threshold again, the AP is over 00153 // and we register all the parameters. 00154 if ( v<mThreshold && prev_v >= mThreshold ) 00155 { 00156 //register peak value for this AP 00157 mPeakValues.push_back(current_peak); 00158 //Re-initialise the current_peak. 00159 current_peak = mThreshold; 00160 00161 //register maximum upstroke velocity for this AP 00162 mMaxUpstrokeVelocities.push_back(max_upstroke_velocity); 00163 //re-initialise max_upstroke_velocity 00164 max_upstroke_velocity = -DBL_MAX; 00165 00166 //register time when maximum upstroke velocity occurred for this AP 00167 mTimesAtMaxUpstrokeVelocity.push_back(current_time_of_upstroke_velocity); 00168 //re-initialise current_time_of_upstroke_velocity=t; 00169 current_time_of_upstroke_velocity = 0.0; 00170 00171 mCounterOfPlateauDepolarisations.push_back(counter_of_plateau_depolarisations); 00172 00173 //update the counters. 00174 ap_counter++; 00175 ap_phase = BELOWTHRESHOLD; 00176 00177 //reinitialise counter of plateau depolarisations 00178 counter_of_plateau_depolarisations = 0; 00179 } 00180 break; 00181 } 00182 prev_v = v; 00183 prev_t = t; 00184 prev_voltage_derivative = voltage_derivative; 00185 } 00186 00187 00188 // One last check. If the simulation ends halfway through an AP 00189 // i.e. if the vectors of onsets has more elements than the vectors 00190 // of peak and upstroke properties (that are updated at the end of the AP), 00191 // then we register the peak and upstroke values so far 00192 // for the last incomplete AP. 00193 if (mOnsets.size()>mMaxUpstrokeVelocities.size()) 00194 { 00195 mMaxUpstrokeVelocities.push_back(max_upstroke_velocity); 00196 mPeakValues.push_back(current_peak); 00197 mTimesAtMaxUpstrokeVelocity.push_back(current_time_of_upstroke_velocity); 00198 mUnfinishedActionPotentials = true; 00199 } 00200 } 00201 00202 00203 std::vector<double> CellProperties::CalculateActionPotentialDurations(const double percentage) 00204 { 00205 CheckExceededThreshold(); 00206 00207 double prev_v = mrVoltage[0]; 00208 unsigned APcounter=0;//will keep count of the APDs that we calculate 00209 bool apd_is_calculated=true;//this will ensure we hit the target only once per AP. 00210 std::vector<double> apds; 00211 double target = DBL_MAX; 00212 bool apd_starting_time_found=false; 00213 double apd_start_time=DBL_MAX; 00214 00215 double t; 00216 double v; 00217 for (unsigned i=1; i<mrTime.size(); i++) 00218 { 00219 t = mrTime[i]; 00220 v = mrVoltage[i]; 00221 00222 //First we make sure we stop calculating after the last AP has been calculated 00223 if (APcounter<mPeakValues.size()) 00224 { 00225 //Set the target potential 00226 target = mRestingValues[APcounter]+0.01*(100-percentage)*(mPeakValues[APcounter]-mRestingValues[APcounter]); 00227 00228 //if we reach the peak, we need to start to calculate an APD 00229 if (fabs(v-mPeakValues[APcounter])<=1e-6) 00230 { 00231 apd_is_calculated = false; 00232 } 00233 00234 // Start the timing where we first cross the target voltage 00235 if ( prev_v<v && prev_v<=target && v>=target && apd_starting_time_found==false) 00236 { 00237 // Linear interpolation of target crossing time. 00238 apd_start_time=t+( (target-prev_v)/(v-prev_v) )*(t-mrTime[i-1]); 00239 apd_starting_time_found = true; 00240 } 00241 00242 //if we hit the target while repolarising 00243 //and we are told this apd is not calculated yet. 00244 if ( prev_v>v && prev_v>=target && v<=target && apd_is_calculated==false) 00245 { 00246 // Linear interpolation of target crossing time. 00247 apds.push_back (t - apd_start_time + ( (target-prev_v)/(v-prev_v) )*(t-mrTime[i-1]) ); 00248 APcounter++; 00249 apd_is_calculated = true; 00250 apd_starting_time_found = false; 00251 } 00252 } 00253 prev_v = v; 00254 } 00255 if (apds.size() == 0) 00256 { 00257 EXCEPTION("No full action potential was recorded"); 00258 } 00259 return apds; 00260 } 00261 00262 std::vector<double> CellProperties::GetActionPotentialAmplitudes() 00263 { 00264 CheckExceededThreshold(); 00265 unsigned size = mPeakValues.size(); 00266 std::vector<double> amplitudes(size); 00267 for (unsigned i=0; i< size ;i++) 00268 { 00269 amplitudes[i] = (mPeakValues[i] - mRestingValues[i]); 00270 } 00271 return amplitudes; 00272 } 00273 00274 void CellProperties::CheckExceededThreshold(void) 00275 { 00276 // mOnsets and mRestingValues are all 00277 // set at the same time, so checking one should suffice. 00278 if (mOnsets.empty()) 00279 { 00280 // possible false error here if the simulation started at time < 0 00281 EXCEPTION("AP did not occur, never exceeded threshold voltage."); 00282 } 00283 } 00284 00285 void CellProperties::CheckReturnedToThreshold(void) 00286 { 00287 // mPeakValues, mMaxUpstrokeVelocities and mTimesAtMaxUpstrokeVelocity are all 00288 // set at the same time so checking one should suffice. 00289 if (mMaxUpstrokeVelocities.empty()) 00290 { 00291 EXCEPTION("AP did not occur, never descended past threshold voltage."); 00292 } 00293 } 00294 00295 // 00296 // The Get std::vector<double> methods 00297 // 00298 00299 std::vector<double> CellProperties::GetCycleLengths() 00300 { 00301 return mCycleLengths; 00302 } 00303 00304 std::vector<double> CellProperties::GetRestingPotentials() 00305 { 00306 CheckExceededThreshold(); 00307 return mRestingValues; 00308 } 00309 00310 std::vector<double> CellProperties::GetPeakPotentials() 00311 { 00312 CheckReturnedToThreshold(); 00313 return mPeakValues; 00314 } 00315 00316 std::vector<double> CellProperties::GetMaxUpstrokeVelocities() 00317 { 00318 CheckReturnedToThreshold(); 00319 return mMaxUpstrokeVelocities; 00320 } 00321 00322 std::vector<double> CellProperties::GetTimesAtMaxUpstrokeVelocity() 00323 { 00324 CheckReturnedToThreshold(); 00325 return mTimesAtMaxUpstrokeVelocity; 00326 } 00327 00328 std::vector<double> CellProperties::GetAllActionPotentialDurations(const double percentage) 00329 { 00330 return CalculateActionPotentialDurations(percentage); 00331 } 00332 00333 std::vector<unsigned> CellProperties::GetNumberOfAboveThresholdDepolarisationsForAllAps() 00334 { 00335 return mCounterOfPlateauDepolarisations; 00336 } 00337 // 00338 // The Get <double> methods 00339 // 00340 00341 double CellProperties::GetLastPeakPotential() 00342 { 00343 CheckReturnedToThreshold(); 00344 return mPeakValues.back(); 00345 } 00346 00347 double CellProperties::GetLastCompletePeakPotential() 00348 { 00349 CheckReturnedToThreshold(); 00350 double peak_value; 00351 if (mUnfinishedActionPotentials && mPeakValues.size()>1u) 00352 { 00353 peak_value = mPeakValues[mPeakValues.size()-2u]; 00354 } 00355 else if (mUnfinishedActionPotentials && mPeakValues.size()==1u) 00356 { 00357 EXCEPTION("No peak potential matching a full action potential was recorded."); 00358 } 00359 else 00360 { 00361 peak_value = mPeakValues.back(); 00362 } 00363 return peak_value; 00364 } 00365 00366 double CellProperties::GetLastMaxUpstrokeVelocity() 00367 { 00368 CheckReturnedToThreshold(); 00369 return mMaxUpstrokeVelocities.back(); 00370 } 00371 00372 double CellProperties::GetLastCompleteMaxUpstrokeVelocity() 00373 { 00374 CheckReturnedToThreshold(); 00375 double max_upstroke; 00376 if (mUnfinishedActionPotentials && mMaxUpstrokeVelocities.size()>1u) 00377 { 00378 max_upstroke = mMaxUpstrokeVelocities[mMaxUpstrokeVelocities.size()-2u]; 00379 } 00380 else if (mUnfinishedActionPotentials && mMaxUpstrokeVelocities.size()==1u) 00381 { 00382 EXCEPTION("No MaxUpstrokeVelocity matching a full action potential was recorded."); 00383 } 00384 else 00385 { 00386 max_upstroke = mMaxUpstrokeVelocities.back(); 00387 } 00388 return max_upstroke; 00389 } 00390 00391 double CellProperties::GetTimeAtLastMaxUpstrokeVelocity() 00392 { 00393 CheckReturnedToThreshold(); 00394 return mTimesAtMaxUpstrokeVelocity.back(); 00395 } 00396 00397 double CellProperties::GetTimeAtLastCompleteMaxUpstrokeVelocity() 00398 { 00399 CheckReturnedToThreshold(); 00400 double max_upstroke_time; 00401 if (mUnfinishedActionPotentials && mTimesAtMaxUpstrokeVelocity.size()>1u) 00402 { 00403 max_upstroke_time = mTimesAtMaxUpstrokeVelocity[mTimesAtMaxUpstrokeVelocity.size()-2u]; 00404 } 00405 else if (mUnfinishedActionPotentials && mTimesAtMaxUpstrokeVelocity.size()==1u) 00406 { 00407 EXCEPTION("No TimeAtMaxUpstrokeVelocity matching a full action potential was recorded."); 00408 } 00409 else 00410 { 00411 max_upstroke_time = mTimesAtMaxUpstrokeVelocity.back(); 00412 } 00413 return max_upstroke_time; 00414 } 00415 00416 double CellProperties::GetLastActionPotentialDuration(const double percentage) 00417 { 00418 std::vector<double> apds = CalculateActionPotentialDurations(percentage); 00419 // We tested this returns a non-empty vector in the method. 00420 return apds.back(); 00421 } 00422 00423 unsigned CellProperties::GetNumberOfAboveThresholdDepolarisationsForLastAp() 00424 { 00425 return mCounterOfPlateauDepolarisations.back(); 00426 } 00427 00428