CellProperties.cpp

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