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 00030 #include "CellProperties.hpp" 00031 #include "Exception.hpp" 00032 #include <cmath> 00033 00034 00035 enum APPhases { BELOWTHRESHOLD , ABOVETHRESHOLD }; 00036 00037 void CellProperties::CalculateProperties() 00038 { 00039 // Check we have some suitable data to process 00040 if (mrTime.size() < 1) 00041 { 00042 EXCEPTION("Insufficient time steps to calculate physiological properties."); 00043 } 00044 00045 if (mrTime.size() != mrVoltage.size()) 00046 { 00047 std::stringstream exception_message; 00048 exception_message << "Time and Voltage series should be the same length. Time.size() = " << mrTime.size() << ", Voltage.size() = " << mrVoltage.size(); 00049 EXCEPTION(exception_message.str()); 00050 } 00051 00052 double prev_v = mrVoltage[0]; 00053 double prev_t = mrTime[0]; 00054 double max_upstroke_velocity = 0; 00055 double current_time_of_upstroke_velocity = 0; 00056 double current_resting_value=-DBL_MAX; 00057 double current_peak=-DBL_MAX; 00058 double current_minimum_velocity=DBL_MAX; 00059 double prev_voltage_derivative=0; 00060 unsigned ap_counter = 0; 00061 unsigned counter_of_plateau_depolarisations = 0; 00062 //boolean to keep track whether we are switching phase from BELOWTHRESHOLD to ABOVETHRESHOLD 00063 bool switching_phase = 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 for (unsigned i=1; i<=time_steps; i++) 00069 { 00070 double v = mrVoltage[i]; 00071 double t = mrTime[i]; 00072 double voltage_derivative = (v - prev_v) / (t - prev_t); 00073 00074 //Look for the upstroke velocity and when it happens (could be below or above threshold). 00075 if (voltage_derivative >= max_upstroke_velocity) 00076 { 00077 max_upstroke_velocity = voltage_derivative; 00078 current_time_of_upstroke_velocity = t; 00079 } 00080 00081 switch (ap_phase) 00082 { 00083 case BELOWTHRESHOLD: 00084 //while below threshold, find the resting value by checking where the velocity is minimal 00085 //i.e. when it is flattest 00086 if (fabs(voltage_derivative)<=current_minimum_velocity) 00087 { 00088 current_minimum_velocity=fabs(voltage_derivative); 00089 current_resting_value = prev_v; 00090 } 00091 00092 // If we cross the threshold, this counts as an AP 00093 if ( v>mThreshold && prev_v <= mThreshold ) 00094 { 00095 //register the resting value and re-initialise the minimum velocity 00096 mRestingValues.push_back(current_resting_value); 00097 current_minimum_velocity = DBL_MAX; 00098 00099 //Register the onset time. Linear interpolation. 00100 mOnsets.push_back(prev_t + (t-prev_t)/(v-prev_v)*(mThreshold-prev_v)); 00101 00102 //If it is not the first AP, calculate cycle length for the last two APs 00103 if (ap_counter>0) 00104 { 00105 mCycleLengths.push_back( mOnsets[ap_counter]-mOnsets[ap_counter-1] ); 00106 } 00107 00108 switching_phase = true; 00109 ap_phase = ABOVETHRESHOLD; 00110 // no break here - deliberate fall through to next case 00111 } 00112 else 00113 { 00114 break; 00115 } 00116 00117 case ABOVETHRESHOLD: 00118 //While above threshold, look for the peak potential for the current AP 00119 if (v>current_peak) 00120 { 00121 current_peak = v; 00122 } 00123 00124 // we check whether we have above threshold depolarisations 00125 // and only if if we haven't just switched from below threshold at this time step. 00126 // The latter is to avoid recording things depending on resting behaviour (in case of sudden upstroke from rest) 00127 if (prev_voltage_derivative<=0 && voltage_derivative>0 && !switching_phase) 00128 { 00129 counter_of_plateau_depolarisations++; 00130 } 00131 00132 // From the next time step, we are not "switching phase" any longer 00133 // (we want to check for above threshold deolarisations) 00134 switching_phase = false; 00135 00136 // If we cross the threshold again, the AP is over 00137 // and we register all the parameters. 00138 if ( v<mThreshold && prev_v >= mThreshold ) 00139 { 00140 //register peak value for this AP 00141 mPeakValues.push_back(current_peak); 00142 //Re-initialise the current_peak. 00143 current_peak = mThreshold; 00144 00145 //register maximum upstroke velocity for this AP 00146 mMaxUpstrokeVelocities.push_back(max_upstroke_velocity); 00147 //re-initialise max_upstroke_velocity 00148 max_upstroke_velocity = 0.0; 00149 00150 //register time when maximum upstroke velocity occurred for this AP 00151 mTimesAtMaxUpstrokeVelocity.push_back(current_time_of_upstroke_velocity); 00152 //re-initialise current_time_of_upstroke_velocity=t; 00153 current_time_of_upstroke_velocity = 0.0; 00154 00155 mCounterOfPlateauDepolarisations.push_back(counter_of_plateau_depolarisations); 00156 00157 //update the counters. 00158 ap_counter++; 00159 ap_phase = BELOWTHRESHOLD; 00160 00161 //reinitialise counter of plateau depolarisations 00162 counter_of_plateau_depolarisations = 0; 00163 } 00164 break; 00165 } 00166 00167 prev_v = v; 00168 prev_t = t; 00169 prev_voltage_derivative = voltage_derivative; 00170 } 00171 00172 // One last check. If the simulation ends halfway through an AP 00173 // i.e. if the vectors of onsets has more elements than the vectors 00174 // of peak and upstroke properties (that are updated at the end of the AP), 00175 // then we register the peak and upstroke values so far 00176 // for the last incomplete AP. 00177 if (mOnsets.size()>mMaxUpstrokeVelocities.size()) 00178 { 00179 mMaxUpstrokeVelocities.push_back(max_upstroke_velocity); 00180 } 00181 if (mOnsets.size()>mPeakValues.size()) 00182 { 00183 mPeakValues.push_back(current_peak); 00184 } 00185 if (mOnsets.size()>mTimesAtMaxUpstrokeVelocity.size()) 00186 { 00187 mTimesAtMaxUpstrokeVelocity.push_back(current_time_of_upstroke_velocity); 00188 } 00189 } 00190 00191 00192 std::vector<double> CellProperties::CalculateActionPotentialDurations(const double percentage) 00193 { 00194 CheckExceededThreshold(); 00195 00196 double prev_v = -DBL_MAX; 00197 unsigned APcounter=0;//will keep count of the APDs that we calculate 00198 bool apd_is_calculated=true;//this will ensure we hit the target only once per AP. 00199 std::vector<double> apds; 00200 double target = DBL_MAX; 00201 00202 for (unsigned i=0; i<mrTime.size(); i++) 00203 { 00204 double t = mrTime[i]; 00205 double v = mrVoltage[i]; 00206 00207 //First we make sure we stop calculating after the last AP has been calculated 00208 if (APcounter<mPeakValues.size()) 00209 { 00210 //Set the target potential 00211 target = mRestingValues[APcounter]+0.01*(100-percentage)*(mPeakValues[APcounter]-mRestingValues[APcounter]); 00212 00213 //if we reach the peak, we need to start to calculate an APD 00214 if (fabs(v-mPeakValues[APcounter])<=1e-6) 00215 { 00216 apd_is_calculated = false; 00217 } 00218 //if we hit the target while repolarising 00219 //and we are told this apd is not calculated yet. 00220 if ( prev_v>v && prev_v>=target && v<=target && apd_is_calculated==false) 00221 { 00222 apds.push_back (t - mOnsets[APcounter]); 00223 APcounter++; 00224 apd_is_calculated = true; 00225 } 00226 } 00227 prev_v = v; 00228 } 00229 if (apds.size() == 0) 00230 { 00231 EXCEPTION("No full action potential was recorded"); 00232 } 00233 return apds; 00234 } 00235 00236 std::vector<double> CellProperties::GetActionPotentialAmplitudes() 00237 { 00238 CheckExceededThreshold(); 00239 unsigned size = mPeakValues.size(); 00240 std::vector<double> amplitudes(size); 00241 for (unsigned i=0; i< size ;i++) 00242 { 00243 amplitudes[i] = (mPeakValues[i] - mRestingValues[i]); 00244 } 00245 return amplitudes; 00246 } 00247 00248 void CellProperties::CheckExceededThreshold(void) 00249 { 00250 // mOnsets and mRestingValues are all 00251 // set at the same time, so checking one should suffice. 00252 if (mOnsets.empty()) 00253 { 00254 // possible false error here if the simulation started at time < 0 00255 EXCEPTION("AP did not occur, never exceeded threshold voltage."); 00256 } 00257 } 00258 00259 void CellProperties::CheckReturnedToThreshold(void) 00260 { 00261 // mPeakValues, mMaxUpstrokeVelocities and mTimesAtMaxUpstrokeVelocity are all 00262 // set at the same time so checking one should suffice. 00263 if (mMaxUpstrokeVelocities.empty()) 00264 { 00265 EXCEPTION("AP did not occur, never descended past threshold voltage."); 00266 } 00267 } 00268 00269 // 00270 // The Get std::vector<double> methods 00271 // 00272 00273 std::vector<double> CellProperties::GetCycleLengths() 00274 { 00275 return mCycleLengths; 00276 } 00277 00278 std::vector<double> CellProperties::GetRestingPotentials() 00279 { 00280 CheckExceededThreshold(); 00281 return mRestingValues; 00282 } 00283 00284 std::vector<double> CellProperties::GetPeakPotentials() 00285 { 00286 CheckReturnedToThreshold(); 00287 return mPeakValues; 00288 } 00289 00290 std::vector<double> CellProperties::GetMaxUpstrokeVelocities() 00291 { 00292 CheckReturnedToThreshold(); 00293 return mMaxUpstrokeVelocities; 00294 } 00295 00296 std::vector<double> CellProperties::GetTimesAtMaxUpstrokeVelocity() 00297 { 00298 CheckReturnedToThreshold(); 00299 return mTimesAtMaxUpstrokeVelocity; 00300 } 00301 00302 std::vector<double> CellProperties::GetAllActionPotentialDurations(const double percentage) 00303 { 00304 return CalculateActionPotentialDurations(percentage); 00305 } 00306 00307 std::vector<unsigned> CellProperties::GetNumberOfAboveThresholdDepolarisationsForAllAps() 00308 { 00309 return mCounterOfPlateauDepolarisations; 00310 } 00311 // 00312 // The Get <double> methods 00313 // 00314 00315 double CellProperties::GetLastPeakPotential() 00316 { 00317 CheckReturnedToThreshold(); 00318 return mPeakValues.back(); 00319 } 00320 00321 double CellProperties::GetLastMaxUpstrokeVelocity() 00322 { 00323 CheckReturnedToThreshold(); 00324 return mMaxUpstrokeVelocities.back(); 00325 } 00326 00327 double CellProperties::GetTimeAtLastMaxUpstrokeVelocity() 00328 { 00329 CheckReturnedToThreshold(); 00330 return mTimesAtMaxUpstrokeVelocity.back(); 00331 } 00332 00333 double CellProperties::GetLastActionPotentialDuration(const double percentage) 00334 { 00335 std::vector<double> apds = CalculateActionPotentialDurations(percentage); 00336 // We tested this returns a non-empty vector in the method. 00337 return apds.back(); 00338 } 00339 00340 unsigned CellProperties::GetNumberOfAboveThresholdDepolarisationsForLastAp() 00341 { 00342 return mCounterOfPlateauDepolarisations.back(); 00343 } 00344 00345