CellProperties.cpp

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

Generated on Mon Nov 1 12:35:16 2010 for Chaste by  doxygen 1.5.5