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