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

Generated by  doxygen 1.6.2