CellProperties.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, 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 double CellProperties::GetLastActionPotentialAmplitude()
00282 {
00283     return GetActionPotentialAmplitudes().back();
00284 }
00285 
00286 void CellProperties::CheckExceededThreshold(void)
00287 {
00288     // mOnsets and mRestingValues are all
00289     // set at the same time, so checking one should suffice.
00290     if (mOnsets.empty())
00291     {
00292         // possible false error here if the simulation started at time < 0
00293         EXCEPTION("AP did not occur, never exceeded threshold voltage.");
00294     }
00295 }
00296 
00297 void CellProperties::CheckReturnedToThreshold(void)
00298 {
00299     // mPeakValues, mMaxUpstrokeVelocities and mTimesAtMaxUpstrokeVelocity are all
00300     // set at the same time so checking one should suffice.
00301     if (mMaxUpstrokeVelocities.empty())
00302     {
00303         EXCEPTION("AP did not occur, never descended past threshold voltage.");
00304     }
00305 }
00306 
00307 //
00308 // The Get std::vector<double> methods
00309 //
00310 
00311 std::vector<double> CellProperties::GetCycleLengths()
00312 {
00313     return mCycleLengths;
00314 }
00315 
00316 std::vector<double> CellProperties::GetRestingPotentials()
00317 {
00318     CheckExceededThreshold();
00319     return mRestingValues;
00320 }
00321 
00322 std::vector<double> CellProperties::GetPeakPotentials()
00323 {
00324     CheckReturnedToThreshold();
00325     return mPeakValues;
00326 }
00327 
00328 std::vector<double> CellProperties::GetMaxUpstrokeVelocities()
00329 {
00330     CheckReturnedToThreshold();
00331     return mMaxUpstrokeVelocities;
00332 }
00333 
00334 std::vector<double> CellProperties::GetTimesAtMaxUpstrokeVelocity()
00335 {
00336     CheckReturnedToThreshold();
00337     return mTimesAtMaxUpstrokeVelocity;
00338 }
00339 
00340 std::vector<double> CellProperties::GetAllActionPotentialDurations(const double percentage)
00341 {
00342     return CalculateActionPotentialDurations(percentage);
00343 }
00344 
00345 std::vector<unsigned> CellProperties::GetNumberOfAboveThresholdDepolarisationsForAllAps()
00346 {
00347     return mCounterOfPlateauDepolarisations;
00348 }
00349 //
00350 // The Get <double> methods
00351 //
00352 
00353 double CellProperties::GetLastPeakPotential()
00354 {
00355     CheckReturnedToThreshold();
00356     return mPeakValues.back();
00357 }
00358 
00359 double CellProperties::GetLastCompletePeakPotential()
00360 {
00361     CheckReturnedToThreshold();
00362     double peak_value;
00363     if (mUnfinishedActionPotentials && mPeakValues.size()>1u)
00364     {
00365         peak_value = mPeakValues[mPeakValues.size()-2u];
00366     }
00367     else if  (mUnfinishedActionPotentials && mPeakValues.size()==1u)
00368     {
00369         EXCEPTION("No peak potential matching a full action potential was recorded.");
00370     }
00371     else
00372     {
00373         peak_value = mPeakValues.back();
00374     }
00375     return peak_value;
00376 }
00377 
00378 double CellProperties::GetLastMaxUpstrokeVelocity()
00379 {
00380     CheckReturnedToThreshold();
00381     return mMaxUpstrokeVelocities.back();
00382 }
00383 
00384 double CellProperties::GetLastCompleteMaxUpstrokeVelocity()
00385 {
00386     CheckReturnedToThreshold();
00387     double max_upstroke;
00388     if (mUnfinishedActionPotentials && mMaxUpstrokeVelocities.size()>1u)
00389     {
00390         max_upstroke = mMaxUpstrokeVelocities[mMaxUpstrokeVelocities.size()-2u];
00391     }
00392     else if  (mUnfinishedActionPotentials && mMaxUpstrokeVelocities.size()==1u)
00393     {
00394         EXCEPTION("No MaxUpstrokeVelocity matching a full action potential was recorded.");
00395     }
00396     else
00397     {
00398         max_upstroke = mMaxUpstrokeVelocities.back();
00399     }
00400     return max_upstroke;
00401 }
00402 
00403 double CellProperties::GetTimeAtLastMaxUpstrokeVelocity()
00404 {
00405     CheckReturnedToThreshold();
00406     return mTimesAtMaxUpstrokeVelocity.back();
00407 }
00408 
00409 double CellProperties::GetTimeAtLastCompleteMaxUpstrokeVelocity()
00410 {
00411     CheckReturnedToThreshold();
00412     double max_upstroke_time;
00413     if (mUnfinishedActionPotentials && mTimesAtMaxUpstrokeVelocity.size()>1u)
00414     {
00415         max_upstroke_time = mTimesAtMaxUpstrokeVelocity[mTimesAtMaxUpstrokeVelocity.size()-2u];
00416     }
00417     else if  (mUnfinishedActionPotentials && mTimesAtMaxUpstrokeVelocity.size()==1u)
00418     {
00419         EXCEPTION("No TimeAtMaxUpstrokeVelocity matching a full action potential was recorded.");
00420     }
00421     else
00422     {
00423         max_upstroke_time = mTimesAtMaxUpstrokeVelocity.back();
00424     }
00425     return max_upstroke_time;
00426 }
00427 
00428 double CellProperties::GetLastActionPotentialDuration(const double percentage)
00429 {
00430     // We tested this returns a non-empty vector in the method.
00431     return CalculateActionPotentialDurations(percentage).back();
00432 }
00433 
00434 unsigned CellProperties::GetNumberOfAboveThresholdDepolarisationsForLastAp()
00435 {
00436     return mCounterOfPlateauDepolarisations.back();
00437 }
00438 
00439 double CellProperties::GetLastRestingPotential()
00440 {
00441     // We tested something sensible has happened in the vector returning method.
00442     return GetRestingPotentials().back();
00443 }

Generated by  doxygen 1.6.2