Chaste Release::3.1
CellProperties.cpp
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