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